#include <stdio.h> #include <fcntl.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <signal.h> #include <errno.h> #include <unistd.h> #include <string.h> #include <dirent.h> #include <sys/wait.h> #include <time.h> #include <unistd.h>#define NUMSERVER 4 /* numero di server partecipanti */ #define TIMEOUT 1800 /* time out per la fase di aggiornamento */ #define ATTESA_MAX 180 /* time out per select */ #define max_tent 2 /* cicli di messaggi datagram nella fase finale */ char server_name[NUMSERVER][80]; /*nomi dei server partecipanti */ int agg = 0; /* agg=1 fase di aggiornamento attiva */ int rich_guasti[NUMSERVER]; /* stato dei server */ int sped_guasti[NUMSERVER]; char final_vet[NUMSERVER + 1]; int pid[NUMSERVER], pid2[NUMSERVER]; /* int o pid_t */ int cont_sped = 0, cont_ric = 0; int my_number; /* Informazioni sul server locale */ char *my_name; char nome_file_tgz[80]; /* sock_gets, sock_write, sock_puts, sock_read, e' codice public domain scritto da Vic Metcalfe (vic@brutus.tlug.org); Ho apportato alcune modifiche */ /* This function reads from a socket, until it recieves a linefeed character. It fills the buffer "str" up to the maximum size "count". This function will return -1 if the socket is closed during the read operation. Note that if a single line exceeds the length of count, the extra data will be read and discarded! You have been warned. */ int sock_gets (sockfd, str, count) int sockfd; char *str; size_t count; { int bytes_read; int total_count = 0; char *current_position; char last_read = 0; current_position = str; while (last_read != 10) { bytes_read = read (sockfd, &last_read, 1); if (bytes_read <= 0) { /* The other side may have closed unexpectedly */ /* Is this effective on other platforms than linux? */ return -1; } if ((total_count < count) && (last_read != 10) && (last_read != 13)) { current_position[0] = last_read; current_position++; total_count++; } } if (count > 0) current_position[0] = 0; /* Riconoscimento riga vuota con \n */ if (current_position == str) return 1; else return total_count; } /* This is just like the write() system call, accept that it will make sure that all data is transmitted. */ int sock_write (sockfd, buf, count) int sockfd; char *buf; size_t count; { size_t bytes_sent = 0; int this_write; while (bytes_sent < count) { do { this_write = write (sockfd, buf, count - bytes_sent); if ((this_write) < 0) perror ("write"); } while ((this_write < 0) && (errno == EINTR)); if (this_write <= 0) return this_write; bytes_sent += this_write; buf += this_write; } return count; } /* This function writes a character string out to a socket. It will return -1 if the connection is closed while it is trying to write. */ int sock_puts (sockfd, str) int sockfd; char *str; { return sock_write (sockfd, str, strlen (str)); } /* This is just like the read() system call, accept that it will make sure that all your data goes through the socket. */ int sock_read (sockfd, buf, count) int sockfd; char *buf; size_t count; { size_t bytes_read = 0; int this_read; while (bytes_read < count) { do this_read = read (sockfd, buf, count - bytes_read); while ((this_read < 0) && (errno == EINTR)); if (this_read < 0) return this_read; else if (this_read == 0) return bytes_read; bytes_read += this_read; buf += this_read; } return count; } /* Legge da un file i nomi dei server partecipanti.I nomi dei server sono scritti nel file server.txt */ int read_name (char *file_name) { char *buf; int i = 0, j = 0, fd; if ((fd = open (file_name, O_RDONLY)) < 0) { printf ("Errore apertura file %s \n", file_name); exit (1); } while ((j < NUMSERVER) && (read (fd, buf, 1) > 0)) { switch (*buf) { case '\n': server_name[j][i] = '\0'; /* posizione del server locale nel file server.txt */ if (strcmp (my_name, server_name[j]) == 0) my_number = j; j++; i = 0; default: if ((*buf) != ('\n')) server_name[j][i++] = *buf; } } close (fd); } /* Instaura una connessione TCP con il server "nomeremoto" per richiedere il file tgz dei nuovi messaggi */ int ricevi_file (char *nomeremoto) { int fromlen, sd, fd, j; char nome_file[80]; char buf[256]; int count = 256; struct hostent *host; struct sockaddr_in rem1_indirizzo; memset ((char *) &rem1_indirizzo, 0, sizeof (struct sockaddr_in)); rem1_indirizzo.sin_family = AF_INET; host = gethostbyname (nomeremoto); if (host == NULL) { printf ("%s not found in /etc/hosts \n", nomeremoto); exit (1); } rem1_indirizzo.sin_addr.s_addr = ((struct in_addr *) (host->h_addr))->s_addr; rem1_indirizzo.sin_port = 3001; sd = socket (AF_INET, SOCK_STREAM, 0); if (sd < 0) { perror ("Apertura socket"); exit (1); } if (connect (sd, (struct sockaddr *) &rem1_indirizzo, sizeof (struct sockaddr)) < 0) { perror ("errore in connect"); return -1; } sprintf (nome_file, "%s%s", nomeremoto, ".tgz"); if ((fd = open (nome_file, O_CREAT | O_EXCL)) < 0) { printf ("File %s error", nome_file); exit (1); } while ((sock_read (sd, buf, count)) == count) { for (j = 0; j < count; j++) { if (buf[j] != EOF) write (fd, buf + j, 1); else { close (fd); close (sd); return (1); } } } close (fd); close (sd); return 0; } /* Compatta i nuovi messaggi ricevuti dal server locale in un file tgz (che verra' poi spedito ai server richiedenti) */ void crea_file (char *nome_file) { int stato; switch (fork ()) { case -1: perror ("fork"); exit (1); case 0: chdir ("dir_nuovi_messaggi"); sprintf (nome_file_tgz, "%s%s%s", "/temp/", nome_file, ".tgz"); /* Sposta i nuovi messaggi in un file compresso */ execl ("/bin/tar", "tar", "-c", "--compress", "--remove-files", "--file", nome_file_tgz, "*.*", (char *) 0); default: wait (&stato); chdir (".."); } } /* Spedisce un file sulla socket "socket" */ int spedisci_file (char *nomefile, int socket, int id) { int fd, nwrite; int count; char buf[256]; fd = open (nomefile, O_RDONLY); /* da sistemare */ if (fd < 0) { printf ("File errore"); return (-1); } /* se il file in questione e' quello dei nuovi messaggi, che deve essere spedito agli altri server, la dimensione concordata di buf e' 256 altrimenti se il file e' un post da spedire ad un client assumo la dimesione di buf uguale a 1 */ if (id == 0) count = 256; else count = 1; while ((read (fd, buf, count)) > 0) { nwrite = sock_write (socket, buf, count); if (nwrite != count) { close (fd); return -1; } } close (fd); return 1; } /* Spedisce sulla socket "socket" il contenuto della directory "name". (per segnalare ad un client i messaggi disponibili) */ int read_dir (int socket, char *name) { int num_file = 0; int k = 1; char nome_post[80]; DIR *dir; struct dirent *dd; dir = opendir (name); while (((dd = readdir (dir)) != NULL) && (k > 0)) { if ((strcmp (".", dd->d_name) != 0) && (strcmp ("..", dd->d_name) != 0)) { sprintf (nome_post, "%s%s", dd->d_name, "\n"); k = sock_puts (socket, nome_post); num_file++; } } closedir (dir); if (k > 0) return (num_file); else return (k); } /* per il vettore rich_guasti 1:OK 0:guasto */ /* per il vettore sped_guasti 1:OK 0:guasto 2:file in ricezione */ void gestore_sped (int signo) /* gestione spedizione dei dati agli altri server */ { int stato, pid_child, j; pid_child = wait (&stato); cont_sped++; j = 0; while (pid[j] != pid_child) j++; stato = (((stato) & 0xff00) >> 8); sped_guasti[j] = stato; } void gestore_ric (int signo) /* gestione richiesta dei dati agli altri server */ { int stato, pid_child, j; pid_child = wait (&stato); cont_ric++; j = 0; while (pid[j] != pid_child) j++; stato = (((stato) & 0xff00) >> 8); rich_guasti[j] = stato; if (stato == 0) { sped_guasti[j] = 0; cont_sped++; } if (cont_ric == (NUMSERVER)) fase_finale (); } void add_new_mex (char *nomefile) { int stato; switch (fork ()) { case -1: perror ("fork"); exit (1); case 0: chdir ("ng_x"); sprintf (nome_file_tgz, "%s%s%s", "/temp/", nomefile, ".tgz"); execl ("/bin/tar", "tar", "-x", nome_file_tgz, (char *) 0); default: wait (&stato); unlink (nome_file_tgz); chdir (".."); } } int fase_finale () { struct sockaddr_in mio_indirizzo, rem_indirizzo; struct hostent *host; int fs, attendi, cont, i, j, k, tent; char final_vet2[NUMSERVER]; struct timeval { unsigned long tv_sec; long tv_usec; } tv; int retval; fd_set rfds; memset ((char *) &mio_indirizzo, 0, sizeof (struct sockaddr_in)); mio_indirizzo.sin_family = AF_INET; mio_indirizzo.sin_port = htons (31337); fs = socket (AF_INET, SOCK_DGRAM, 0); if (bind (fs, (struct sockaddr *) &mio_indirizzo, sizeof (struct sockaddr_in)) < 0) { perror ("bind"); exit (1); } attendi = 0; do { for (i = 0; i < NUMSERVER; i++) if (sped_guasti[i] == 2) attendi = 1; } while (attendi == 1); /* final_vet[i]=0 il server non ha ricevuto dati dalla macchina i e la macchina i non ha richiesto dati final_vet[i]=1 il server non ha ricevuto dati dalla macchina i ma la macchina i ha richiesto dati final_vet[i]=2 il server ha ricevuto dati dalla macchina i e la macchina i non ha richiesto dati final_vet[i]=3 il server ha ricevuto dati dalla macchina i e la macchina i ha richiesto dati */ for (i = 0; i < NUMSERVER; i++) { switch (rich_guasti[i]) { case 0: if (sped_guasti[i] == 0) final_vet[i] = '0'; else final_vet[i] = '1'; case 1: if (sped_guasti[i] == 0) final_vet[i] = '2'; else final_vet[i] = '3'; } } final_vet[i] = '\0'; rem_indirizzo.sin_port = 31337; for (tent = 0; tent < max_tent; tent++) { for (j = 0; j < NUMSERVER; j++) { if (j != my_number) { host = gethostbyname (server_name[j]); rem_indirizzo.sin_addr.s_addr = ((struct in_addr *) (host->h_addr))->s_addr; if (sendto (fs, final_vet, NUMSERVER + 1, 0, &rem_indirizzo, sizeof (struct sockaddr_in)) == -1) { fprintf (stderr, "Errore nell'invio datagram"); } } } for (j = 0; j < (NUMSERVER - 1); j++) { FD_ZERO (&rfds); FD_SET (fs, &rfds); tv.tv_sec = ATTESA_MAX; tv.tv_usec = 0; retval = select (fs + 1, &rfds, NULL, NULL, &tv); if (retval) { if (recvfrom (fs, final_vet2, NUMSERVER + 1, 0, &rem_indirizzo, sizeof (struct sockaddr_in)) < 0) { perror ("recvfrom"); exit (1); } else { for (k = 0; k < NUMSERVER; k++) { if ((final_vet[i] == '3') && (final_vet2[i] == '3')) final_vet[i] == '3'; else final_vet[i] = '0'; } } } for (i = 0; i < NUMSERVER; i++) { if (final_vet[i] == '3') add_new_mex (server_name[i]); } } } } /* Crea NUMSERVER-1 figli incaricati di richiedere i file dei nuovi messaggi dai server remoti */ void richiedi_dati () { int i; signal (SIGCHLD, gestore_ric); for (i = 0; i < NUMSERVER; i++) { if (i != my_number) { switch (pid[i] = fork ()) { case -1: perror ("fork"); exit (1); case 0: if ((ricevi_file (server_name[i])) > 0) exit (1); else exit (0); default: /* continua con il ciclo */ } } } } /* Crea un demone che attende sulla porta 3000 le connessioni dai client */ void demone_client () { int sd1, ns1, prior, id_client, fromlen, connected = 1; long timevar; char *hostname; char buffer[1024]; char nomepost[80]; int fd_post; struct sockaddr_in myaddr_in; /* local socket address */ struct sockaddr_in peeraddr_in; /* remote socket address */ struct hostent *hp; memset ((char *) &peeraddr_in, 0, sizeof (struct sockaddr_in)); memset ((char *) &myaddr_in, 0, sizeof (struct sockaddr_in)); myaddr_in.sin_family = AF_INET; myaddr_in.sin_port = htons (3000); /* definizione della porta */ fromlen = sizeof (struct sockaddr_in); /* Wildcard Address... */ myaddr_in.sin_addr.s_addr = INADDR_ANY; /* Socket d'ascolto */ sd1 = socket (AF_INET, SOCK_STREAM, 0); if (sd1 < 0) { perror ("Apertura socket"); exit (1); } /* Collegamento della socket all'indirizzo */ if (bind (sd1, (struct sockaddr *) &myaddr_in, sizeof (struct sockaddr_in)) < 0) { perror ("bind"); exit (1); } /* Coda d'ascolto */ if (listen (sd1, 5) == -1) { perror ("listen"); exit (1); } signal (SIGCHLD, SIG_IGN); switch (fork ()) { case -1: perror ("fork"); exit (1); case 0: /* figlio - demone */ setsid (); printf ("Demone client \n"); signal (SIGCHLD, SIG_IGN); for (;;) { ns1 = accept (sd1, &peeraddr_in, &fromlen); if (ns1 < 0) { if (errno == EINTR) { perror ("accept, continuo"); continue; } else exit (1); } switch (fork ()) { case -1: perror ("fork"); exit (1); case 0: /* esecuzione server */ close (sd1); if (sock_puts (ns1, "Guarapito's little news server ready - posting OK.\n") == -1) { connected = 0; exit (2); } else connected = 1; while (connected) { /* Legge input dal client. Considero comandi scritti in minuscolo (eventualmente si puo' aggiungere una funzione di conversione */ if (sock_gets (ns1, buffer, 1024) < 0) { connected = 0; exit (2); } else if (strcmp ("quit", buffer) == 0) { sock_puts (ns1, "Goodbye \n"); close (ns1); connected = 0; exit (2); } else if (strcmp ("list", buffer) == 0) { /* Da implementare. Si considera una solo NG e quindi un'unica directory */ exit (2); } else if (strcmp ("group", buffer) == 0) { /* Invio dei nomi dei post */ if ((read_dir (ns1, "/guarapito/ng_x")) < 0) { connected = 0; exit (2); } } else if (strcmp ("post", buffer) == 0) { chdir ("dir_nuovi_messaggi"); /* Ricezione nuovo articolo */ do { /* Creazione nome del post */ time (&timevar); sprintf (nomepost, "%d%s", timevar, my_name); fd_post = creat (nomepost, 0644); } while (fd_post < 0); /* per cambiare nome */ sock_puts (ns1, "punto per fine file \n"); connected = sock_gets (ns1, buffer, 1024); while ((connected) && ((strcmp (".", buffer)) != 0)) { strcat (buffer, "\n"); write (fd_post, buffer, strlen (buffer)); connected = sock_gets (ns1, buffer, 1024); } close (fd_post); chdir (".."); } else if (strncmp ("article", buffer, 7) == 0) { /* Invio body del messaggio selezionato */ chdir ("ng_x"); connected = spedisci_file (buffer + 8, ns1, 1); chdir (".."); if (connected <= 0) exit (2); } } exit (0); default: close (ns1); } } default: /* processo iniziale */ } } /* Fa partire, allo scadere del TIMEOUT, la fase coordinata di aggiornamento dati */ void richiedi_coord () { if (agg != 1) { agg = 1; richiedi_dati (); } } main (int argc, char **argv) { int sd, ns, prior, id_client, i, fromlen, connected = 1; char *hostname; struct sockaddr_in myaddr_in; /* local socket address */ struct sockaddr_in peeraddr_in; /* remote socket address */ struct hostent *hp, *myhost; memset ((char *) &peeraddr_in, 0, sizeof (struct sockaddr_in)); memset ((char *) &myaddr_in, 0, sizeof (struct sockaddr_in)); myaddr_in.sin_family = AF_INET; myaddr_in.sin_port = htons (3001); /* definizione della porta */ fromlen = sizeof (struct sockaddr_in); /* Wildcard Address... */ myaddr_in.sin_addr.s_addr = INADDR_ANY; /* Socket d'ascolto */ myhost = gethostbyname ("localhost"); my_name = myhost->h_name; printf ("Il programma gira sul server: %s \n", my_name); rich_guasti[my_number] = 1; sped_guasti[my_number] = 1; pid[my_number] = -1; pid2[my_number] = -1; read_name ("server.txt"); sd = socket (AF_INET, SOCK_STREAM, 0); if (sd < 0) { perror ("Apertura socket"); exit (1); } /* Collegamento della socket all'indirizzo */ if (bind (sd, (struct sockaddr *) &myaddr_in, sizeof (struct sockaddr_in)) < 0) { perror ("bind"); exit (1); } /* Coda d'ascolto */ if (listen (sd, 5) == -1) { perror ("listen"); exit (1); } switch (fork ()) { case -1: perror ("fork"); exit (1); case 0: /* figlio - demone */ setsid (); for (;;) { signal (SIGALRM, richiedi_coord); alarm (TIMEOUT); if ((ns = accept (sd, &peeraddr_in, &fromlen)) < 0) { if (errno == EINTR) { perror ("accept, continuo"); continue; } else exit (1); } /* Rilevazione nome host remoto */ hp = gethostbyaddr ((char *) &peeraddr_in.sin_addr, sizeof (peeraddr_in.sin_addr), peeraddr_in.sin_family); if (hp == NULL) hostname = inet_ntoa (peeraddr_in.sin_addr); else hostname = hp->h_name; /* Rilevazione posizione dell host remoto nel file server.txt */ prior = 0; id_client = 1; while (((id_client = strcmp (server_name[prior], hostname)) != 0) && (prior < NUMSERVER)) prior++; if (id_client != 1) { if (agg == 0) { agg = 1; crea_file (my_name); for (i = 0; i < NUMSERVER; i++) { if (i != my_number) { sped_guasti[i] = 0; rich_guasti[i] = 0; } } cont_sped = 1; cont_ric = 1; signal (SIGCHLD, gestore_ric); richiedi_dati (); } signal (SIGCHLD, gestore_sped); sped_guasti[prior] = 2; switch (pid2[prior] = fork ()) { case -1: perror ("fork"); exit (1); case 0: /* esecuzione server */ close (sd); chdir ("\temp"); if ((spedisci_file (nome_file_tgz, ns, 0)) > 0) { chdir (".."); close (ns); exit (1); } else { chdir (".."); close (ns); exit (0); } default: close (ns); } } else /* accesso non autorizzato */ close (ns); } default: /* processo iniziale */ demone_client (); printf ("Processo iniziale fine \n"); exit (0); } }