Esercizio 1
Si scriva un programma
execif.c in ambiente POSIX che prende da riga di comando:
- il nome o il percorso di un file eseguibile exec1;
- 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 lerrore 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 duso: (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
#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:
#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:
- 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;
- writeToFile() scriva i primi len bytes nel buffer buf sul descrittore di file fd;
- alla ricezione di un segnale TERM o alla pressione di CTRL-C da parte dellutente la variabile globale shouldStop venga settata ad 1 (senza uscire immediatamente);
- in uscita dal ciclo while del metodo main il descrittore del file venga chiuso.
- eventuali errori di invocazione delle system call vengano gestiti opportunatamente
- vengano gestite opportunatamente le interruzione dovute a segnali durante l'esecuzione di write
Soluzione
#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;
}