Difference between revisions of "Esercizio 1, prova pratica 20.01.2015"
(Created page with "<source lang="text"> 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 ...") |
|||
(4 intermediate revisions by 2 users not shown) | |||
Line 83: | Line 83: | ||
Il codice ho provato a testarlo ma la shell mi restituisce sempre su entrambe le exec "Permission Denied". | 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]) [[User:Renzo|Renzo]] ([[User talk: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. | ||
+ | |||
+ | Due versioni: una utilizzando la libreria s2argv e la funzione getline, l'altra senza entrambe. | ||
+ | |||
+ | '''Versione 1 (con s2argv e getline)''' | ||
+ | <source lang="c"> | ||
+ | #include <stdio.h> | ||
+ | #include <stdlib.h> | ||
+ | #include <unistd.h> | ||
+ | #include "/home/alessandro/Scrivania/s2argv/s2argv.h" | ||
+ | |||
+ | 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); | ||
+ | } | ||
+ | |||
+ | FILE *f; | ||
+ | size_t n; | ||
+ | int fd[2]; | ||
+ | char *command = NULL; | ||
+ | |||
+ | f = fopen(argv[1], "rt"); | ||
+ | |||
+ | //get first command | ||
+ | getline(&command, &n, f); | ||
+ | |||
+ | //create the pipe | ||
+ | if (pipe(fd) == -1) { | ||
+ | perror("pipe"); | ||
+ | return(EXIT_FAILURE); | ||
+ | } | ||
+ | |||
+ | //create child process | ||
+ | switch (fork()) { | ||
+ | case -1: | ||
+ | perror("fork"); | ||
+ | break; | ||
+ | |||
+ | case 0: //child process | ||
+ | close(fd[0]); | ||
+ | dup2(fd[1], STDOUT_FILENO); //redirect STDOUT to write to fd[1]->fd[0] | ||
+ | execsp(command); | ||
+ | return(EXIT_FAILURE); | ||
+ | |||
+ | default: | ||
+ | close(fd[1]); | ||
+ | dup2(fd[0], STDIN_FILENO); //redirect STDIN to read from fd[0]<-fd[1] | ||
+ | wait(NULL); //wait for child to terminate | ||
+ | lseek (fd[1], 0, SEEK_SET); //reposition STDOUT read offset to the start | ||
+ | free(command); | ||
+ | command = NULL; | ||
+ | getline(&command, &n, f); //get second command | ||
+ | execsp(command); | ||
+ | return(EXIT_FAILURE); | ||
+ | } | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | |||
+ | |||
+ | '''Versione 2 (senza s2argv e getline)''' | ||
+ | |||
+ | '''Nota 2''': Nella versione senza s2argv e getline, tutto funziona, però ho un "problema" incredibilmente strano: se cancello la variabile <code>char sono_forse_inutile[4096]</code> 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. | ||
+ | |||
+ | <source lang="c"> | ||
+ | #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); | ||
+ | }</source> | ||
+ | --[[User:Ale|Ale]] ([[User talk:Ale|talk]]) 14:01, 25 May 2015 (CEST) |
Latest revision as of 17:17, 25 May 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.
Due versioni: una utilizzando la libreria s2argv e la funzione getline, l'altra senza entrambe.
Versione 1 (con s2argv e getline)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "/home/alessandro/Scrivania/s2argv/s2argv.h"
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);
}
FILE *f;
size_t n;
int fd[2];
char *command = NULL;
f = fopen(argv[1], "rt");
//get first command
getline(&command, &n, f);
//create the pipe
if (pipe(fd) == -1) {
perror("pipe");
return(EXIT_FAILURE);
}
//create child process
switch (fork()) {
case -1:
perror("fork");
break;
case 0: //child process
close(fd[0]);
dup2(fd[1], STDOUT_FILENO); //redirect STDOUT to write to fd[1]->fd[0]
execsp(command);
return(EXIT_FAILURE);
default:
close(fd[1]);
dup2(fd[0], STDIN_FILENO); //redirect STDIN to read from fd[0]<-fd[1]
wait(NULL); //wait for child to terminate
lseek (fd[1], 0, SEEK_SET); //reposition STDOUT read offset to the start
free(command);
command = NULL;
getline(&command, &n, f); //get second command
execsp(command);
return(EXIT_FAILURE);
}
}
Versione 2 (senza s2argv e getline)
Nota 2: Nella versione senza s2argv e getline, tutto funziona, 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);
}