===== Esempio di programma client/server che utilizza pipe con nome (FIFO) ===== Client e server comunicano con una pipe FIFO. Il server riceve i comandi da eseguire su una FIFO "pubblica" e restituisce le risposte su una FIFO privata del client il cui nome รจ stato inviato insieme al messaggio di richiesta. Il processo server "forka" un processo figlio per eseguire il comando richiesto dal client redirigendo lo standard output per ottenere il risultato. ==== msg.h ==== #if !defined(MSG_H) #define MSG_H #include /* Linux specific definisce PIPE_BUF */ #define F_SIZE 256 /* size massima del nome della FIFO del client */ #define B_SIZE (PIPE_BUF-F_SIZE) /* size massima dei dati scambiati in modo atomico */ #define SYSCALL(r,c,e) \ if((r=c)==-1) { perror(e);exit(errno); } // FIFO pubblica per inviare le richieste al server const char *PUBLIC = "/tmp/PUBLIC"; /* * In un singolo messaggio vogliamo inviare sia il comando da eseguire * che il nome della FIFO su cui vogliamo ottenere il risultato. */ struct message { char fifo_name[F_SIZE]; char cmd_line[B_SIZE]; }; #endif /* MSG_H */ ==== client.c ==== #include #include #include #include #include #include #include #include #include "msg.h" // nome della FIFO privata char fifo_name[F_SIZE]; void cleanup() { int n; SYSCALL(n, unlink(fifo_name), "cleanup: unlink private fifo"); } int main() { static char buffer[PIPE_BUF]; struct message msg; int publicfifo, privatefifo, n; // fifo privata per ricevere le risposte dal server snprintf(fifo_name, F_SIZE, "/tmp/fifo%d", getpid()); SYSCALL(n, mkfifo(fifo_name, 0666), "mkfifo"); // qualora qualcosa vada storto if (atexit(cleanup) != 0) { fprintf(stderr, "ERRORE in atexit\n"); exit(EXIT_FAILURE); } // apro la FIFO pubblica in sola scrittura SYSCALL(publicfifo, open(PUBLIC,O_WRONLY), "open public fifo"); while(1) { SYSCALL(n,write(1, "\n cmd>", 6), "write 1"); // resetto il messaggio memset(msg.fifo_name, 0x0, F_SIZE); memset(msg.cmd_line, 0x0, B_SIZE); // leggo il comando da inviare al server SYSCALL(n,read(0, msg.cmd_line, B_SIZE), "read 0"); // devo uscire ? if(strncmp("quit", msg.cmd_line, n-1) == 0) break; strncpy(msg.fifo_name,fifo_name, strlen(fifo_name)+1); // mando la richiesta al server SYSCALL(n, write(publicfifo, &msg, sizeof(msg)), "write public fifo"); // apro la FIFO privata in sola lettura aspettando che il server la apra in scrittura SYSCALL(privatefifo, open(msg.fifo_name, O_RDONLY), "open private fifo"); do { memset(buffer, 0x0, PIPE_BUF); SYSCALL(n ,read(privatefifo, buffer, PIPE_BUF), "read private fifo"); fprintf(stdout, "%s", buffer); } while(n>0); SYSCALL(n, close(privatefifo), "close private fifo"); } SYSCALL(n, close(publicfifo), "close public fifo"); return 0; } ==== server.c ==== #include #include #include #include #include "msg.h" #define MAXARGS 100 // massimo numero di argomenti di un comando // costruisce un vettore di argomenti da una stringa void buildargs(char *line, char * args[]) { int i = 0; args[i++] = strtok(line," \n"); do { args[i] = strtok(NULL," \n"); } while(args[i++]!=NULL); } int main() { struct message msg; static char buffer[PIPE_BUF]; /* creo la FIFO pubblica */ if ((mkfifo(PUBLIC, 0666) == -1) && errno!=EEXIST) { perror("mkfifo public fifo"); exit(errno); } int publicfifo; // apertura in sola lettura per ricevere i comandi dai clients, aspetto che // almeno uno dei client apra la FIFO in scrittura SYSCALL(publicfifo, open(PUBLIC, O_RDONLY), "open public fifo (read)"); // apro la FIFO in scrittura per evitare di ricevere EOF sulla FIFO pubblica // che rimane sempre aperta int notused; SYSCALL(notused, open(PUBLIC, O_WRONLY|O_NONBLOCK), "open public fifo (write)"); while(1) { int n, done; SYSCALL(n, read(publicfifo, &msg, sizeof(msg)), "read public fifo"); if (n == 0) break; // ho letto End-Of-File (EOF), esco n=0, done=0; do { int privatefifo; // attendo che il client si connetta riprovando un po' di volte if ((privatefifo = open(msg.fifo_name, O_WRONLY|O_NONBLOCK)) == -1) { sleep(2); } else { int channel[2]; int r; SYSCALL(r, pipe(channel), "pipe"); // creo una pipe senza nome char *args[MAXARGS]; buildargs(msg.cmd_line, args); if (fork() == 0) { // figlio SYSCALL(r, close(channel[0]), "close reader"); SYSCALL(r, dup2(channel[1],1), "dup2 writer"); SYSCALL(r, close(channel[1]), "close writer"); execvp(args[0], &args[0]); fprintf(stderr, "execvp fallita"); exit(EXIT_FAILURE); } // padre SYSCALL(r, close(channel[1]), "close writer"); SYSCALL(r, write(privatefifo,"\n",1), "write private fifo"); do { SYSCALL(n, read(channel[0], buffer, PIPE_BUF), "read channel[0]"); SYSCALL(r, write(privatefifo,buffer,n), "write private fifo"); memset(buffer, 0x0, PIPE_BUF); } while(n>0); SYSCALL(r, close(channel[0]), "close reader"); SYSCALL(r, close(privatefifo), "close private fifo"); // invio EOF done = 1; } }while(!done && n++ < 10); if (!done) { fprintf(stderr, "Il client non ha inviato il comando, condizione di fallimento\n"); exit(EXIT_FAILURE); } } return 0; }