I am making a program in which I need to read from a file and execute the command in pipe. The command could be something like "ps -lu user | sort", and I should execute the first command and then the second, in different processes (using fork and exec). The problem comes when I do the exec and it doesn't work correctly.
What I have so far is the following:
`int main(int argc, char* argv[]){
int file = open(argv[1], O_RDONLY);
int fd[2]; int pid;
char s[8192];
int nBytes = read(file, &s, 8192);
char* s2 =malloc(8192); char* s3 = malloc(8192);
char* comando1[8192]; char* comando2[8192];
s2 = strtok(s, "|");
s3 = strtok(NULL, "\n");
comando1[0] = strtok(s2, " ");
int i;
if(s2 != NULL)
for(i = 1; comando1[i] = strtok(NULL, " \t"); i++);
comando2[0] = strtok(s3, " ");
if(s3 != NULL)
for(i = 1; comando2[i] = strtok(NULL, " \t"); i++);
pid = fork();
if(pid == 0){//
pipe(fd);
int pid2 = fork();
if (pid2 > 0){
printf("%s\n", comando1[0]);
close(1);
close(fd[0]); //cerramos stdout y extremo de lectura
dup(fd[1]); close(fd[1]); //redireccionamos salida estándar a la pipe
execvp(comando1[0], &comando1[0]); //ejecutamos el comando 1 con salida pipe
perror("Error");
write(2, "Error en el exec1", 17);
}
else if(pid == 0){
close(fd[1]); close(0); //cerramos extremo de escritura y stdin
dup(fd[0]); close(fd[0]); //redirección entrada estándar a la pipe
printf("%s\n", comando2[0]);
execvp(comando2[0], &comando2[0]); //ejecutamos el comando 1 con salida pipe
perror("Error");
write(2, "Error en el exec2", 17);
}
}
}
`
Which does not work as expected. For a file "file.txt" with the following command: "ps -lu user | sort -n -r", it says:
sort:
illegal option --
usage: sort [-cmu] [-o output] [-T directory] [-S mem] [-z recsz] [-dfiMnr] [-ddfiMnr] [-sdmem] [-sdm] [-z recsz
[-dfiMnr] [-b] [-t char] [-k keydef] [+pos1 [-pos2]] files....
However, if I manually add the parameters keeping everything the same (change execlp to execvp and pass (execlp("ps", "ps", "-lu", "user", 0) and execlp("sort", "sort", 0); the result is as expected (but it is not correct since I am not reading from the file). what could it be due to?
Related
I'm trying to code a shell in C that supports multi-pipe process handling, depending on the amount of separate processes given by the user, and each separated by a "|" symbol. The shell forks the amount of processes into a child for each process.
Here's an example:
texto1.txt = "Isto é o Texto 1"
$ cat texto1.txt | grep -c Isto
result: 1
But I'm having trouble with communicating between children and finally to the parent process.
Here's my current code for the execpipe function, which executes the pipe process, being argv1 and argv2 the 2 separate processes given by user input: (Example: ls -l | wc -l)
int execpipe(char ** argv1, char ** argv2)
{
int fds[2];
pipe(fds);
int i;
pid_t p1, p2;
p1 = fork();
if (p1 == -1)
{ // error
char * error = strerror(errno);
printf("error fork!!\n");
return 1;
}
if (p1 == 0)
{ // child process
close(fds[0]);
dup2(fds[1], STDOUT_FILENO);
close(fds[1]);
if(execvp(argv1[0], argv1)<0){ // run command AFTER pipe character in userinput
char * error = strerror(errno);
printf("unknown command\n");
return 0;
}
}
else
{ // parent process
p2 = fork();
if(p2==0){
close(fds[1]);
dup2(fds[0], STDIN_FILENO);
close(fds[0]);
if(execvp(argv2[0], argv2)<0){ // run command AFTER pipe character in userinput
char * error = strerror(errno);
printf("unknown command\n");
return 0;
}
}else{ //Parent waits for both of it's children
wait(NULL);
wait(NULL);
}
}
}
The parent must close the pipe's write end to indicate the second process that no more data will come:
}
}else{ //Parent waits for both of it's children
close(fds[1]); // ++ add this line
wait(NULL);
wait(NULL);
}
Little note: instead of
char * error = strerror(errno);
printf("error fork!!\n");
why not
perror("fork");
I have a school project which consists of reproducing the shell pipe command between two commands that the operator will choose as it pleases.
./pipex infile "ls -l" "wc -l" outfile
should be equivalent to
< infile ls -l | wc -l > outfile
Roles of my find_path function:
extract the contents of PATH from the envp
use split() to delimit the paths according to ":" and store them in a double pointer
use strjoin() to append a "/" to the end of each path, and add the user command
test each path with access() then return the valid path
char *find_path(char *cmd, char **envp)
{
char **array_of_paths;
char *path_ultime;
int i;
char *temp;
i = 0;
while (ft_strnstr(envp[i], "PATH=", 5) == 0)
i++;
array_of_paths = ft_split(envp[i] + 5, ':');
i = 0;
while (array_of_paths[i])
{
temp = ft_strjoin(array_of_paths[i], "/");
path_ultime = ft_strjoin(temp, cmd);
free(temp);
if (access(path_ultime, F_OK | X_OK) == 0)
return (path_ultime);
i++;
}
return (0);
}
My main takes care of:
split the argv[2] and the argv[3] with a space as delimiter, to keep only the command "ls" or "wc", and not the options
classic stuff like pipe(), fork(), execve() …
int main(int argc, char *argv[], char **envp)
{
(void)argc;
pid_t pid1;
pid_t pid2;
int fd[2];
char **cmd_and_options1;
char **cmd_and_options2;
char *path_ultime1;
char *path_ultime2;
cmd_and_options1 = ft_split(argv[2], ' ');
cmd_and_options2 = ft_split(argv[3], ' ');
path_ultime1 = find_path(cmd_and_options1[0], envp);
path_ultime2 = find_path(cmd_and_options2[0], envp);
if (pipe(fd) == -1)
return (1);
pid1 = fork();
if (pid1 == -1)
return (1);
if (pid1 == 0) // First child
{
dup2(fd[1], STDOUT_FILENO);
close(fd[0]);
close(fd[1]);
execve(path_ultime1, cmd_and_options1, envp);
}
pid2 = fork();
if (pid2 == -1)
return (1);
if (pid2 == 0) // Second child
{
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
close(fd[1]);
execve(path_ultime2, cmd_and_options2, envp);
}
close(fd[0]);
close(fd[1]);
waitpid(pid1, NULL, 0);
waitpid(pid2, NULL, 0);
return (0);
}
A friend told me that I lacked the management of the input and output file (argv[1] and argv[4]) that the operator will enter but I don't really understand what that means.
I know I have to use open() somewhere...
Can you give me some clues?
I am doing a simple shell program in Linux. I am trying to implement the pipe operator for shell. I cant seem to find the problem and solution. The execvp didnt read from the previous pipe pipi. So if i do ls -l | more, the more prints bad usage instead of the list of files
Here is my code below.
execCommands handles the commands.
command.h parse the command.
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <fcntl.h>
#include <wait.h>
#include "modules/token.h"
#include "modules/command.h"
#define BUFSIZE 10000
int tokenSize = 0;
int cmdSize = 0;
char promptSymbol[BUFSIZE] = "%";
char cwd[BUFSIZE];
// execute commands accordingly to the apropriate function
void execCommands(Command command[]){
int fd = 0;
char dir[BUFSIZE];
pid_t pid, p2;
int pipi[2];
for(int i = 0;i < cmdSize;i++){
//start executing commands
if(strcmp(command[i].argv[0], "prompt") == 0){
strcpy(promptSymbol, command[i].argv[1]); //change prompt symbol
}else if(strcmp(command[i].argv[0], "pwd") == 0){
if(getcwd(cwd, sizeof(cwd)) != NULL){
printf("%s\n", cwd );
}else{
perror("getcwd() error\n");
}
}else if(strcmp(command[i].argv[0], "cd") == 0){
if(command[i].argv[1] == NULL){
strcpy(dir, "/home");
}else if(strcmp(command[i].argv[1], "~") == 0 || strcmp(command[i].argv[1], "~/") == 0){
strcpy(dir, "/home");
}else{
strcpy(dir, command[i].argv[1]);
}
if(chdir(dir) < 0){
printf("No such directory/file %s found\n", dir);
}
}else{
pid = fork();
if(pid < 0){
perror("pid fork failed\n");
}
if(pid == 0){
if(command[i].stdin_file || command[i].stdout_file){
//check for stdin redirection
if(command[i].stdin_file){
fd = open(command[i].stdin_file, O_RDONLY);
dup2(fd, STDIN_FILENO);
}
//check for stdout redirection
if(command[i].stdout_file){
fd = open(command[i].stdout_file, O_WRONLY | O_CREAT, 0777);
dup2(fd, STDOUT_FILENO);
}
}
if(strcmp(command[i].sep,"|") == 0){
if(pipe(pipi) == -1){
perror("pipi pipe failed\n");
}
close(pipi[0]);
dup2(pipi[1], STDOUT_FILENO);
close(pipi[1]);
execvp(command[i].argv[0], command[i].argv);
p2 = fork();
if(p2 > 0){
close(pipi[1]);
dup2(pipi[0], STDIN_FILENO);
close(pipi[0]);
execvp(command[i].argv[0], command[i].argv);
}
}else{
execvp(command[i].argv[0], command[i].argv);
}
close(fd);
exit(0);
}else{
if(strcmp(command[i].sep, "&") != 0){
wait(0);
}
} // end of first fork()
} //end of command check
} //end of for loop
} //end of execCommands
//handles SIGINT, SIGTSTP and SIGQUIT
void handler(int num){
printf("\nEnter 'exit' to end shell program\n");
}
main code
int main(){
char cmdLine[BUFSIZE];
char *token[BUFSIZE];
Command command[BUFSIZE];
//Runs untill user wants to exit
while(strcmp(cmdLine, "exit") != 0){
signal(SIGTSTP, handler); //Ignores SIGTSTP signal(ctrl+z)
signal(SIGINT, handler); //Ignores SIGINT signal(ctrl+c)
signal(SIGQUIT, handler); //Ignores SIGQUIT signal(ctrl+\)
//get current directory
if(getcwd(cwd, sizeof(cwd)) != NULL){
strcat(cwd, promptSymbol);
}else{
perror("getcwd error");
return 1;
}
//prompts user for icommand
printf("%s ", cwd);
scanf(" %[^\n]", cmdLine);
// split command line into tokens
tokenSize = tokenise(cmdLine, token);
//split the tokens into commands
initCommand(command);
cmdSize = seperateCommands(token, command);
//execute commands accordingly
execCommands(command);
}
return 0;
}
I have an operating systems problem where I need to execute the 'cat' command in the child process. The child process is created using fork() system call. While running 'cat' command in the terminal, the input entered in the terminal should be read and converted to another string. Example: 'dog' entered in the terminal should be converted to 'fish'. The program should use pipe() system call to read the input and output the converted string.
Example of the output after 'cat' command is executed:
dog
fish
dog
fish
My current output after 'cat' command is executed:
dog
dog
hh
hh
I have included my C code but it does not read the input of 'cat' command.
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<string.h>
#include<sys/wait.h>
void main(int argc , char *argv[]){
int fd[2];
int fd2[2];
char fixed_str[] = "forgeeks.org";
pid_t childpid;
if (pipe(fd)==-1)
{
fprintf(stderr, "Pipe Failed" );
return 1;
}
if (pipe(fd2)==-1)
{
fprintf(stderr, "Pipe Failed" );
return 1;
}
childpid = fork();
printf("before if");
if (childpid < 0){
fprintf(stderr, "fork Failed" );
return 1;
} else if(childpid == 0){
char input_str[100];
printf("child");
/*
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
char* myargs[] = {"cat",NULL};
execvp(myargs[0], myargs);
printf("cat executed");
// scanf("%s", input_str);
read(fd[0], input_str, 100);
close(fd[0]);
*/
close(fd2[0]);
dup2(STDOUT_FILENO, fd2[1]);
char* myargs[] = {"cat",NULL};
execvp(myargs[0], myargs);
/*
int k = strlen(input_str);
int i;
for (i=0; i<strlen(fixed_str); i++)
input_str[k++] = fixed_str[i];
input_str[k] = '\0'; // string ends with '\0'
*/
write(fd2[1], input_str, strlen(input_str)+1);
close(fd2[1]);
} else {
printf("parent");
char concat_str[100];
close(fd2[1]);
dup2(STDIN_FILENO, fd2[0]);
wait(NULL);
printf("after wait");
int l = strlen(concat_str);
int m;
for (m=0; m<strlen(fixed_str); m++)
concat_str[l++] = fixed_str[m];
concat_str[l] = '\0'; // string ends with '\0'
read(fd2[0], concat_str, strlen(concat_str)+1);
printf("%s",concat_str);
close(fd2[0]);
}
}
Hi i'm trying to build a shell on linux and i'm stuck with the pipelining part.First i take the inputs from the user like "ls | sort" then when i try to run the program it lookls like the commands ls and sort doesnt work
It looks like i've done everything right but it still cant seem to work. can you help please. thanks in advance
include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <fcntl.h>
#include <sys/stat.h>
#define CREATE_FLAGS (O_WRONLY | O_CREAT | O_APPEND)
#define CREATE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
int setup();
int main(int argc, const char * argv[])
{
while(1)
{
printf("333sh: ");
if(setup())
break;
}
return 0;
}
int setup(){
char input [128];
char *arg[32];
int i = 1;
while(fgets(input,128,stdin)!=NULL)
{
arg[0] = strtok(input," \n");
while((arg[i]=strtok(NULL," \n")) != NULL){
i++;
}
if (arg[1]!=NULL && strcmp(arg[1],"|")==0 && arg[2]!=NULL ){
pid_t pid;
int fd[3];
pipe(fd);
pid=fork();
if(pid<0){
printf("fork");
}
else if(pid==0){
pid_t cpid;
cpid=fork();
if(cpid==0){
dup2(fd[2], 1); // Replace stdin with the read end of the pipe
close(fd[0]); // Don't need another copy of the pipe read end hanging about
close(fd[2]);
execvp(arg[0],arg);
}
else if(pid>0){
dup2(fd[0], 0); // Replace stdout with the write end of the pipe
close(fd[0]); //close read from pipe, in parent
close(fd[2]); // Don't need another copy of the pipe write end hanging about
execvp(arg[2], arg);
}
}
else if(pid>0){
waitpid(pid, NULL,0);
}
}
}
}
Your biggest problem is that your argument lists for your commands are malformed (after you've resolved the index 2 vs index 1 issue with the pipe file descriptors diagnosed by Ben Jackson in his answer).
I added a function:
static void dump_args(int pid, char **argv)
{
int i = 0;
fprintf(stderr, "args for %d:\n", pid);
while (*argv != 0)
fprintf(stderr, "%d: [%s]\n", i++, *argv++);
}
and called it just before the calls to execvp(), and the output I got was:
$ ./ns
333sh: ls | sort
args for 29780:
0: [ls]
1: [|]
2: [sort]
ls: sort: No such file or directory
ls: |: No such file or directory
^C
$
The control-C was me interrupting the program. The arguments for each command must be 'the command name' (conventionally, the name of the executable), followed by the remaining arguments and a null pointer.
Your tokenization code is not providing two correct commands.
You also have a problem with which PID you're looking at:
cpid = fork();
if (cpid == 0)
{
dup2(fd[1], 1);
close(fd[0]);
close(fd[1]);
dump_args(getpid(), arg);
execvp(arg[0], arg);
fprintf(stderr, "Failed to exec %s\n", arg[0]);
exit(1);
}
else if (pid > 0) // should be cpid!
{
dup2(fd[0], 0);
close(fd[0]);
close(fd[1]);
dump_args(pid, arg);
execvp(arg[1], arg);
fprintf(stderr, "Failed to exec %s\n", arg[1]);
exit(1);
}
You also need to close the pipe file descriptors in the parent process before waiting.
This code compiles and 'works' for simple x | y command sequences such as ls | sort or ls | sort -r. However, it is far from being a general solution; you'll need to fix your argument parsing code quite a lot before you reach a general solution.
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int setup(void);
int main(void)
{
while (1)
{
printf("333sh: ");
if (setup())
break;
}
return 0;
}
static void dump_args(int pid, char **argv)
{
int i = 0;
fprintf(stderr, "args for %d:\n", pid);
while (*argv != 0)
fprintf(stderr, "%d: [%s]\n", i++, *argv++);
}
int setup(void)
{
char input[128];
char *arg[32];
int i = 1;
while (fgets(input, sizeof(input), stdin) != NULL)
{
arg[0] = strtok(input, " \n");
while ((arg[i] = strtok(NULL, " \n")) != NULL)
{
i++;
}
if (arg[1] != NULL && strcmp(arg[1], "|") == 0 && arg[2] != NULL)
{
pid_t pid;
int fd[2];
arg[1] = NULL;
pipe(fd);
pid = fork();
if (pid < 0)
{
fprintf(stderr, "fork failed\n");
return 1;
}
else if (pid == 0)
{
pid_t cpid = fork();
if (cpid < 0)
{
fprintf(stderr, "fork failed\n");
return 1;
}
else if (cpid == 0)
{
printf("Writer: [%s]\n", arg[0]);
dup2(fd[1], 1);
close(fd[0]);
close(fd[1]);
dump_args(getpid(), arg);
execvp(arg[0], arg);
fprintf(stderr, "Failed to exec %s\n", arg[0]);
exit(1);
}
else
{
printf("Reader: [%s]\n", arg[2]);
assert(cpid > 0);
dup2(fd[0], 0);
close(fd[0]);
close(fd[1]);
dump_args(getpid(), &arg[2]);
execvp(arg[2], &arg[2]);
fprintf(stderr, "Failed to exec %s\n", arg[2]);
exit(1);
}
}
else
{
close(fd[0]);
close(fd[1]);
assert(pid > 0);
while (waitpid(pid, NULL, 0) != -1)
;
}
}
}
return 1;
}
You're using fd[0] and fd[2] but pipe(fd) only sets fd[0] and fd[1].
Couple of immediate problems:
setup() has no return value, but you expect an int
The definition of fgets is:
char * fgets ( char * str, int num, FILE * stream );
Get string from stream
Reads characters from stream and stores them as a C string into str until (num-1) characters have been read or either a newline or the end-of-file is reached, whichever happens first.
A newline character makes fgets stop reading, but it is considered a valid character by the function and included in the string copied to str.
fgets() returns NULL on an error; otherwise it returns a pointer to str. So this seems like a very unsound test condition in your while loop.