| /* ************************************************* |
| * *********** README ****************************** |
| * ************************************************* |
| * |
| * COMPILE : make |
| * RUN : ./locktests -n <number of concurent process> -f <test file> [-P] |
| * |
| * GOAL : This test tries to stress the fcntl locking functions. A |
| * master process sets a lock on a file region (this is called "byte |
| * range locking"). Some slave processes try to perform operations on |
| * this region, such as read, write, set a new lock ... The expected |
| * results of these operations are known. If the operation result is |
| * the same as the expected one, the test suceeds, else it fails. |
| * |
| * |
| * |
| * Slaves are concurent processes or thread. |
| * -n <num> : Number of threads to use (mandatory). |
| * -f <file> : Run the test on given test file defined by the -f option (mandatory). |
| * -c <num> : Number of clients to connect before starting the tests. |
| * |
| * HISTORY : This program was written to stress NFSv4 locks. |
| * EXAMPLE : ./locktests -n 50 -f /file/system/to/test |
| * |
| * |
| * Vincent ROQUETA 2005 - vincent.roqueta@ext.bull.net |
| * BULL S.A. |
| */ |
| |
| #include "locktests.h" |
| |
| int MAXLEN=64; |
| int MAXTEST=10; |
| extern int maxClients; |
| extern int fdServeur; |
| |
| char message[M_SIZE]; |
| int esclaveLecteur; |
| int maitreLecteur; |
| int esclaveEcrivain; |
| |
| /* Quel lock sera appplique par le maitre en debut de test */ |
| /* Which lock will be applied by the master process on test startup */ |
| int LISTE_LOCKS[]= {READLOCK, WRITELOCK, |
| READLOCK, WRITELOCK, |
| READLOCK, WRITELOCK, |
| READLOCK, WRITELOCK, |
| BYTELOCK_READ, BYTELOCK_WRITE |
| }; |
| |
| /* Les operations que les programes esclaves essaieront de faire */ |
| /* The operations the slave processes will try to perform */ |
| |
| int LISTE_TESTS[]= {WRONLY, WRONLY, |
| RDONLY, RDONLY, |
| READLOCK, WRITELOCK, |
| WRITELOCK, READLOCK, |
| BYTELOCK_READ, BYTELOCK_WRITE |
| }; |
| |
| /* List of test names */ |
| char *LISTE_NOMS_TESTS[]={"WRITE ON A READ LOCK", |
| "WRITE ON A WRITE LOCK", |
| "READ ON A READ LOCK", |
| "READ ON A WRITE LOCK", |
| "SET A READ LOCK ON A READ LOCK", |
| "SET A WRITE LOCK ON A WRITE LOCK", |
| "SET A WRITE LOCK ON A READ LOCK", |
| "SET A READ LOCK ON A WRITE LOCK", |
| "READ LOCK THE WHOLE FILE BYTE BY BYTE", |
| "WRITE LOCK THE WHOLE FILE BYTE BY BYTE" |
| |
| }; |
| |
| /* Resultat attendu du test - Processus */ |
| /* List of expected test results, when slaves are processes */ |
| |
| int LISTE_RESULTATS_PROCESS[]= {SUCCES, SUCCES, |
| SUCCES, SUCCES, |
| SUCCES, ECHEC, |
| ECHEC, ECHEC, |
| SUCCES, SUCCES |
| |
| }; |
| /* List of expected test results, when slaves are threads */ |
| |
| int LISTE_RESULTATS_THREADS[]= { SUCCES, SUCCES, |
| SUCCES, SUCCES, |
| SUCCES, SUCCES, |
| SUCCES, SUCCES, |
| ECHEC, ECHEC |
| |
| }; |
| |
| int *LISTE_RESULTATS=NULL; |
| char *eType=NULL; |
| |
| int TOTAL_RESULTAT_OK[]={0,0,0,0,0,0,0,0,0,0}; |
| |
| void * esclave(void *donnees); |
| int (*terminer)(int a); |
| |
| int terminerProcess(int a) { |
| exit(a); |
| } |
| |
| int (*load)(); |
| |
| struct donneesPub dp; |
| |
| /* Manipulation des tests/ noms de tests/ resultats de tests */ |
| /* Functions to access tests/tests names/tests results*/ |
| int testSuiv(int n) { |
| return LISTE_TESTS[n]; |
| } |
| int resAttSuiv(int n) { |
| return LISTE_RESULTATS[n]; |
| } |
| char * nomTestSuiv(int n) { |
| return LISTE_NOMS_TESTS[n]; |
| } |
| int lockSuiv(int n) { |
| return LISTE_LOCKS[n]; |
| } |
| |
| /* Verifie si le resultat obtenu pour le test est le resultat attendu pour ce test */ |
| /* Verifie the test result is the expected one */ |
| int matchResult(int r, int n) { |
| |
| P("r=%d\n",r); |
| if (r==LISTE_RESULTATS[n]) |
| return 1; |
| else return 0; |
| } |
| |
| /* Incremente le nombre de process ayant valide le test */ |
| /* Increments the number of process which have successfully passed the test */ |
| void compteur(int r,int n) { |
| TOTAL_RESULTAT_OK[n]+=matchResult(r, n); |
| } |
| |
| /* Validation des resultats pour le lock de fichier octet par octet |
| * Pour chaque octet on verifie qu'un lock de 1 octet est bien pose |
| */ |
| /* Special case for test 'lock file byte byte by byte'. |
| * We ensure each byte is correctly locked. |
| */ |
| |
| void validationResultats(int n) { |
| int i,u,l, fsize; |
| struct flock request; |
| |
| fsize=dp.nclnt*(maxClients+1); |
| TOTAL_RESULTAT_OK[n]=0; |
| l=FALSE; |
| u=TRUE; |
| /* Si le resultat de l'operation attendu est un succe on prevoi d'incrementer le nombre de resultats corrects */ |
| /* If the expected operation result is a success, we will have to increase the number of correct results */ |
| if (LISTE_RESULTATS[n]) { |
| l=TRUE; |
| u=FALSE; |
| } |
| |
| for (i=0;i<fsize;i++) { |
| request.l_type=F_WRLCK; |
| request.l_whence=SEEK_SET; |
| request.l_start=i; |
| request.l_len=1; |
| fcntl(dp.fd, F_GETLK, &request); |
| /* On verifie si le lock est mis ou non */ |
| /* Ensure the lock is correctly set */ |
| if (request.l_type!=F_UNLCK) |
| TOTAL_RESULTAT_OK[n]+=l; |
| else |
| TOTAL_RESULTAT_OK[n]+=u; |
| } |
| } |
| |
| /* Procedures d'initialisation */ |
| /* Initialisation functions */ |
| |
| int initTest() { |
| |
| P("Maitre ouvre %s\n",dp.fname); |
| dp.fd = open(dp.fname, OPENFLAGS, MANDMODES); |
| if (dp.fd < 0) { |
| perror("lock test : can't open test file :"); |
| terminer(1); |
| } |
| P("fd=%d\n", dp.fd); |
| return 0; |
| } |
| |
| struct donneesFils *initClientFork(int i) |
| { |
| struct donneesPriv *dpr; |
| struct donneesFils *df; |
| |
| /* Initialisation des champs de donnees */ |
| /* Initialize private data fields */ |
| dpr=(struct donneesPriv *)malloc(sizeof(struct donneesPriv )); |
| df=(struct donneesFils *)malloc(sizeof(struct donneesFils)); |
| dpr->whoami=i; |
| df->dp=&dp; |
| df->dpr=dpr; |
| /* Initialisation du tubes de synchronisation */ |
| /* Initialize master to client pipe */ |
| dp.lclnt[i]=(int *)malloc(sizeof(int)*2); |
| if (pipe(dp.lclnt[i])<0) { |
| perror("Impossible to create pipe\n"); |
| exit(1); |
| } |
| P("Initialization %d\n", i); |
| write(0,".",1); |
| return df; |
| } |
| |
| int initialise(int clnt) { |
| |
| /* Initialisation des donnees publiques */ |
| /* Initialize private data fields */ |
| printf("Init\n"); |
| dp.nclnt=clnt; |
| dp.lclnt=(int **)malloc(sizeof(int *)*clnt); |
| dp.lthreads=(pthread_t *)malloc(sizeof(pthread_t)*clnt); |
| |
| /* initialisation de la communication avec le maitre */ |
| /* Initialize client to master pipe */ |
| if (pipe(dp.maitre)<0) { |
| perror("Master pipe creation error\n"); |
| exit(1); |
| } |
| printf("%s initialization\n",eType); |
| load(); |
| initTest(); |
| |
| return 0; |
| } |
| |
| void cleanClient(struct donneesFils *df) { |
| int i; |
| i=df->dpr->whoami; |
| free(dp.lclnt[i]); |
| free(df->dpr); |
| free(df); |
| } |
| |
| void clean() { |
| free(dp.lthreads); |
| free(dp.lclnt); |
| } |
| |
| int loadProcess() { |
| int i; |
| struct donneesFils *df; |
| for (i=0; i<dp.nclnt; i++) { |
| df=initClientFork(i); |
| if (!fork()) { |
| P("Running slave num: %d\n",df->dpr->whoami); |
| write(0,".",1); |
| esclave((void *)df); |
| cleanClient(df); |
| exit(0); |
| } |
| } |
| return 0; |
| } |
| |
| void lockWholeFile(struct flock *request) { |
| request->l_whence = SEEK_SET; |
| request->l_start = 0; |
| /* Lock de l'ensemble du fichier*/ |
| /* Lock the whole file */ |
| request->l_len = 0; |
| } |
| |
| void selectTest(int n, struct s_test *test) { |
| |
| test->test=testSuiv(n); |
| test->resAtt=resAttSuiv(n); |
| test->nom=nomTestSuiv(n); |
| test->type=lockSuiv(n); |
| } |
| |
| /* Final test report */ |
| void rapport(clnt) { |
| int i; |
| int totalClients; |
| totalClients=clnt*(maxClients+1); |
| printf("\n%s number : %d - Remote clients: %d local client 1 - Total client %d - Total concurent tests: %d\n", |
| eType, |
| clnt, |
| maxClients, |
| maxClients+1, |
| totalClients); |
| printf("%s number running test successfully :\n",eType); |
| for (i=0; i<MAXTEST; i++) |
| printf("%d %s of %d successfully ran test : %s\n", TOTAL_RESULTAT_OK[i], eType, totalClients, LISTE_NOMS_TESTS[i]); |
| |
| } |
| |
| int serverSendLocal() { |
| int i; |
| /* Synchronisation des processus esclaves. */ |
| /* Synchronize slave processes */ |
| |
| /* On configure les esclaves pour le test */ |
| /* Configure slaves for test */ |
| |
| for (i=0; i<dp.nclnt; i++) |
| write(dp.lclnt[i][1], message,M_SIZE ); |
| return 0; |
| |
| } |
| |
| void serverSendNet() { |
| writeToAllClients(message); |
| } |
| |
| int serverReceiveNet() { |
| int i,c; |
| for (c=0;c<maxClients; c++) { |
| for (i=0;i<dp.nclnt;i++) { |
| serverReceiveClient(c); |
| } |
| } |
| return 0; |
| } |
| |
| int serverReceiveLocal() { |
| int i; |
| for (i=0; i<dp.nclnt; i++) |
| read(maitreLecteur, message,M_SIZE ); |
| return 0; |
| } |
| |
| int clientReceiveLocal() { |
| read(esclaveLecteur,message,M_SIZE); |
| return 0; |
| } |
| |
| int clientSend() { |
| write(esclaveEcrivain, message, M_SIZE); |
| return 0; |
| } |
| |
| int serverSend() { |
| serverSendNet(); |
| serverSendLocal(); |
| return 0; |
| } |
| |
| int serverReceive() { |
| serverReceiveNet(); |
| serverReceiveLocal(); |
| return 0; |
| } |
| |
| /* binary structure <-> ASCII functions used to ensure data will be correctly used over |
| * the network, especially when multiples clients do not use the same hardware architecture. |
| */ |
| int serialiseTLock(struct s_test *tLock) { |
| memset(message,0,M_SIZE); |
| sprintf(message,"T:%d:%d:%d::",tLock->test, tLock->type,tLock->resAtt ); |
| return 0; |
| } |
| |
| void unSerialiseTLock(struct s_test *tLock) { |
| sscanf(message, "T:%d:%d:%d::",&(tLock->test),&( tLock->type),&(tLock->resAtt) ); |
| memset(message,0, M_SIZE); |
| |
| } |
| |
| void serialiseFLock(struct flock *request) { |
| int len, pid, start; |
| memset(message,0,M_SIZE); |
| len=(int )request->l_len; |
| pid=(int )request->l_pid; |
| start=(int)request->l_start; |
| /* Beware to length of integer conversions ... */ |
| sprintf(message,"L:%hd:%hd:%d:%d:%d::", |
| request->l_type, |
| request->l_whence, |
| start, |
| len, |
| pid |
| ); |
| } |
| |
| void serialiseResult(int resultat) { |
| memset(message, 0, M_SIZE); |
| sprintf(message,"R:%d::", resultat); |
| |
| } |
| |
| void unSerialiseResult(int *resultat) { |
| sscanf(message,"R:%d::", resultat); |
| } |
| |
| void unSerialiseFLock(struct flock *request) { |
| int len, pid, start; |
| sscanf(message,"L:%hd:%hd:%d:%d:%d::", |
| &(request->l_type), |
| &(request->l_whence), |
| &start, |
| &len, |
| &pid |
| ); |
| request->l_start=(off_t)start; |
| request->l_len=(off_t)len; |
| request->l_pid=(pid_t)pid; |
| } |
| |
| int serverSendLockClient(struct flock *request, int client) { |
| serialiseFLock(request); |
| return serverSendClient(client); |
| } |
| |
| int serverSendLockLocal(struct flock *request, int esclave) { |
| serialiseFLock(request); |
| return write(dp.lclnt[esclave][1], message, M_SIZE); |
| } |
| |
| int getLockSection(struct flock *request) { |
| memset(message, 0, M_SIZE); |
| clientReceiveLocal(); |
| unSerialiseFLock(request); |
| return 0; |
| } |
| |
| int sendLockTest(struct s_test *tLock) { |
| serialiseTLock(tLock); |
| serverSend(); |
| return 0; |
| } |
| |
| int getLockTest(struct s_test *tLock) { |
| clientReceiveLocal(); |
| unSerialiseTLock(tLock); |
| return 0; |
| } |
| |
| int sendResult(int resultat) { |
| serialiseResult(resultat); |
| clientSend(); |
| return 0; |
| } |
| |
| int getResults(int ntest) { |
| int i,c; |
| int resultat=0; |
| /* On comptabilise les resultats distants */ |
| /* Add remote test results */ |
| for (c=0;c<maxClients;c++) { |
| for (i=0;i<dp.nclnt; i++) { |
| serverReceiveClient(c); |
| unSerialiseResult(&resultat); |
| compteur(resultat,ntest); |
| |
| } |
| } |
| /* On comptabilise les resultats locaux */ |
| /* Add local test results */ |
| for (i=0; i<dp.nclnt; i++) { |
| read(maitreLecteur, message,M_SIZE ); |
| unSerialiseResult(&resultat); |
| compteur(resultat,ntest); |
| } |
| |
| return 0; |
| } |
| |
| /* Usefull debug macro */ |
| #ifdef DEBUG |
| #define P(a,b) memset(dbg,0,16);sprintf(dbg,a,b);write(0,dbg,16); |
| #endif |
| |
| /* Le maitre de l'application client n'est qu'un repetiteur d'information. |
| * Il retransmet l'information qu'il reçoit du maitre du client a ses esclaves |
| */ |
| /* In the case of a network use, the master of the client application si only |
| * a 'repeater' of information. It resends server-master instructions to its own slaves. |
| */ |
| |
| void maitreClient() { |
| fd_set fdread; |
| struct timeval tv; |
| int n,i,r,m,start; |
| #ifdef DEBUG |
| char dbg[16]; |
| #endif |
| struct flock lock; |
| int t; |
| |
| maitreLecteur=dp.maitre[0]; |
| FD_ZERO(&fdread); |
| tv.tv_sec = 50; |
| tv.tv_usec = 0; |
| n=fdServeur>maitreLecteur?fdServeur:maitreLecteur; |
| printf("Maitre CLient - fdServeur=%d\n", fdServeur); |
| while (1) { |
| /* On ajoute les descripteurs esclave et serveur */ |
| /* Add slave and server pipe file descriptors */ |
| FD_ZERO(&fdread); |
| FD_SET(fdServeur,&fdread); |
| FD_SET(maitreLecteur,&fdread); |
| r=select(n+1, &fdread, NULL, NULL,&tv); |
| if (r<0) { |
| perror("select:\n"); |
| continue; |
| } |
| if (r==0) { |
| exit(0); |
| } |
| |
| if (FD_ISSET(fdServeur, &fdread)) { |
| /* On vient de recevoir une information du serveur |
| * On la retransmet aux esclaves |
| */ |
| /* We just have received information from the server. |
| * We repeat it to slaves. |
| */ |
| i=readFromServer(message); |
| t=message[0]; |
| switch(t) { |
| case 'L': |
| /* Instruction : Lock. Dans ce cas il faut envoyer a chaque processus une section differente a locker */ |
| /* Lock instruction. We need to send a different section to lock to each process */ |
| |
| unSerialiseFLock(&lock); |
| start=lock.l_start; |
| for (i=0;i<dp.nclnt;i++) { |
| lock.l_start=start+i; |
| serialiseFLock(&lock); |
| write(dp.lclnt[i][1], message,M_SIZE); |
| } |
| printf("\n"); |
| continue; |
| case 'T': |
| /* Instruction: Test. Il s'agit d'une trame annoncant un nouveau test : on verifie qu'il ne s'agit pas d'une |
| * demande de FIN des tests |
| */ |
| /* Test instruction. Ensure server is not sending the FIN(ish) instruction to end tests */ |
| |
| /* A re-ecrire un peu mieux des que possible */ |
| /* To be rewritten asap*/ |
| m=atoi(&(message[2])); |
| if (m==FIN) |
| break; |
| if (m==CLEAN) |
| printf("\n"); |
| |
| serverSendLocal(); |
| continue; |
| } |
| break; |
| }else{ |
| /* Dans le cas inverse, on lis les esclaves et on remonte l'information au serveur |
| */ |
| /* Else, we read information from slaves and repeat them to the server */ |
| for (i=0;i<dp.nclnt;i++) { |
| r=read(maitreLecteur, message,M_SIZE ); |
| r=write(fdServeur,message,M_SIZE ); |
| if (r<0)perror("write : "); |
| |
| } |
| continue; |
| } |
| } |
| |
| /* On vient de recevoir la trame FIN de programme */ |
| /* Receive the FIN(ish) instruction*/ |
| |
| /* On la communique a tous les esclaves */ |
| /* Repeat it to the slaves */ |
| printf("Fin du programme en cours...\n"); |
| serverSendLocal(); |
| |
| /* Et c'est fini! */ |
| /* Ok, we can quit */ |
| printf("Bye :)\n"); |
| |
| } |
| |
| void maitre() |
| { |
| int i,n,bl; |
| int clnt; |
| char tmp[MAXLEN], *buf; |
| #ifdef DEBUG |
| char dbg[16]; |
| #endif |
| struct flock request; |
| struct s_test tLock; |
| enum etat_t etat; |
| int offset; |
| /* A test sentence written in the file */ |
| char phraseTest[]="Ceci est une phrase test ecrite par le maitre dans le fichier"; |
| bl = -1; |
| clnt=dp.nclnt; |
| maitreLecteur=dp.maitre[0]; |
| etat=SELECT; |
| /* On commence par le premier test. C'est original ;) */ |
| /* Start with the first test ;) */ |
| n=0; |
| printf("\n--------------------------------------\n"); |
| while (1) { |
| switch(etat) { |
| case SELECT: |
| /* Selection du test a effectuer */ |
| /* Select the test to perform */ |
| printf("\n"); |
| E("Maitre: SELECT"); |
| selectTest(n, &tLock); |
| etat=tLock.type; |
| bl=0; |
| if (n<MAXTEST) { |
| memset(tmp,0,MAXLEN); |
| sprintf(tmp,"TEST : TRY TO %s:",LISTE_NOMS_TESTS[n]); |
| write(0, tmp, strlen(tmp)); |
| } |
| else |
| etat=FIN; |
| P("etat=%d\n", etat); |
| n+=1; |
| continue; |
| |
| case RDONLY: |
| case WRONLY: |
| |
| case READLOCK: |
| P("Read lock :%d\n", etat); |
| request.l_type=F_RDLCK; |
| etat=LOCK; |
| continue; |
| |
| case WRITELOCK: |
| P("Write lock :%d\n", etat); |
| request.l_type=F_WRLCK; |
| etat=LOCK; |
| continue; |
| |
| case LOCK: |
| /* On applique le lock que l'on veut */ |
| /* Apply the wanted lock */ |
| E("Maitre: LOCK"); |
| write(dp.fd,phraseTest,strlen(phraseTest)); |
| lockWholeFile(&request); |
| if (fcntl(dp.fd, F_SETLK, &request)<0) { |
| perror("Master: can't set lock\n"); |
| perror("Echec\n"); |
| exit(0); |
| } |
| E("Maitre"); |
| etat=SYNC; |
| continue; |
| |
| case BYTELOCK_READ: |
| bl=1; |
| request.l_type=F_RDLCK; |
| etat=SYNC; |
| continue; |
| |
| case BYTELOCK_WRITE: |
| bl=1; |
| request.l_type=F_WRLCK; |
| etat=SYNC; |
| continue; |
| |
| case BYTELOCK: |
| /* |
| * L'idee est de faire locker l'ensemble du fichier octet par octet par un ensemble de sous processus |
| * Il nous faut donc |
| * -creer un fichier ayant autant d'octet que le nombre de processus passe en parametres |
| * -passer les sections a locker a chacun des esclaves |
| * -verifier que les locks sont bien appliques |
| * |
| */ |
| |
| /* The main idea is to lock all the bytes in a file. Each slave process locks one byte. |
| * |
| * We need : |
| * - To create a file of a length equal to the total number of slave processes |
| * - send the exact section to lock to each slave |
| * - ensure locks have been correctly set |
| */ |
| |
| /* On cree une chaine de caracteres a enregistrer dans le fichier qui contienne exactement un octet par |
| * processus. |
| */ |
| /* Create a string to record in the test file. Length is exactly the number of sub process */ |
| P("Maitre: BYTELOCK: %d\n", etat); |
| buf=(char *)malloc(clnt*(maxClients+1)); |
| memset(buf,'*', clnt); |
| write(dp.fd, buf, clnt); |
| free(buf); |
| |
| /* Chaque processus esclave reecrit son champs a locker. */ |
| /* Each slave process re-writes its own field to lock */ |
| request.l_whence=SEEK_SET; |
| request.l_start=0; |
| request.l_len=1; |
| |
| /* On commence par les envois reseau */ |
| /* Start to send sections to lock to remote process (network clients) */ |
| |
| for (i=0;i<maxClients;i++) { |
| /* On renseigne la structure avec le lock qui va bien */ |
| /* Set the correct byte to lock */ |
| offset=(i+1)*clnt; |
| request.l_start=(off_t )offset; |
| serverSendLockClient(&request, i); |
| } |
| |
| /* Puis les envois locaux */ |
| /* Now send sections to local processes */ |
| for (i=0;i<clnt;i++) { |
| request.l_start=i; |
| serverSendLockLocal(&request, i); |
| } |
| etat=RESULTAT; |
| continue; |
| |
| case SYNC: |
| sendLockTest(&tLock); |
| if (bl) { |
| etat=BYTELOCK; |
| continue; |
| } |
| |
| if (n<MAXTEST+1) etat=RESULTAT; |
| else etat=FIN; |
| continue; |
| |
| case RESULTAT: |
| /* On lit les resultats un par un */ |
| /* Read results by one */ |
| getResults(n-1); |
| if (bl) validationResultats(n-1); |
| etat=CLEAN; |
| continue; |
| |
| case CLEAN: |
| /* On demande aux clients d'arreter le test */ |
| /* Ask the clients to stop testing ... */ |
| tLock.test=CLEAN; |
| serialiseTLock(&tLock); |
| serverSend(); |
| /* ... et on attend un accuse de reception avant de fermer */ |
| /* ... and wait for an ack before closing */ |
| serverReceive(); |
| /* On ignore les resultats, ce n'est qu'un accuse de reception */ |
| /* Ignore message content : that is only an ack */ |
| |
| /* close and open file */ |
| close(dp.fd); |
| initTest(dp); |
| etat=SELECT; |
| continue; |
| case FIN: |
| tLock.test=FIN; |
| serialiseTLock(&tLock); |
| serverSend(); |
| sleep(2); |
| break; |
| |
| printf("(end)\n"); |
| exit(0); |
| |
| }/* switch */ |
| break; |
| }/* while */ |
| |
| rapport(clnt); |
| } |
| |
| /* Slave process/thread */ |
| |
| void * esclave(void *donnees) |
| { |
| struct donneesFils *df; |
| int i, a, resultat,ftest; |
| struct s_test tLock; |
| struct flock request; |
| char tmp[16]; |
| #ifdef DEBUG |
| char dbg[16]; |
| #endif |
| char *phraseTest="L'ecriture a reussi"; |
| int len; |
| enum etat_t etat; |
| |
| resultat = -1; |
| ftest = -1; |
| len=strlen(phraseTest); |
| df=(struct donneesFils *)donnees; |
| i=df->dpr->whoami; |
| P("Esclave n=%d\n", i); |
| esclaveLecteur=dp.lclnt[i][0]; |
| esclaveEcrivain=dp.maitre[1]; |
| etat=SYNC; |
| errno=0; |
| memset(tmp,0,16); |
| while (1) { |
| switch(etat) { |
| case SELECT: |
| case SYNC: |
| getLockTest(&tLock); |
| etat=tLock.test; |
| P("Esclave Etat=%d\n",etat); |
| |
| continue; |
| case RDONLY: |
| /* On tente de lire un fichier */ |
| /* Try to read a file */ |
| P("TEST READ ONLY %d\n", RDONLY); |
| if ((ftest=open(dp.fname, O_RDONLY|O_NONBLOCK))<0) { |
| resultat=ECHEC; |
| etat=RESULTAT; |
| if (dp.verbose) |
| perror("Open:"); |
| continue; |
| } |
| P("fd=%d\n",ftest); |
| a=read(ftest, tmp, 16); |
| if (a<16) |
| resultat=ECHEC; |
| else |
| resultat=SUCCES; |
| etat=RESULTAT; |
| continue; |
| |
| case WRONLY: |
| /* On tente d'ecrire un fichier */ |
| /* Try to write a file */ |
| P("TEST WRITE ONLY %d\n", WRONLY); |
| if ((ftest=open(dp.fname, O_WRONLY|O_NONBLOCK))<0) { |
| resultat=ECHEC; |
| etat=RESULTAT; |
| if (dp.verbose) |
| perror("Open read only:"); |
| continue; |
| } |
| P("fd=%d\n",ftest); |
| if (write(ftest, phraseTest, len)<len) |
| resultat=ECHEC; |
| else |
| resultat=SUCCES; |
| etat=RESULTAT; |
| continue; |
| |
| case LOCK: |
| |
| case READLOCK: |
| /* On essaie de lire une section lockee en lecture sur le fichier */ |
| /* Try to read a read-locked section */ |
| P("READ LOCK %d\n", F_RDLCK); |
| if ((ftest=open(dp.fname, O_RDONLY|O_NONBLOCK))<0) { |
| resultat=ECHEC; |
| etat=RESULTAT; |
| if (dp.verbose) |
| perror("Open read-write:"); |
| continue; |
| } |
| |
| P("fd=%d\n",ftest); |
| /* Lock de l'ensemble du fichier*/ |
| /* Lock the whole file */ |
| request.l_type=F_RDLCK; |
| request.l_whence = SEEK_SET; |
| request.l_start = 0; |
| request.l_len = 0; |
| |
| if (fcntl(ftest, F_SETLK, &request)<0) { |
| if (dp.verbose || errno != EAGAIN) |
| perror("RDONLY: fcntl"); |
| resultat=ECHEC; |
| } |
| else |
| resultat=SUCCES; |
| etat=RESULTAT; |
| continue; |
| |
| case WRITELOCK: |
| /* On essaie d'ecrire le fichier */ |
| /* Try to write a file */ |
| P("WRITE LOCK %d\n", F_WRLCK); |
| if ((ftest=open(dp.fname, O_WRONLY|O_NONBLOCK))<0) { |
| resultat=ECHEC; |
| etat=RESULTAT; |
| if (dp.verbose) |
| perror("\nOpen:"); |
| continue; |
| } |
| /* Lock de l'ensemble du fichier*/ |
| /* Lock the whole file */ |
| P("fd=%d\n",ftest); |
| request.l_type=F_WRLCK; |
| request.l_whence = SEEK_SET; |
| request.l_start = 0; |
| request.l_len = 0; |
| |
| if (fcntl(ftest, F_SETLK, &request)<0) { |
| if (dp.verbose || errno != EAGAIN) |
| perror("\nWRONLY: fcntl"); |
| resultat=ECHEC; |
| } |
| else |
| resultat=SUCCES; |
| etat=RESULTAT; |
| continue; |
| |
| case BYTELOCK_READ: |
| P("BYTE LOCK READ: %d\n", etat); |
| etat=BYTELOCK; |
| continue; |
| |
| case BYTELOCK_WRITE: |
| P("BYTE LOCK WRITE: %d\n", etat); |
| etat=BYTELOCK; |
| continue; |
| |
| case BYTELOCK: |
| /* On se met en attente de la section a locker. L'ensemble de la structure est |
| * donnee par le maitre et transmise par le tube. |
| */ |
| /* Wait for the exact section to lock. The whole file is sent by the master */ |
| |
| P("BYTE LOCK %d\n", etat); |
| getLockSection(&request); |
| if ((ftest=open(dp.fname, O_RDWR|O_NONBLOCK))<0) { |
| resultat=ECHEC; |
| etat=RESULTAT; |
| if (dp.verbose) |
| perror("\nOpen:"); |
| continue; |
| } |
| |
| if (fcntl(ftest, F_SETLK, &request)<0) { |
| if (dp.verbose || errno != EAGAIN) |
| perror("\nBTLOCK: fcntl"); |
| resultat=ECHEC; |
| etat=RESULTAT; |
| continue; |
| } |
| /* On change le caractere a la place donnee pour verification */ |
| /* Change the character at the given position for an easier verification */ |
| a=lseek(ftest, request.l_start,SEEK_SET); |
| write(ftest,"=",1); |
| resultat=SUCCES; |
| etat=RESULTAT; |
| continue; |
| |
| case RESULTAT: |
| if (resultat==SUCCES) |
| write(0,"=",1); |
| else |
| write(0,"x",1); |
| P("RESULTAT: %d\n", resultat); |
| sendResult(resultat); |
| etat=SYNC; |
| continue; |
| |
| case CLEAN: |
| close(ftest); |
| /* Send CLEAN Ack */ |
| sendResult(resultat); |
| etat=SYNC; |
| continue; |
| |
| case FIN: |
| E("(End)\n"); |
| terminer(0); |
| printf("Unknown command\n"); |
| terminer(1); |
| |
| } |
| } |
| |
| } |
| |
| char *nextArg(int argc, char **argv, int *i) { |
| if (((*i)+1)<argc) { |
| (*i)+=1; |
| return argv[(*i)]; |
| } |
| return NULL; |
| } |
| |
| int usage() { |
| printf("locktest -n <number of process> -f <test file> [-T]\n"); |
| printf("Number of child process must be higher than 1\n"); |
| printf("\n"); |
| printf("Send bugs to vincent.roqueta@ext.bull.net\n"); |
| exit(0); |
| return 0; |
| } |
| |
| int main(int argc,char ** argv) { |
| int i, nThread=0; |
| char *tmp; |
| int type=0; |
| int clients; |
| type=PROCESS; |
| dp.fname=NULL; |
| dp.verbose=0; |
| int serveur=1; |
| char *host; |
| |
| host = NULL; |
| clients=0; |
| |
| for (i=1;i<argc;i++) { |
| |
| if (!strcmp("-n",argv[i])) { |
| if (!(tmp=nextArg(argc, argv,&i))) |
| usage(); |
| nThread=atoi(tmp); |
| continue; |
| } |
| |
| if (!strcmp("-f",argv[i])) { |
| if (!(dp.fname=nextArg(argc, argv,&i))) |
| usage(); |
| continue; |
| } |
| if (!strcmp("-v",argv[i])) { |
| dp.verbose=TRUE; |
| continue; |
| } |
| if (!strcmp("-c",argv[i])) { |
| if (!(clients=atoi(nextArg(argc, argv,&i)))) |
| usage(); |
| continue; |
| } |
| |
| if (!strcmp("--server",argv[i])) { |
| if (!(host=nextArg(argc, argv,&i))) |
| usage(); |
| serveur=0; |
| continue; |
| } |
| /* Option inconnue */ |
| printf("Ignoring unknown option: %s\n", argv[i]); |
| } |
| |
| if (serveur) { |
| if (!(dp.fname && nThread)) |
| usage(); |
| if (clients>0) { |
| configureServeur(clients); |
| setupClients(type, dp.fname, nThread); |
| } |
| }else{ |
| configureClient(host); |
| dp.fname=(char *)malloc(512); |
| memset(dp.fname,0, 512); |
| getConfiguration(&type, dp.fname, &nThread); |
| } |
| |
| if (dp.verbose) |
| printf("By process.\n"); |
| load=loadProcess; |
| eType="process"; |
| terminer=terminerProcess; |
| LISTE_RESULTATS=LISTE_RESULTATS_PROCESS; |
| initialise(nThread); |
| if (serveur) { |
| maitre(); |
| |
| }else{ |
| maitreClient(); |
| free(dp.fname); |
| } |
| clean(); |
| |
| return 0; |
| } |