You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1035 lines
21 KiB
1035 lines
21 KiB
/* *************************************************
|
|
* *********** 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 fdServer;
|
|
|
|
char message[M_SIZE];
|
|
int slaveReader;
|
|
int masterReader;
|
|
int slaveWriter;
|
|
|
|
/* Which lock will be applied by the master process on test startup */
|
|
int LIST_LOCKS[] = { READLOCK, WRITELOCK,
|
|
READLOCK, WRITELOCK,
|
|
READLOCK, WRITELOCK,
|
|
READLOCK, WRITELOCK,
|
|
BYTELOCK_READ, BYTELOCK_WRITE
|
|
};
|
|
|
|
/* The operations the slave processes will try to perform */
|
|
int LIST_TESTS[] = { WRONLY, WRONLY,
|
|
RDONLY, RDONLY,
|
|
READLOCK, WRITELOCK,
|
|
WRITELOCK, READLOCK,
|
|
BYTELOCK_READ, BYTELOCK_WRITE
|
|
};
|
|
|
|
/* List of test names */
|
|
char *LIST_NAMES_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"
|
|
};
|
|
|
|
/* List of expected test results, when slaves are processes */
|
|
int LIST_RESULTS_PROCESS[] = { SUCCES, SUCCES,
|
|
SUCCES, SUCCES,
|
|
SUCCES, ECHEC,
|
|
ECHEC, ECHEC,
|
|
SUCCES, SUCCES
|
|
};
|
|
|
|
/* List of expected test results, when slaves are threads */
|
|
int LIST_RESULTS_THREADS[] = { SUCCES, SUCCES,
|
|
SUCCES, SUCCES,
|
|
SUCCES, SUCCES,
|
|
SUCCES, SUCCES,
|
|
ECHEC, ECHEC
|
|
};
|
|
|
|
int *LIST_RESULTS = NULL;
|
|
char *eType = NULL;
|
|
|
|
int TOTAL_RESULT_OK[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
|
|
void *slave(void *data);
|
|
int (*finish) (int a);
|
|
|
|
int finishProcess(int a)
|
|
{
|
|
exit(a);
|
|
}
|
|
|
|
int (*load) (void);
|
|
|
|
struct dataPub dp;
|
|
|
|
/* Functions to access tests/tests names/tests results*/
|
|
int testSuiv(int n)
|
|
{
|
|
return LIST_TESTS[n];
|
|
}
|
|
|
|
int resAttSuiv(int n)
|
|
{
|
|
return LIST_RESULTS[n];
|
|
}
|
|
|
|
char *nomTestSuiv(int n)
|
|
{
|
|
return LIST_NAMES_TESTS[n];
|
|
}
|
|
|
|
int lockSuiv(int n)
|
|
{
|
|
return LIST_LOCKS[n];
|
|
}
|
|
|
|
/* Verify the test result is the expected one */
|
|
int matchResult(int r, int n)
|
|
{
|
|
|
|
P("r=%d\n", r);
|
|
if (r == LIST_RESULTS[n])
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/* Increments the number of process which have successfully passed the test */
|
|
void counter(int r, int n)
|
|
{
|
|
TOTAL_RESULT_OK[n] += matchResult(r, n);
|
|
}
|
|
|
|
/* Special case for test 'lock file byte byte by byte'.
|
|
* We ensure each byte is correctly locked.
|
|
*/
|
|
void validationResults(int n)
|
|
{
|
|
int i, u, l, fsize;
|
|
struct flock request;
|
|
|
|
fsize = dp.nclnt * (maxClients + 1);
|
|
TOTAL_RESULT_OK[n] = 0;
|
|
l = FALSE;
|
|
u = TRUE;
|
|
|
|
/* If the expected operation result is a success, we will have to increase the number of correct results */
|
|
if (LIST_RESULTS[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);
|
|
/* Ensure the lock is correctly set */
|
|
if (request.l_type != F_UNLCK)
|
|
TOTAL_RESULT_OK[n] += l;
|
|
else
|
|
TOTAL_RESULT_OK[n] += u;
|
|
}
|
|
}
|
|
|
|
int initTest(void)
|
|
{
|
|
|
|
P("Master opens %s\n", dp.fname);
|
|
dp.fd = open(dp.fname, OPENFLAGS, MANDMODES);
|
|
if (dp.fd < 0) {
|
|
perror("lock test : can't open test file :");
|
|
finish(1);
|
|
}
|
|
P("fd=%d\n", dp.fd);
|
|
return 0;
|
|
}
|
|
|
|
struct dataChild *initClientFork(int i)
|
|
{
|
|
struct dataPriv *dpr;
|
|
struct dataChild *df;
|
|
|
|
/* Initialize private data fields */
|
|
dpr = malloc(sizeof(struct dataPriv));
|
|
df = malloc(sizeof(struct dataChild));
|
|
dpr->whoami = i;
|
|
df->dp = &dp;
|
|
df->dpr = dpr;
|
|
/* Initialize master to client pipe */
|
|
dp.lclnt[i] = 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 initialize(int clnt)
|
|
{
|
|
|
|
/* Initialize private data fields */
|
|
printf("Init\n");
|
|
dp.nclnt = clnt;
|
|
dp.lclnt = malloc(sizeof(int *) * clnt);
|
|
dp.lthreads = malloc(sizeof(pthread_t) * clnt);
|
|
|
|
/* Initialize client to master pipe */
|
|
if (pipe(dp.master) < 0) {
|
|
perror("Master pipe creation error\n");
|
|
exit(1);
|
|
}
|
|
printf("%s initialization\n", eType);
|
|
load();
|
|
initTest();
|
|
|
|
return 0;
|
|
}
|
|
|
|
void cleanClient(struct dataChild *df)
|
|
{
|
|
int i;
|
|
i = df->dpr->whoami;
|
|
free(dp.lclnt[i]);
|
|
free(df->dpr);
|
|
free(df);
|
|
}
|
|
|
|
void clean(void)
|
|
{
|
|
free(dp.lthreads);
|
|
free(dp.lclnt);
|
|
}
|
|
|
|
int loadProcess(void)
|
|
{
|
|
int i;
|
|
struct dataChild *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);
|
|
slave((void *)df);
|
|
cleanClient(df);
|
|
exit(0);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void lockWholeFile(struct flock *request)
|
|
{
|
|
request->l_whence = SEEK_SET;
|
|
request->l_start = 0;
|
|
/* 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 */
|
|
int report(int clnt)
|
|
{
|
|
int rc = 0;
|
|
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++) {
|
|
if (TOTAL_RESULT_OK[i] != totalClients)
|
|
rc = 1;
|
|
|
|
printf("%d %s of %d successfully ran test : %s\n",
|
|
TOTAL_RESULT_OK[i], eType, totalClients,
|
|
LIST_NAMES_TESTS[i]);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
int serverSendLocal(void)
|
|
{
|
|
int i;
|
|
/* Synchronize slave processes */
|
|
/* Configure slaves for test */
|
|
|
|
for (i = 0; i < dp.nclnt; i++)
|
|
write(dp.lclnt[i][1], message, M_SIZE);
|
|
return 0;
|
|
|
|
}
|
|
|
|
void serverSendNet(void)
|
|
{
|
|
writeToAllClients(message);
|
|
}
|
|
|
|
int serverReceiveNet(void)
|
|
{
|
|
int i, c;
|
|
for (c = 0; c < maxClients; c++) {
|
|
for (i = 0; i < dp.nclnt; i++) {
|
|
serverReceiveClient(c);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int serverReceiveLocal(void)
|
|
{
|
|
int i;
|
|
for (i = 0; i < dp.nclnt; i++)
|
|
read(masterReader, message, M_SIZE);
|
|
return 0;
|
|
}
|
|
|
|
int clientReceiveLocal(void)
|
|
{
|
|
read(slaveReader, message, M_SIZE);
|
|
return 0;
|
|
}
|
|
|
|
int clientSend(void)
|
|
{
|
|
write(slaveWriter, message, M_SIZE);
|
|
return 0;
|
|
}
|
|
|
|
int serverSend(void)
|
|
{
|
|
serverSendNet();
|
|
serverSendLocal();
|
|
return 0;
|
|
}
|
|
|
|
int serverReceive(void)
|
|
{
|
|
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 serializeTLock(struct s_test *tLock)
|
|
{
|
|
memset(message, 0, M_SIZE);
|
|
sprintf(message, "T:%d:%d:%d::", tLock->test, tLock->type,
|
|
tLock->resAtt);
|
|
return 0;
|
|
}
|
|
|
|
void unSerializeTLock(struct s_test *tLock)
|
|
{
|
|
sscanf(message, "T:%d:%d:%d::", &(tLock->test), &(tLock->type),
|
|
&(tLock->resAtt));
|
|
memset(message, 0, M_SIZE);
|
|
|
|
}
|
|
|
|
void serializeFLock(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 serializeResult(int result)
|
|
{
|
|
memset(message, 0, M_SIZE);
|
|
sprintf(message, "R:%d::", result);
|
|
|
|
}
|
|
|
|
void unSerializeResult(int *result)
|
|
{
|
|
sscanf(message, "R:%d::", result);
|
|
}
|
|
|
|
void unSerializeFLock(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)
|
|
{
|
|
serializeFLock(request);
|
|
return serverSendClient(client);
|
|
}
|
|
|
|
int serverSendLockLocal(struct flock *request, int slave)
|
|
{
|
|
serializeFLock(request);
|
|
return write(dp.lclnt[slave][1], message, M_SIZE);
|
|
}
|
|
|
|
int getLockSection(struct flock *request)
|
|
{
|
|
memset(message, 0, M_SIZE);
|
|
clientReceiveLocal();
|
|
unSerializeFLock(request);
|
|
return 0;
|
|
}
|
|
|
|
int sendLockTest(struct s_test *tLock)
|
|
{
|
|
serializeTLock(tLock);
|
|
serverSend();
|
|
return 0;
|
|
}
|
|
|
|
int getLockTest(struct s_test *tLock)
|
|
{
|
|
clientReceiveLocal();
|
|
unSerializeTLock(tLock);
|
|
return 0;
|
|
}
|
|
|
|
int sendResult(int result)
|
|
{
|
|
serializeResult(result);
|
|
clientSend();
|
|
return 0;
|
|
}
|
|
|
|
int getResults(int ntest)
|
|
{
|
|
int i, c;
|
|
int result = 0;
|
|
/* Add remote test results */
|
|
for (c = 0; c < maxClients; c++) {
|
|
for (i = 0; i < dp.nclnt; i++) {
|
|
serverReceiveClient(c);
|
|
unSerializeResult(&result);
|
|
counter(result, ntest);
|
|
|
|
}
|
|
}
|
|
/* Add local test results */
|
|
for (i = 0; i < dp.nclnt; i++) {
|
|
read(masterReader, message, M_SIZE);
|
|
unSerializeResult(&result);
|
|
counter(result, ntest);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
#define P(a,b) memset(dbg,0,16);sprintf(dbg,a,b);write(0,dbg,16);
|
|
#endif
|
|
|
|
/* 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 masterClient(void)
|
|
{
|
|
fd_set fdread;
|
|
struct timeval tv;
|
|
int n, i, r, m, start;
|
|
#ifdef DEBUG
|
|
char dbg[16];
|
|
#endif
|
|
struct flock lock;
|
|
int t;
|
|
|
|
masterReader = dp.master[0];
|
|
FD_ZERO(&fdread);
|
|
tv.tv_sec = 50;
|
|
tv.tv_usec = 0;
|
|
n = fdServer > masterReader ? fdServer : masterReader;
|
|
printf("Master Client - fdServer=%d\n", fdServer);
|
|
while (1) {
|
|
/* Add slave and server pipe file descriptors */
|
|
FD_ZERO(&fdread);
|
|
FD_SET(fdServer, &fdread);
|
|
FD_SET(masterReader, &fdread);
|
|
r = select(n + 1, &fdread, NULL, NULL, &tv);
|
|
if (r < 0) {
|
|
perror("select:\n");
|
|
continue;
|
|
}
|
|
if (r == 0) {
|
|
exit(0);
|
|
}
|
|
|
|
if (FD_ISSET(fdServer, &fdread)) {
|
|
/* We just have received information from the server.
|
|
* We repeat it to slaves.
|
|
*/
|
|
i = readFromServer(message);
|
|
t = message[0];
|
|
switch (t) {
|
|
case 'L':
|
|
/* Lock instruction. We need to send a different section to lock to each process */
|
|
unSerializeFLock(&lock);
|
|
start = lock.l_start;
|
|
for (i = 0; i < dp.nclnt; i++) {
|
|
lock.l_start = start + i;
|
|
serializeFLock(&lock);
|
|
write(dp.lclnt[i][1], message, M_SIZE);
|
|
}
|
|
printf("\n");
|
|
continue;
|
|
case 'T':
|
|
/* Test instruction. Ensure server is not sending the END(ish) instruction to end tests */
|
|
/* To be rewritten asap */
|
|
m = atoi(&(message[2]));
|
|
if (m == END)
|
|
break;
|
|
if (m == CLEAN)
|
|
printf("\n");
|
|
|
|
serverSendLocal();
|
|
continue;
|
|
}
|
|
break;
|
|
} else {
|
|
/* Else, we read information from slaves and repeat them to the server */
|
|
for (i = 0; i < dp.nclnt; i++) {
|
|
r = read(masterReader, message, M_SIZE);
|
|
r = write(fdServer, message, M_SIZE);
|
|
if (r < 0)
|
|
perror("write : ");
|
|
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* Receive the END(ish) instruction */
|
|
|
|
/* Repeat it to the slaves */
|
|
printf("Exitting...\n");
|
|
serverSendLocal();
|
|
|
|
/* Ok, we can quit */
|
|
printf("Bye :)\n");
|
|
|
|
}
|
|
|
|
int master(void)
|
|
{
|
|
int i, n, bl;
|
|
int clnt;
|
|
char tmp[MAXLEN], *buf;
|
|
#ifdef DEBUG
|
|
char dbg[16];
|
|
#endif
|
|
struct flock request;
|
|
struct s_test tLock;
|
|
enum state_t state;
|
|
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;
|
|
masterReader = dp.master[0];
|
|
state = SELECT;
|
|
/* Start with the first test ;) */
|
|
n = 0;
|
|
printf("\n--------------------------------------\n");
|
|
while (1) {
|
|
switch (state) {
|
|
case SELECT:
|
|
/* Select the test to perform */
|
|
printf("\n");
|
|
E("Master: SELECT");
|
|
selectTest(n, &tLock);
|
|
state = tLock.type;
|
|
bl = 0;
|
|
if (n < MAXTEST) {
|
|
memset(tmp, 0, MAXLEN);
|
|
sprintf(tmp, "TEST : TRY TO %s:",
|
|
LIST_NAMES_TESTS[n]);
|
|
write(0, tmp, strlen(tmp));
|
|
} else
|
|
state = END;
|
|
P("state=%d\n", state);
|
|
n += 1;
|
|
continue;
|
|
|
|
case RDONLY:
|
|
case WRONLY:
|
|
|
|
case READLOCK:
|
|
P("Read lock :%d\n", state);
|
|
request.l_type = F_RDLCK;
|
|
state = LOCK;
|
|
continue;
|
|
|
|
case WRITELOCK:
|
|
P("Write lock :%d\n", state);
|
|
request.l_type = F_WRLCK;
|
|
state = LOCK;
|
|
continue;
|
|
|
|
case LOCK:
|
|
/* Apply the wanted lock */
|
|
E("Master: 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("Master");
|
|
state = SYNC;
|
|
continue;
|
|
|
|
case BYTELOCK_READ:
|
|
bl = 1;
|
|
request.l_type = F_RDLCK;
|
|
state = SYNC;
|
|
continue;
|
|
|
|
case BYTELOCK_WRITE:
|
|
bl = 1;
|
|
request.l_type = F_WRLCK;
|
|
state = SYNC;
|
|
continue;
|
|
|
|
case BYTELOCK:
|
|
/* 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
|
|
*/
|
|
|
|
/* Create a string to record in the test file. Length is exactly the number of sub process */
|
|
P("Master: BYTELOCK: %d\n", state);
|
|
buf = malloc(clnt * (maxClients + 1));
|
|
memset(buf, '*', clnt);
|
|
write(dp.fd, buf, clnt);
|
|
free(buf);
|
|
|
|
/* Each slave process re-writes its own field to lock */
|
|
request.l_whence = SEEK_SET;
|
|
request.l_start = 0;
|
|
request.l_len = 1;
|
|
|
|
/* Start to send sections to lock to remote process (network clients) */
|
|
for (i = 0; i < maxClients; i++) {
|
|
/* Set the correct byte to lock */
|
|
offset = (i + 1) * clnt;
|
|
request.l_start = (off_t) offset;
|
|
serverSendLockClient(&request, i);
|
|
}
|
|
|
|
/* Now send sections to local processes */
|
|
for (i = 0; i < clnt; i++) {
|
|
request.l_start = i;
|
|
serverSendLockLocal(&request, i);
|
|
}
|
|
state = RESULT;
|
|
continue;
|
|
|
|
case SYNC:
|
|
sendLockTest(&tLock);
|
|
if (bl) {
|
|
state = BYTELOCK;
|
|
continue;
|
|
}
|
|
|
|
if (n < MAXTEST + 1)
|
|
state = RESULT;
|
|
else
|
|
state = END;
|
|
continue;
|
|
|
|
case RESULT:
|
|
/* Read results by one */
|
|
getResults(n - 1);
|
|
if (bl)
|
|
validationResults(n - 1);
|
|
state = CLEAN;
|
|
continue;
|
|
|
|
case CLEAN:
|
|
/* Ask the clients to stop testing ... */
|
|
tLock.test = CLEAN;
|
|
serializeTLock(&tLock);
|
|
serverSend();
|
|
/* ... and wait for an ack before closing */
|
|
serverReceive();
|
|
/* Ignore message content : that is only an ack */
|
|
|
|
/* close and open file */
|
|
close(dp.fd);
|
|
initTest();
|
|
state = SELECT;
|
|
continue;
|
|
case END:
|
|
tLock.test = END;
|
|
serializeTLock(&tLock);
|
|
serverSend();
|
|
sleep(2);
|
|
break;
|
|
|
|
printf("(end)\n");
|
|
exit(0);
|
|
|
|
}
|
|
break;
|
|
}
|
|
|
|
return report(clnt);
|
|
}
|
|
|
|
/* Slave process/thread */
|
|
|
|
void *slave(void *data)
|
|
{
|
|
struct dataChild *df;
|
|
int i, a, result, 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 state_t state;
|
|
|
|
result = -1;
|
|
ftest = -1;
|
|
len = strlen(phraseTest);
|
|
df = (struct dataChild *)data;
|
|
i = df->dpr->whoami;
|
|
P("Slave n=%d\n", i);
|
|
slaveReader = dp.lclnt[i][0];
|
|
slaveWriter = dp.master[1];
|
|
state = SYNC;
|
|
errno = 0;
|
|
memset(tmp, 0, 16);
|
|
while (1) {
|
|
switch (state) {
|
|
case SELECT:
|
|
case SYNC:
|
|
getLockTest(&tLock);
|
|
state = tLock.test;
|
|
P("Slave State=%d\n", state);
|
|
|
|
continue;
|
|
case RDONLY:
|
|
/* Try to read a file */
|
|
P("TEST READ ONLY %d\n", RDONLY);
|
|
if ((ftest = open(dp.fname, O_RDONLY | O_NONBLOCK)) < 0) {
|
|
result = ECHEC;
|
|
state = RESULT;
|
|
if (dp.verbose)
|
|
perror("Open:");
|
|
continue;
|
|
}
|
|
P("fd=%d\n", ftest);
|
|
a = read(ftest, tmp, 16);
|
|
if (a < 16)
|
|
result = ECHEC;
|
|
else
|
|
result = SUCCES;
|
|
state = RESULT;
|
|
continue;
|
|
|
|
case WRONLY:
|
|
/* Try to write a file */
|
|
P("TEST WRITE ONLY %d\n", WRONLY);
|
|
if ((ftest = open(dp.fname, O_WRONLY | O_NONBLOCK)) < 0) {
|
|
result = ECHEC;
|
|
state = RESULT;
|
|
if (dp.verbose)
|
|
perror("Open read only:");
|
|
continue;
|
|
}
|
|
P("fd=%d\n", ftest);
|
|
if (write(ftest, phraseTest, len) < len)
|
|
result = ECHEC;
|
|
else
|
|
result = SUCCES;
|
|
state = RESULT;
|
|
continue;
|
|
|
|
case LOCK:
|
|
|
|
case READLOCK:
|
|
/* Try to read a read-locked section */
|
|
P("READ LOCK %d\n", F_RDLCK);
|
|
if ((ftest = open(dp.fname, O_RDONLY | O_NONBLOCK)) < 0) {
|
|
result = ECHEC;
|
|
state = RESULT;
|
|
if (dp.verbose)
|
|
perror("Open read-write:");
|
|
continue;
|
|
}
|
|
|
|
P("fd=%d\n", ftest);
|
|
/* 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");
|
|
result = ECHEC;
|
|
} else
|
|
result = SUCCES;
|
|
state = RESULT;
|
|
continue;
|
|
|
|
case WRITELOCK:
|
|
/* Try to write a file */
|
|
P("WRITE LOCK %d\n", F_WRLCK);
|
|
if ((ftest = open(dp.fname, O_WRONLY | O_NONBLOCK)) < 0) {
|
|
result = ECHEC;
|
|
state = RESULT;
|
|
if (dp.verbose)
|
|
perror("\nOpen:");
|
|
continue;
|
|
}
|
|
/* 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");
|
|
result = ECHEC;
|
|
} else
|
|
result = SUCCES;
|
|
state = RESULT;
|
|
continue;
|
|
|
|
case BYTELOCK_READ:
|
|
P("BYTE LOCK READ: %d\n", state);
|
|
state = BYTELOCK;
|
|
continue;
|
|
|
|
case BYTELOCK_WRITE:
|
|
P("BYTE LOCK WRITE: %d\n", state);
|
|
state = BYTELOCK;
|
|
continue;
|
|
|
|
case BYTELOCK:
|
|
/* Wait for the exact section to lock. The whole file is sent by the master */
|
|
P("BYTE LOCK %d\n", state);
|
|
getLockSection(&request);
|
|
if ((ftest = open(dp.fname, O_RDWR | O_NONBLOCK)) < 0) {
|
|
result = ECHEC;
|
|
state = RESULT;
|
|
if (dp.verbose)
|
|
perror("\nOpen:");
|
|
continue;
|
|
}
|
|
|
|
if (fcntl(ftest, F_SETLK, &request) < 0) {
|
|
if (dp.verbose || errno != EAGAIN)
|
|
perror("\nBTLOCK: fcntl");
|
|
result = ECHEC;
|
|
state = RESULT;
|
|
continue;
|
|
}
|
|
/* Change the character at the given position for an easier verification */
|
|
a = lseek(ftest, request.l_start, SEEK_SET);
|
|
write(ftest, "=", 1);
|
|
result = SUCCES;
|
|
state = RESULT;
|
|
continue;
|
|
|
|
case RESULT:
|
|
if (result == SUCCES)
|
|
write(0, "=", 1);
|
|
else
|
|
write(0, "x", 1);
|
|
P("RESULT: %d\n", result);
|
|
sendResult(result);
|
|
state = SYNC;
|
|
continue;
|
|
|
|
case CLEAN:
|
|
close(ftest);
|
|
/* Send CLEAN Ack */
|
|
sendResult(result);
|
|
state = SYNC;
|
|
continue;
|
|
|
|
case END:
|
|
E("(End)\n");
|
|
finish(0);
|
|
printf("Unknown command\n");
|
|
finish(1);
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
char *nextArg(int argc, char **argv, int *i)
|
|
{
|
|
if (((*i) + 1) < argc) {
|
|
(*i) += 1;
|
|
return argv[(*i)];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int usage(void)
|
|
{
|
|
printf("locktest -n <number of process> -f <test file> [-T]\n");
|
|
printf("Number of child process must be higher than 1\n");
|
|
exit(0);
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int rc = 0;
|
|
int i, nThread = 0;
|
|
char *tmp;
|
|
int type = 0;
|
|
int clients;
|
|
type = PROCESS;
|
|
dp.fname = NULL;
|
|
dp.verbose = 0;
|
|
int server = 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();
|
|
server = 0;
|
|
continue;
|
|
}
|
|
printf("Ignoring unknown option: %s\n", argv[i]);
|
|
}
|
|
|
|
if (server) {
|
|
if (!(dp.fname && nThread))
|
|
usage();
|
|
if (clients > 0) {
|
|
configureServer(clients);
|
|
setupClients(type, dp.fname, nThread);
|
|
}
|
|
} else {
|
|
configureClient(host);
|
|
dp.fname = malloc(512);
|
|
memset(dp.fname, 0, 512);
|
|
getConfiguration(&type, dp.fname, &nThread);
|
|
}
|
|
|
|
if (dp.verbose)
|
|
printf("By process.\n");
|
|
load = loadProcess;
|
|
eType = "process";
|
|
finish = finishProcess;
|
|
LIST_RESULTS = LIST_RESULTS_PROCESS;
|
|
initialize(nThread);
|
|
if (server) {
|
|
rc = master();
|
|
} else {
|
|
masterClient();
|
|
free(dp.fname);
|
|
}
|
|
clean();
|
|
|
|
return rc;
|
|
}
|