Esercizio 1, prova pratica 20.01.2015
Scrivere un programma C di nome filepipe che abbia come unico parametro il pathnae di un file di testo.
Questo file contiene due comandi con I rispettivi parametri, uno per riga.
Il programma deve mettere in esecuzione concorrente I due comandi in modo che l'output del primo venga fornito come input del secondo usando una pipe.
Il programma deve terminare quando entrambi I comandi sono terminati.
Esempio: se il file ffff contiene:
ls -l
tac
il comando:
filepipe ffff
deve restituire lo stesso output del comando:
ls -l | tac
Soluzione di Stefano.zaniboni
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <error.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]){
int mypipe[2];
FILE *stream;
char *line=NULL;
size_t len=0;
ssize_t read;
/*controllo il numero degli argomenti passati*/
if(argc != 2){
fprintf(stderr, "no such arguments \n");
exit(1);
}
/*apro il file in sola lettura*/
stream=fopen(argv[1], "r");
if(stream==NULL){
exit(EXIT_FAILURE);
}
/*apro la pipe*/
if(pipe(mypipe) == -1){
fprintf(stderr, "Error pipe\n");
exit (1);
}
if(fork()==0){
close(1); /*chiudo lo stdout*/
dup(mypipe[1]); /*rimpiazzo lo stdout con la pipe*/
close(mypipe[0]); /*chiudo la read della pipe*/
close(mypipe[1]);
read=getline(&line, &len, stream);
char *lines[2]={line, NULL};
execvp(argv[1], lines);
perror("execvp failed");
exit (1);
}
if(fork()==0){
close(0); /*chiudo lo stdin*/
dup(mypipe[0]); /*rimpiazzo lo stdin con la pipe read*/
close(mypipe[1]); /*chiudo la write della pipe*/
close(mypipe[0]);
read=getline(&line, &len, stream);
execvp(argv[1], &line);
perror("execvp 2 failed");
exit (1);
}
close(mypipe[0]);
close(mypipe[1]);
wait(0);
wait(0);
free(line);
fclose(stream);
exit (0);
}
Il codice ho provato a testarlo ma la shell mi restituisce sempre su entrambe le exec "Permission Denied".
Nota del Prof: execvp(argv[1], &line) tenta di eseguire il file di testo (argv[1]) Renzo (talk) 16:04, 7 April 2015 (CEST)
Soluzione di AleZ
Nota: ho messo la soluzione di questo esercizio solamente perché non ne ho trovata sulla wiki una versione funzionante. Non so se sia stato corretto in classe e/o se sia stato già postato da qualche altra parte. Nel caso mi scuso.
Nota 2: La mia versione dovrebbe funzionare, però ho un "problema" incredibilmente strano: se cancello la variabile char sono_forse_inutile[4096]
dichiarata a riga 48, a run-time il programma restituisce segmentation fault. Quella variabile era stata introdotta in precedenza inizialmente per diversa scelta implementativa, e in seguito mantenuta per scopi di debug. Terminata la sua utilità, avevo pensato di rimuoverla, ma a quanto pare non mi è permesso, e il motivo mi sfugge davvero.
La funzione fsplit è stata realizzata prendendo largamente spunto da un'altra funzione trovata su stackoverflow.
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
char **fsplit(char **res, char str[]) {
char *p = strtok(str, " ");
int n_spaces = 0;
/* split string and append tokens to "res" */
while (p) {
n_spaces++;
res = realloc(res, sizeof(char*) * n_spaces);
if (res == NULL)
exit(EXIT_FAILURE); //allocation failed
res[n_spaces - 1] = p;
p = strtok(NULL, " ");
}
/* realloc one extra element for the last NULL */
res = realloc(res, sizeof(char*) * (n_spaces + 1));
res[n_spaces] = NULL;
// debug purpose
/* int i;
for (i = 0; i < (n_spaces + 1); i++) {
printf ("res[%d] = %s\n", i, res[i]);
}*/
return res;
}
int main (int argc, char* argv[]) {
//check arguments
if (argc != 2) {
fprintf(stderr, "Error: Expected 1 parameter.\n"
"Usage: %s <pathname>\n", argv[0]);
return(EXIT_FAILURE);
}
char *pathname = argv[1];
FILE *f;
struct stat f_info;
char command_one[100], command_two[100], sono_forse_inutile[4096];
int fd[2], file_desc_out;
pid_t pid;
f = fopen(pathname, "rt");
//check if file exists
if(lstat(pathname, &f_info) == -1) {
perror("stat");
return(EXIT_FAILURE);
}
//read the commands from the first two lines - ignore the other lines
if ( (fgets(command_one, 100, f) == NULL) ||
(fgets(command_two, 100, f) == NULL) ) {
fprintf(stderr, "Error while reading text from %s.", argv[1]);
}
fclose(f);
//remove the \n patterns from the command strings
strtok(command_one, "\n");
strtok(command_two, "\n");
/*split the command strings in arrays, where
- comm[0] is the command
- comm[1], comm[2], etc, are the arguments*/
char **commv_1 = fsplit(commv_1, command_one);
char **commv_2 = fsplit(commv_2, command_two);
//create the pipe
//remember: fd[0] reads and fd[1] writes
if (pipe(fd) == -1) {
perror("pipe");
return(EXIT_FAILURE);
}
//create the child and get its pid
if ( (pid = fork()) == -1) {
perror("fork");
return(EXIT_FAILURE);
}
if (pid == 0) { //child
dup2(fd[1], STDOUT_FILENO); //connect pipe from execvp to standard output
close(fd[0]); //close read side from parent
execvp(commv_1[0], commv_1);
perror(commv_1[0]);
return(EXIT_FAILURE);
} else {
close(fd[1]); //close write side from child
dup2(fd[0], file_desc_out);
char *path;
// obtain absolute path of the reader-side file descriptor of the pipe
realpath("/proc/self/fd/file_desc_out", path);
commv_2[1] = path; //set the obtained path as argument for command 2
wait(NULL); //wait for child to terminate
}
execvp(commv_2[0], commv_2);
perror(commv_2[0]);
return(EXIT_FAILURE);
}