#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include #include #define UNIX_PATH_MAX 256 #define MAXARGLENGTH 256 static char globalSocket[UNIX_PATH_MAX] = ""; // struttura della lista dei comandi typedef struct cmd_s { char name; // nome del comando char *arg; // (eventuale) argomento del comando struct cmd_s *next; // puntatore al prossimo comando nella lista } cmd_t; // ----------------------------------------------------------------------------- // helper functions // libera la memoria della lista dei comandi void destroyCommandList(cmd_t *l); // aggiunge un comando alla lista int addCommand(cmd_t **l, char cmd, char *arg); // esegue tutti i comandi nella lista int execute(cmd_t *l, int print); // per chiudere la connessione prima dell'uscita void cleanup() { if (strncmp(globalSocket, "", 2) != 0) { closeConnection(globalSocket); strncpy(globalSocket, "", 2); } } // compare files int compare(const FTSENT ** first, const FTSENT ** second) { return (strcmp((*first)->fts_name, (*second)->fts_name)); } // -h static void usage(const char *argv0) { // TODO change this printf("Uso: %s\n", argv0); printf("-h: stampa il presente messaggio d'aiuto.\n"); printf("-f filename: connettiti al socket AF_UNIX 'filename'.\n"); printf("-w dirname[,n=0]: invia al server 'n' file nella cartella 'dirname'. Se n=0 o non e' specificato, tenta di inviare tutti i file al server.\n"); printf("-W file1[,file2]: scrivi sul server una lista di file, separati da virgole.\n"); printf("-D dirname: specifica la cartella dove scrivere i file espulsi dal server in seguito a capacity misses.\n"); printf("-r file1[,file2]: leggi dal server una lista di nomi di file, separati da virgole.\n"); printf("-R [n=0]: leggi dal server 'n' file qualsiasi. Se n=0 o non e' specificato, leggi tutti i file presenti nel server per i quali si hanno i permessi necessari.\n"); printf("-d dirname: cartella dove scrivere i file letti dal server con i comandi '-r' o '-R'.\n"); printf("-t time: se specificato, fra le richieste successive al server vi sara' un'attesa di 'time' millisecondi.\n"); printf("-l file1[,file2]: acquisisci la mutua esclusione su una lista di file, separati da virgole.\n"); printf("-u file1[,file2]: rilascia la mutua esclusione su una lista di file, separati da virgole.\n"); printf("-c file1[,file2]: rimuovi dal server una lista di file (se presenti), separati da virgole.\n"); printf("-p: stampa sullo standard output le informazioni riguardo ogni operazione effettuata.\n"); } // -f int cmd_f(char *socket); // -w int cmd_w(char *dirname, char *Dir, int print); // -W int cmd_W(char *filelist, char *Dir, int print); // -r int cmd_r(char *filelist, char *dir, int print); // -R int cmd_R(char *numStr, char *dir, int print); // -l int cmd_l(char *filelist, int print); // -u int cmd_u(char *filelist, int print); // -c int cmd_c(char *filelist, int print); // ----------------------------------------------------------------------------- // MAIN int main(int argc, char* argv[]) { atexit(cleanup); if(argc <= 1) { // no arguments => -h usage(argv[0]); return 0; } struct sigaction siga; // ignoro il segnale SIGPIPE memset(&siga, 0, sizeof(siga)); siga.sa_handler = SIG_IGN; if (sigaction(SIGPIPE, &siga, NULL) == -1) { perror("sigaction.\n"); return 1; } // lista dei comandi cmd_t *cmds = NULL; int opt = 0; char args[MAXARGLENGTH]; int print = 0; char f = 0; // add args to list while ((opt = getopt(argc, argv, ":hpf:t:w:W:D:r:R:d:l:u:c:")) != -1) { switch (opt) { case 'h': // help message usage(argv[0]); goto _cleanup; case 'f': // socket name if(!f) { if (optarg && strnlen(optarg, MAXARGLENGTH) > 0 && optarg[0] == '-') { fprintf(stderr, "Il comando -f necessita di un argomento.\n"); goto _cleanup; } memset(args, 0, MAXARGLENGTH); strncpy(args, optarg, strnlen(optarg, MAXARGLENGTH-1)+1); addCommand(&cmds, opt, args); ++f; } break; case 'p': // print to stdout printInfo(1, stdout); ++print; break; case 'w': // send files from folder (n is specified after) if (optarg && strnlen(optarg, MAXARGLENGTH) > 0 && optarg[0] == '-') { fprintf(stderr, "Il comando -w necessita di un argomento.\n"); goto _cleanup; } memset(args, 0, MAXARGLENGTH); strncpy(args, optarg, strnlen(optarg, MAXARGLENGTH-1)+1); addCommand(&cmds, opt, args); break; case 'W': // files to send separated by ',' case 'D': // directory to store recived files case 'r': // files to read from server separated by ',' case 'd': // directory to store read files case 't': // time in ms between requests case 'l': // file to request lock of separated by ',' case 'u': // file to relese lock of separated by ',' case 'c': // files to remove separated by ',' if (optarg && strnlen(optarg, MAXARGLENGTH) > 0 && optarg[0] == '-') { fprintf(stderr, "Il comando -%c necessita di un argomento.\n", optopt); goto _cleanup; } memset(args, 0, MAXARGLENGTH); strncpy(args, optarg, strnlen(optarg, MAXARGLENGTH-1)+1); addCommand(&cmds, opt, args); break; case 'R': // read n random files if (optarg && strnlen(optarg, MAXARGLENGTH) > 0 && optarg[0] == '-') { optind -= 1; addCommand(&cmds, opt, NULL); break; } memset(args, 0, MAXARGLENGTH); strncpy(args, optarg, strnlen(optarg, MAXARGLENGTH-1)+1); addCommand(&cmds, opt, args); break; case ':': // command with no argument (:: is a GNU extension) switch(optopt) { case 'R': addCommand(&cmds, opt, NULL); break; default: fprintf(stderr, "Il comando -%c necessita di un argomento.\n", optopt); goto _cleanup; } break; case '?': // unknown default: // unknown fprintf(stderr, "Comando non riconosciuto: -%c.\n", optopt); usage(argv[0]); goto _cleanup; } } if(execute(cmds, print) < 0) { perror("execute"); goto _cleanup; } if(cmds) { destroyCommandList(cmds); } return 0; _cleanup: if(cmds) { destroyCommandList(cmds); } return 0; } // ----------------------------------------------------------------------------- void destroyCommandList(cmd_t *l) { if (!l) { errno = EINVAL; return; } cmd_t *tmp; // scorro tutta la lista e libero la memoria while (l) { tmp = l; l = l->next; free(tmp->arg); free(tmp); } } int addCommand(cmd_t **l, char cmd, char *arg) { if(!l) { errno = EINVAL; return -1; } cmd_t *new = calloc(1, sizeof(cmd_t)); if(!new) { perror("calloc"); return -1; } new->name = cmd; if (arg) { new->arg = malloc(MAXARGLENGTH); if (new->arg == NULL) { perror("malloc arg"); free(new); return -1; } strncpy(new->arg, arg, strnlen(arg, MAXARGLENGTH-1)+1); } new->next = NULL; // se lista vuota aggiungo in cima, altrimenti scorro la lista if (*l == NULL) { *l = new; return 0; } cmd_t *tail = *l; while (tail->next) { tail = tail->next; } tail->next = new; return 0; } int execute(cmd_t *l, int print) { if(!l) { errno = EINVAL; return -1; } cmd_t *tmp = l; struct timespec interval; interval.tv_nsec = 0; interval.tv_sec = 0; // loop that serches for -t while(tmp) { switch(tmp->name) { case 't': {// time in ms between requests long num; num = strtol(tmp->arg, NULL, 10); if(num==0 && errno==EINVAL) { errno = EINVAL; perror("Invalid time specified after -t"); return -1; } // milliseconds converted to nanoseconds interval.tv_nsec = (num%1000) * 1000000; // seconds interval.tv_sec = (num/1000); if (print) fprintf(stdout, "t - Tempo fra due richieste: %ld ms\tEsito: ok\n", num); break; } default: break; } tmp = tmp->next; } fflush(stdout); // loop that serches for -f tmp = l; while(tmp) { switch(tmp->name) { case 'f': { int ok = 1; strncpy(globalSocket, tmp->arg, strnlen(tmp->arg, MAXARGLENGTH)+1); if(cmd_f(tmp->arg) != 0) { if (print) perror("-f"); ok = 0; } if (print) { fprintf(stdout, "f - Connessione al socket: %s\tEsito: ", globalSocket); if(ok) printf("ok\n"); if(!ok) printf("errore\n"); } if(!ok) { closeConnection(tmp->arg); return 0; // no socket to connect, nothing to do } // we only read the first -f, no error reported if more than one is // specified tmp = NULL; break; } default: break; } if(tmp) tmp = tmp->next; } fflush(stdout); // loop that checks for consistencies: // 1) -D with no -w or -W after // 2) -d with no -r or -R after tmp = l; int unmachedD = 0; int unmachedd = 0; while(tmp){ switch (tmp->name) { case 'D': if(unmachedD) { fprintf(stdout, "\nError: -D has no -w or -W matching after\n"); return 0; } unmachedD = 1; break; case 'w': case 'W': unmachedD = 0; break; case 'd': if(unmachedd) { fprintf(stdout, "\nError: -d has no -r or -R matching after\n"); return 0; } unmachedd = 1; break; case 'r': case 'R': unmachedd = 0; break; default: break; } tmp = tmp->next; } if(unmachedD) { fprintf(stdout, "\nError: -D has no -w or -W matching after\n"); return 0; } if(unmachedd) { fprintf(stdout, "\nError: -d has no -r or -R matching after\n"); return 0; } fflush(stdout); char *Dir = NULL; // -D folder char *dir = NULL; // -d folder // loop that executes -w, -W, -D; -r, -R, -d; -l, -u, -c tmp = l; while(tmp) { switch (tmp->name) { case 'w': cmd_w(tmp->arg, Dir, print); break; case 'W': cmd_W(tmp->arg, Dir, print); break; case 'D': if(Dir) free(Dir); Dir = calloc(strnlen(tmp->arg, MAXARGLENGTH)+1, sizeof(char)); strncpy(Dir, tmp->arg, strnlen(tmp->arg, MAXARGLENGTH)); if (setDirectory(Dir, 1) == -1) { if(print) perror("-D"); } if (print) printf("\nD - Cartella per le scritture: %s\tEsito: ok\n", Dir); break; case 'r': cmd_r(tmp->arg, dir, print); break; case 'R': cmd_R(tmp->arg, dir, print); break; case 'd': if(dir) free(dir); dir = calloc(strnlen(tmp->arg, MAXARGLENGTH)+1, sizeof(char)); strncpy(dir, tmp->arg, strnlen(tmp->arg, MAXARGLENGTH)); if(setDirectory(dir, 0) == -1) { if(print) perror("-d"); } if(print) printf("\nd - Cartella per le letture: %s\tEsito: ok\n", dir); break; case 'l': cmd_l(tmp->arg, print); break; case 'u': cmd_u(tmp->arg, print); break; case 'c': cmd_c(tmp->arg, print); break; default: break; } // maybe better: while(nanosleep(&interval, &interval)); nanosleep(&interval, NULL); tmp = tmp->next; } if(print) printf("\n"); if(dir) free(dir); if(Dir) free(Dir); cleanup(); return 0; } // ----------------------------------------------------------------------------- // funzioni relative ai comandi int cmd_f(char *socket) { if(!socket) { errno = EINVAL; return -1; } struct timespec ts; ts.tv_sec = 2; ts.tv_nsec = 0; if (openConnection(socket, 100, ts) != 0) return -1; return 0; } int cmd_w(char *dirname, char *Dir, int print) { if(!dirname) { errno = EINVAL; return -1; } int num; // we copy dirname because we are nice char *tofree = calloc(strnlen(dirname, MAXARGLENGTH)+1, sizeof(char)); if(!tofree) { perror("malloc"); return -1; } strncpy(tofree, dirname, strnlen(dirname, MAXARGLENGTH)); char *firstArg; char *secondArg = tofree; firstArg = strsep_gnu(&secondArg, ","); // secondArg has the number of files if (!secondArg) { num = -1; } else if (secondArg[0] == 'n' && secondArg[1] == '=') { char *number = &secondArg[2]; for (int i = 0; i < strlen(number); ++i) { if (!isdigit(number[i])) { free(tofree); errno = EINVAL; return -1; } } num = (int) strtol(number, NULL, 10); num = (num==0)?-1:num; } else { free(tofree); errno = EINVAL; return -1; } if (print) { printf("\nw - Scrivo i seguenti file sul server: \n"); fflush(stdout); } // we use fts to traverse all files in the directory recursively FTS *fhandle = NULL; FTSENT *child = NULL; FTSENT *parent = NULL; fhandle = fts_open(&firstArg, FTS_COMFOLLOW, &compare); if(fhandle != NULL) { // we check for num == 0 so that -1 yields all files in folder while (num!=0 && (parent = fts_read(fhandle)) != NULL) { child = fts_children(fhandle, 0); while (num!=0 && (child != NULL)) { // for all children in folder if(child->fts_info == FTS_F) { // if child is a file char *tmp = malloc(child->fts_namelen + child->fts_pathlen + 2); snprintf(tmp, child->fts_namelen + child->fts_pathlen + 2, "%s/%s", child->fts_path, child->fts_name); if(print) { printf("%s [", tmp); } // we send the file with the other function but set print to // 0 since we do the printing before int r = cmd_W(tmp, Dir, 0); if(print && !r) { printf("Esito: ok"); } else if (print && r) { printf("Esito: errore"); } if(print) { printf("]\n"); } free(tmp); --num; } child = child->fts_link; } } fts_close(fhandle); } free(tofree); return 0; } int cmd_W(char *filelist, char *Dir, int print) { if(!filelist) { errno = EINVAL; return -1; } // we copy filelist because we are nice char *tofree = malloc(strnlen(filelist, MAXARGLENGTH)+1); if(!tofree) { perror("malloc"); return -1; } memset(tofree, 0, strnlen(filelist, MAXARGLENGTH)+1); strncpy(tofree, filelist, strnlen(filelist, MAXARGLENGTH)+1); char *token = NULL; char *string = tofree; if (print == 1) { printf("W - Scrivo i seguenti file sul server: \n"); fflush(stdout); } int r = 0; while ((token = strsep_gnu(&string, ",")) != NULL) { int ok = 1; int opened = 0; if (print == 1) { printf("%s [", token); fflush(stdout); } // creo il file in modalita' locked if (openFile(token, O_CREATE | O_LOCK) == -1) { ok = 0; } else { // creato con successo opened = 1; } if (opened && (writeFile(token, Dir) == -1)) { ok = 0; } if (opened && !ok) { // errore precedente -> elimino il file vuoto removeFile(token); } else if (opened && (closeFile(token) == -1)) { // chiudo il file ok = 0; } if (print != 0) { printf("Esito: "); if (ok) printf("ok"); else { printf("errore"); perror("-w"); } printf("]\n"); fflush(stdout); } if(!ok) r = -1; } free(tofree); return r; } int cmd_r(char *filelist, char *dir, int print) { if (!filelist) { errno = EINVAL; return -1; } // we copy filelist because we are nice char *tofree = malloc(strnlen(filelist, MAXARGLENGTH)+1); memset(tofree, 0, strnlen(filelist, MAXARGLENGTH)+1); if(!tofree) { perror("malloc"); return -1; } strncpy(tofree, filelist, strnlen(filelist, MAXARGLENGTH)); char *token = NULL; char *string = tofree; if (print) { printf("r - Leggo i seguenti file dal server:\n"); fflush(stdout); } while ((token = strsep_gnu(&string, ",")) != NULL) { int ok = 1; int opened = 0; if (print != 0) { printf("%s ", token); fflush(stdout); } if (openFile(token, 0) == -1) { ok = 0; } else { opened = 1; } void *buf = NULL; size_t size = -1; printInfo(0, stdout); // read the content of the file if (ok && readFile(token, &buf, &size) == -1) { fprintf(stdout, "\nreadFile\n"); fflush(stdout); ok = 0; } if (print) { printInfo(1, stdout); } if (ok && print) { printf("Dimensione: %zu B\t", size); fflush(stdout); } if (buf) { free(buf); } // close the file if (opened && closeFile(token) == -1) { fprintf(stdout, "\ncloseFile\n"); fflush(stdout); ok = 0; } if (print) { printf("[Esito: "); if (ok) { printf("ok"); } else { printf("errore"); perror("-r"); } } if (print) { printf("]\n"); fflush(stdout); } } free(tofree); if (print && dir) { printf("I file letti sono stati scritti nella cartella %s.\n.", dir); fflush(stdout); } else if (print) { printf("I file letti non sono stati memorizzati su disco.\n"); fflush(stdout); } return 0; } int cmd_R(char *numStr, char *dir, int print) { if (print) { printf("R - Leggo i seguenti file dal server:\n"); fflush(stdout); } int n = -1; if(!numStr) // skips the step of converting n from string to int goto skipGetNumber; // we copy numStr because we are nice char *tofree = malloc(strnlen(numStr, MAXARGLENGTH)+1); if(!tofree) { perror("malloc"); return -1; } strncpy(tofree, numStr, strnlen(numStr, MAXARGLENGTH)); char *secondArg = tofree; strsep_gnu(&secondArg, ","); if (!secondArg) { n = -1; } else if (secondArg[0] == 'n' && secondArg[1] == '=') { char *number = &secondArg[2]; for (int i = 0; i < strlen(number); ++i) { if (!isdigit(number[i])) { free(tofree); errno = EINVAL; return -1; } } n = (int) strtol(number, NULL, 10); n = (n==0)?-1:n; } else { free(tofree); errno = EINVAL; return -1; } free(tofree); skipGetNumber: if (readNFiles(n, dir) == -1) { return -1; } if (print && dir) { printf("I file letti sono stati scritti nella cartella %s.\n", dir); } else if (print) { printf("I file letti non sono stati memorizzati su disco.\n"); } return 0; } int cmd_l(char *filelist, int print) { if (!filelist) { errno = EINVAL; return -1; } // we copy filelist because we are nice char *tofree = malloc(strnlen(filelist, MAXARGLENGTH)+1); if(!tofree) { perror("malloc"); return -1; } strncpy(tofree, filelist, strnlen(filelist, MAXARGLENGTH)); char *token; char *string = tofree; if (print) { printf("l - Acquisisco la mutua esclusione sui seguenti file:\n"); fflush(stdout); } while ((token = strsep_gnu(&string, ",")) != NULL) { if (print != 0) { printf("%s [Esito: ", token); fflush(stdout); } if (lockFile(token) == -1) { if (print != 0) { printf("errore"); perror("-l"); } } else if(print != 0) { printf("ok"); } if (print != 0) { printf("]\n"); fflush(stdout); } } free(tofree); return 0; } int cmd_u(char *filelist, int print) { if (!filelist) { errno = EINVAL; return -1; } // we copy filelist because we are nice char *tofree = malloc(strnlen(filelist, MAXARGLENGTH)+1); if(!tofree) { perror("malloc"); return -1; } strncpy(tofree, filelist, strnlen(filelist, MAXARGLENGTH)); char *token; char *string = tofree; if (print) { printf("u - Rilascio la mutua esclusione sui seguenti file:\n"); fflush(stdout); } while ((token = strsep_gnu(&string, ",")) != NULL) { if (print != 0) { printf("%s [Esito: ", token); fflush(stdout); } if (unlockFile(token) == -1) { if (print != 0) { printf("errore"); perror("-u"); } } else if(print != 0) { printf("ok"); } if (print != 0) { printf("]\n"); fflush(stdout); } } free(tofree); return 0; } int cmd_c(char *filelist, int print) { if (!filelist) { errno = EINVAL; return -1; } // we copy filelist because we are nice char *tofree = malloc(strnlen(filelist, MAXARGLENGTH)+1); if(!tofree) { perror("malloc"); return -1; } strncpy(tofree, filelist, strnlen(filelist, MAXARGLENGTH)); char *token; char *string = tofree; if (print) { printf("c - Cancello i seguenti file dal server:\n"); fflush(stdout); } while ((token = strsep_gnu(&string, ",")) != NULL) { if (print != 0) { printf("%s [Esito: ", token); fflush(stdout); } if (lockFile(token) == -1) { if (print) { printf("errore"); perror("-c"); } } else { if (removeFile(token) == -1) { if (print) { printf("errore"); perror("-c"); } } else if (print) { printf("ok"); } } if (print != 0) { printf("]\n"); fflush(stdout); } } free(tofree); return 0; }