Sistemi di Calcolo

Corso di Laurea in Ingegneria Informatica e Automatica - A.A. 2017-2018

HomePage | Avvisi | Diario lezioni | Programma | Materiale didattico | Esami | Forum | Login

Esercizio 1


Si scriva un programma execif.c in ambiente POSIX che prende da riga di comando:
  1. il nome o il percorso di un file eseguibile exec1;
  2. il nome o il percorso di un file eseguibile exec2, seguito da eventuali argomenti.
Si ricordi che comando man permette di consultare la documentazione POSIX.

Esempio: execif mioprogramma ls -l

Funzionamento: il programma esegue exec2 con i suoi parametri, attendendone la terminazione. Se exec2 termina con codice di terminazione diverso da 0, allora esegue anche exec1 e ne attende la terminazione. In ogni caso, restituisce 0 come codice di terminazione.
Gestione degli errori: se il programma viene lanciato con meno di 2 argomenti, segnala l’errore su stderr mediante testo: "usage: execif <exec1-pathname> <exec2-pathname> [<exec2-args>]". In caso di questo o di altri errori (es. system call che
falliscono) il programma termina con codice 1.

Esempio di sessione d’uso: (si ricordi che $? rappresenta il codice di terminazione del programma appena eseguito)
$ ./execif date rm file-inesistente
rm: cannot remove ‘file-inesistente’: No such file or directory
Fri Jan 27 08:44:11 CET 2017
$ echo $?
0
$ ./execif date echo “execution ok”
execution ok
$ echo $?
0
$ ./execif date
usage: execif <exec1-pathname> <exec2-pathname> [<exec2-args>]
$ echo $?
1
$ ./execif date file-inesistente
exexif: cannot execute file-inesistente
Fri Jan 27 08:45:18 CET 2017
$ echo $?
0


Suggerimento: utilizzare il seguente template per generare la soluzione:
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>

int exec(const char* pathname, char* const args[]) {

    // STEP 1: fork
    // STEP 2: verifico fork sia andato a buon fine
    // STEP 3: se sono nel processo figlio, allora eseguo il comando ricevuto; in caso di errore termino processo
    // STEP 4: se sono nel padre, aspetto terminazione figlio; se terminazione con errore ritorno 1 altrimenti 0

}

int main(int argc, char* argv[]) {

    // STEP 1: verifico numero di argomenti ricevuto, ritorno 1 in caso di errore
    // STEP 2: invoco la funzione exec indicando comando da eseguire e i suoi argomenti
    // STEP 3: se esecuzione comando non è andata a buon fine, allora eseguo altro comando invocando exec

    return 0;
}


Soluzione
execif.c
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>

int exec(const char* pathname, char* const args[]) {
    int status;
    pid_t p = fork();
    if (p == -1) {
        fprintf(stderr, "execif: cannot create new process\n");
        exit(EXIT_FAILURE);
    }
    if (p == 0) {
        execvp(pathname, args);
        fprintf(stderr, "execif: cannot execute %s\n", pathname);
        exit(EXIT_FAILURE);
    }
    if (wait(&status) == -1) {
        fprintf(stderr, "execif: error in wait\n");
        exit(EXIT_FAILURE)
    }
    return WIFEXITED(status) ? WEXITSTATUS(status) : 1;
}

int main(int argc, char* argv[]) {
    if (argc < 3) {
        fprintf(stderr, "usage: execif <exec1-pathname> <exec2-pathname> ...\n");
        return EXIT_FAILURE;
    }
    if (exec(argv[2], argv+2) != 0) {
        char* cmd[] = { argv[1], NULL };
        exec(*cmd, cmd);
    }
    return 0;
}


Esercizio 2

Si consideri il seguente codice:
primer.c
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define START   86028157

volatile int shouldStop = 0;

void writeToFile(int fd, char* buf, int len) {
    // DA COMPLETARE
}

int prime(unsigned int n) { // solo per n>=3 dispari
    unsigned int i;
    for (i=3; i<n/2; i+=2)
        if (n%i==0) return 0;
    return 1;
}

int main() {
    int fd = -1; // apertura descrittore - DA COMPLETARE

    // main loop per generazione numeri e stringhe da salvare in out.txt
    char buf[11];
    unsigned int num = START, found = 0;
    while (!shouldStop) {
        if (prime(num)) {
            int ret = sprintf(buf, "%u", num);
            buf[ret++] = '\n';
            writeToFile(fd, buf, ret);
            ++found;
        }
        num += 2;
    }
   
    printf("Numeri primi trovati partendo da %u: %u\n", START, found);
   
    return 0;
}


Si completi codice presenti in primer.c di modo che:
  1. all'avvio del programma venga creato un file di output denominato out.txt (se il file esiste già, troncarne il contenuto) con permessi: utente => lettura e scrittura, gruppo => lettura, altri => lettura;
  2. writeToFile() scriva i primi len bytes nel buffer buf sul descrittore di file fd;
  3. alla ricezione di un segnale TERM o alla pressione di CTRL-C da parte dell’utente la variabile globale shouldStop venga settata ad 1 (senza uscire immediatamente);
  4. in uscita dal ciclo while del metodo main il descrittore del file venga chiuso.
  5. eventuali errori di invocazione delle system call vengano gestiti opportunatamente
  6. vengano gestite opportunatamente le interruzione dovute a segnali durante l'esecuzione di write

Soluzione
primer.c
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#define START   86028157

volatile int shouldStop = 0;

void writeToFile(int fd, char* buf, int len) {

    ssize_t byte_scritti = 0;
    while (byte_scritti < len) {

        ssize_t res = write(fd, buf + byte_scritti, len - byte_scritti);
        if (res == -1) { // errore
            // analizziamo la causa dell'errore
            switch (errno) {
                case EINTR:
                    // se e' arrivato un segnale, non e' un errore fatale possiamo ritentare la scrittura
                    continue;
                    break;
                default:
                    // consideriamo le altre cause come errori fatali non recuperabili
                    perror("errore write");
                    exit(EXIT_FAILURE);
            }
        }
        byte_scritti += res;
    }
}

int prime(unsigned int n) { // solo per n>=3 dispari
    unsigned int i;
    for (i=3; i<n/2; i+=2)
        if (n%i==0) return 0;
    return 1;
}

void handler(int signo) {
    char* s = (signo == SIGINT) ? "SIGINT" : "SIGTERM";
    printf("Segnale %s ricevuto: imposto shouldStop = 1\n", s);
    shouldStop = 1;
}

int main() {
    // registrazione signal handler (comune per i due segnali)
    struct sigaction act;
    memset(&act, 0, sizeof(struct sigaction));
    act.sa_handler = handler;

    if (sigaction(SIGINT, &act, NULL)) {
        perror("sigaction fallita");
        exit(1);
    }
    if (sigaction(SIGTERM, &act, NULL)) {
        perror("sigaction fallita");
        exit(1);
    }
   
    // apertura descrittore
    int fd = open("out.txt", O_WRONLY|O_CREAT|O_TRUNC, 0644);
    if (fd == -1) {
        perror("open fallita");
        exit(1);
    }

    // main loop per generazione numeri e stringhe da salvare in out.txt
    char buf[11];
    unsigned int num = START, found = 0;
    while (!shouldStop) {
        if (prime(num)) {
            int ret = sprintf(buf, "%u", num);
            buf[ret++] = '\n';
            writeToFile(fd, buf, ret);
            ++found;
        }
        num += 2;
    }
   
    printf("Numeri primi trovati partendo da %u: %u\n", START, found);
   
    // chiusura descrittore
    if (close(fd) == -1) {
        perror("close fallita");
        exit(1);
    }
   
    return 0;
}