Esercizio 1, prova pratica 20.01.2015

From Sistemi Operativi
Jump to navigation Jump to search
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);
}

--Ale (talk) 14:01, 25 May 2015 (CEST)