#include #include #include #include #include #include /* For O_* constants */ #include /* For mode constants */ #include /* * ***esercizio B3 'zip 2' *** Scrivere un programma che prende come parametro un nome di file da creare (nome_file) Legge i dati da stdin e li fa comprimere dall’utility zip nell’archivio compresso nome_file Quando l’utente digita Ctrl-C, il programma termina il processo zip, e mostra la dimensione dell’archivio compresso (ovvero lancia il comando ls -l nome_file.zip), poi termina. Esempio di comando bash che comprime la stringa ‘foo bar’ immagazzinandola in un un archivio compresso chiamato nome_file.zip (l’estensione .zip viene aggiunta automaticamente) echo 'foo bar' | zip -q nome_file - */ int pipe_fd[2]; pid_t pid; char * nome_archivio = "archivio.zip"; __off_t get_file_size(char * filename) { struct stat sb; int res; res = stat(filename, &sb); if (res == -1) { perror("fstat()"); return -1; } return sb.st_size; } static void sig_handler(int sig) { // NON è sicuro chiamare printf da un signal handler! // printf non è async-signal-safe (vedere Kerrisk sezione 21.1.2) // printf è usato qui solo a scopo diagnostico/didattico printf("[parent] ho ricevuto il segnale %d\n", sig); // termino il processo figlio chiudendo // l'estremità di scrittura della pipe // di conseguenza all'estremità di lettura della pipe // arriverà EOF if (close(pipe_fd[1]) == -1) { perror("close"); } printf("[signal handler] bye\n"); // aspetto la conclusione del processo figlio (zip) if (wait(NULL) == -1) { perror("wait"); } else { // mostro la dimensione dell'archivio __off_t filesize = get_file_size(nome_archivio); printf("dimensione archivio: %ld bytes\n", filesize); } exit(0); } extern char **environ; /* Or define _GNU_SOURCE to get it from */ int main() { // /usr/bin/zip -q nome_archivio - // Ctrl-C => SIGINT if (signal(SIGINT, sig_handler) == SIG_ERR) { perror("signal"); } if (pipe(pipe_fd) == -1) { perror("pipe"); exit(1); } pid = fork(); if (pid == -1) { perror("fork"); exit(1); } if (pid == 0) { // execve // eseguo zip con pipe al posto di stdin // chiudo estremità di scrittura della pipe close(pipe_fd[1]); // stdin diventa l'estremità di lettura della pipe if (dup2(pipe_fd[0], 0) == -1) { perror("dup2"); exit(1); } // ogni processo ha: // process ID : PID // parent process ID: PPID // ogni processo fa parte di una "sessione", identificata da "session ID" // ogni processo fa parte di una "gruppo di processi", identificata da "process group ID" // quando la shell rileva Ctrl-C, manda SIGINT a tutti i processi della "sessione". // per evitare questo, occorre "staccare" il processo figlio dalla sessione del processo padre. // vedere: man 2 setsid // man 7 credentials // A child created via fork(2) inherits its parent's session ID. The session ID is preserved across an execve(2). // per evitare che il processo figlio riceva Ctrl-C, il processo deve creare la sua "sessione" pid_t new_session_id = setsid(); // char * unzip = "/usr/bin/zip"; char * new_arguments [] = { unzip, "-q", nome_archivio, "-", NULL }; execve(unzip, new_arguments, environ); perror("execve"); return 1; } // chiudo estremità di lettura della pipe if (close(pipe_fd[0]) == -1) { perror("close"); exit(1); } int ch; int res; while ((ch = getchar()) != EOF) { char c; c = (char) ch; res = write(pipe_fd[1], &c, sizeof(c)); if (res == -1) { perror("write"); exit(1); } } // arrivo qui ad esempio se l'utente digita Ctrl-D if (ch == EOF) { printf("[parent] ho ricevuto EOF\n"); } if (close(pipe_fd[1]) == -1) { perror("close"); } return 0; }