I am have written a function in C that emulates command line piping. Now i am trying to handle redirection using '<' and '>'. I have managed to implement redirecting stdout correctly, however I am having problems wit stdin.
The function takes 3 arguments, the list of commands, and an input and output location. These can be NULL if there is no '<' or '>' used.
I am not sure why the input is not working. When I run "wc < input_file" it just executes wc.
The code looks like this:
void pipe_function(char *** cmd, char * input, char * output)
{
int p[2];
pid_t pid;
int fd_in = 0;
int count = 0;
/* If there are no input or output destinations specified */
if ((input == NULL) && (output == NULL))
{
while (* cmd != NULL)
{
pipe(p);
if ((pid = fork()) == -1)
{
exit(EXIT_FAILURE);
}
else if (pid == 0)
{
dup2(fd_in, 0);
if (* (cmd + 1) != NULL)
{
dup2(p[1], 1);
}
close(p[0]);
execvp((* cmd)[0], * cmd);
exit(EXIT_FAILURE);
}
else
{
wait(NULL);
close(p[1]);
fd_in = p[0];
cmd++;
}
}
}
/* If there is no input destination specified but an output destination specified */
else if ((input == NULL) && (output != NULL))
{
while (* cmd != NULL)
{
pipe(p);
if ((pid = fork()) == -1)
{
exit(EXIT_FAILURE);
}
else if (pid == 0)
{
dup2(fd_in, 0);
if (* (cmd + 1) != NULL)
{
dup2(p[1], 1);
}
else
{
int file = open(output, O_WRONLY | O_CREAT, 0777);
dup2(file, 1);
close(file);
}
close(p[0]);
execvp((* cmd)[0], * cmd);
exit(EXIT_FAILURE);
}
else
{
wait(NULL);
close(p[1]);
fd_in = p[0];
cmd++;
}
}
}
/* If there is an input destination and no output destination */
/* THIS IS WHERE TROUBLE IS OCCOURING*/
else if ((input != NULL) && (output == NULL))
{
while (* cmd != NULL)
{
pipe(p);
if ((pid = fork()) == -1)
{
exit(EXIT_FAILURE);
}
else if (pid == 0)
{
if (count != 0)
{
dup2(fd_in, 0);
}
else
{
int file = open(input, O_RDONLY);
dup2(file, 0);
close(file);
}
if (* (cmd + 1) != NULL)
{
dup2(p[1], 1);
}
close(p[0]);
execvp((* cmd)[0], * cmd);
exit(EXIT_FAILURE);
}
else
{
wait(NULL);
close(p[1]);
fd_in = p[0];
cmd++;
}
count++;
}
}
/* If there is both an input and an output destination */
else
{
}
}
Any help would be greatly appreciated :)
It doesn't completely fix your program, but I think the main bug here is that you don't close STDIN_FILENO before calling dup2. Thus dup2 fails, and you remain with STDIN at the 0 fd.
This is actually incorrect, dup2() closes newfd for you!
Generally, the code looks pretty good, but it's best practice to always check the return value of library functions - this would obviously have caught the bug here.
Related
I am trying to create a small shell using C. At the moment I am trying to figure out piping and external commands. I got stuck in them both even after looking at various youtube videos.
I referred to MAN and even Advanced Linux Programming
What can I change to improve and make the implementation work?
This a part of the checking of commands, args := tokenisation by whitespace, commLHS := will be used to store args before | and commRHS will be used to store args after | and indexT refers to the number of arguments inputted
else if((check4pipe(args, commLHS, commRHS, indexT) != 0))
{
return runPipeComm(commLHS, commRHS);
//fprintf(stderr, "%s: command not found\n", args[0]);
}
This will execute External Commands
void externalCommands(char **args)
{
// fork-plus-exec pattern
// https://www.percona.com/community-blog/2021/01/04/fork-exec-wait-and-exit/
/*
First we Fork
Then we Exec
Then we Wait
Then we Exit
*/
int status;
pid_t pip = fork();
if (pip == -1)
{
perror("Error - fork()");
}
else if (pip == 0)
{
//If PID is the child process
//Launches the process.
if (execvp(args[0], args) < 0)
{
perror("Error - execvp()");
}
}
else
{ //If PID is the parent process.
//Waits for the child process and returns exit code if waitpid() is successful.
if(waitpid(pip, &status, WUNTRACED) == -1)
{
perror("Error occured during waitpi");
}
else
{
//set_exitcode(status); //Sets the exitcode environment variable.
}
}
}
This is to check for | in args inputted by user after tokenisation.
int check4pipe(char **args, char **pipeLHS, char **pipeRHS, int indexT)
{
bool foundPipe = false;
for(int i = 0; i < indexT; i++)
{
if(strcmp(args[i], "|") == 0)
{
foundPipe = true;
memcpy(pipeLHS, args, (i+1) * sizeof(char*));
pipeLHS[i] = NULL;
memcpy(pipeLHS, args+(i+1), ((indexT-i)+1) * sizeof(char*));
pipeRHS[i]= NULL;
}
else
{
continue;
}
}
if(foundPipe == true)
{
return 1;
}
else
{
return 0;
}
}
This will run the pipe commands
int runPipeComm(char **commLHS, char **commRHS)
{
int userPipe[2];
pid_t pip1; // Pipe ID 1
pid_t pip2; // Pipe ID 2
if(pipe(userPipe) < 0)
{
perror("Error Occurred while piping: ");
}
// Start Process
pip1 = fork();
if(pip1 == -1)
{
perror("Error Occurred while forking: ");
}
else if(pip1 == 0)
{
dup2(userPipe[1], STDOUT_FILENO);
close(userPipe[1]);
//run
exit(0);
}
else
{
pip2 = fork();
if(pip2 == -1)
{
perror("Error Occurred while forking: ");
}
else if(pip2 == 0)
{
dup2(userPipe[0], STDOUT_FILENO);
close(userPipe[1]);
//run
exit(0);
}
else
{
close(userPipe[0]);
close(userPipe[1]);
wait(NULL);
wait(NULL);
}
}
return 1;
}
You forgot to change some things when copying snippets within your program.
wrong:
memcpy(pipeLHS, args+(i+1), ((indexT-i)+1) * sizeof(char*));
pipeRHS[i]= NULL;
right:
memcpy(pipeRHS, args+(i+1), (indexT-(i+1)) * sizeof (char*));
pipeRHS[indexT-(i+1)] = NULL;
wrong:
dup2(userPipe[0], STDOUT_FILENO);
close(userPipe[1]);
right:
dup2(userPipe[0], STDIN_FILENO);
close(userPipe[0]);
After the first //run line, add the missing
execvp(*commLHS, commLHS);
perror("Error - execvp()");
After the second //run line, add the missing
execvp(*commRHS, commRHS);
perror("Error - execvp()");
Finally, the write end of the pipe must be closed in the parent process, so move the close() there:
close(userPipe[1]);
pip2 = fork();
I'm trying to create my own unix shell and I've hit a wall while trying to append a command such as ls to an existing file, for example.
ls >> myOutput
I was able to do a basic redirection using > to print to an output file and figured doing >> would be quite similar, but I guess I'm wrong.
This is my code:
int pid;
int in = 0, out = 0, append = 0, j;
int fd0, fd1, fda;
char* args[MAX_ARGS];
char inFileName[64], outFileName[64];
//used to get arguments for desired command
//i.e. ls -a -l -t
get_args(cmdline, args);
//Commands used to exit the shell.
if(!strcmp(args[0], "quit") || !strcmp(args[0], "exit"))
{
exit(0);
}
pid = fork();
if(pid == 0)
{ /* child process */
for(j = 0; args[j] != '\0'; ++j)
{
if(strcmp(args[j], ">>") == 0)
{
args[j] = NULL;
strcpy(outFileName, args[j + 1]);
printf("You want to append data to existing file\n");
append = 2;
}
if(strcmp(args[j], "<") == 0)
{
args[j] = NULL;
strcpy(inFileName, args[j + 1]);
printf("input file name is %s\n", inFileName);
in = 2;
}
if(strcmp(args[j], ">") == 0)
{
args[j] = NULL;
strcpy(outFileName, args[j + 1]);
printf("output file name is %s\n", outFileName);
out = 2;
}
}
//printf("in is %d and out is %d\n", in, out);
if(!strcmp(args[0], "|"))
{
printf("You want to pipe info\n");
}
if(append)
{
**//here is where my issue occurs**
if((fda = open(outFileName, O_RDWR|O_APPEND)) < 0)
{
perror("Error appending data\n");
exit(0);
}
dup2(fda, STDOUT_FILENO);//1
close(fda);
}
if(in)
{
if((fd0 = open(inFileName, O_RDONLY, 0)) < 0)
{
perror("Couldn't read from input file\n");
exit(0);
}
//Changes where the command will read from STDIN to a input file.
dup2(fd0, 0);
close(fd0);
}
if(out)
{
if((fd1 = creat(outFileName , 0644)) < 0)
{
perror("Coudln't create output file\n");
exit(0);
}
dup2(fd1, STDOUT_FILENO);//1
close(fd1);
}
execvp(*args, args);
perror("exec failed");
exit(-1);
}
else if(pid > 0)
{ /* parent process */
waitpid(pid, NULL, 0);
}
else
{ /* error occurred */
perror("fork failed");
exit(1);
}
Please let me know if you would like further details. Any help would truly be appreciated.
The following function successfully executes any command that doesn't contain pipes, so don't worry about the weird functions. These work. The problem I am having is that whenever I execute any command like the following:
cat file.txt | grep string
the command is successfully executed, but it remains idle, so somehow it gets stuck and no other command can execute. why is this happening?. I think it has something to do with the way I use pipe, dup and fork, so try to approach the problem from these functions. I know you may be arguing that this code doesn't work for other commands with pipes, but I just want to get this particular example to work and to do so I just redirect STDIN to the open file in the first iteration.
int myshell_execute(struct processNode* list, int a)
{
struct processNode* myList = list; // next node to be handled
int pipefd[2];
int in=0;
if (pipe(pipefd) == -1) {
perror("pipe");
myshell_exit(-1);
}
while(myList != NULL)
{
char* program = myList->program; // Get the program to be executed
char ** program_args = myList->program_arguments; // get the programs and arguments to be executed
char ** redirection_string = myList->redirection; //get the part of the command that contains redirection
int *status;
int* stdout;
int stdout_num = 1;
stdout = &stdout_num;
int fileDescriptor;
pid_t pid;
if(strcmp(program,"cd") == 0)
{
return myshell_cd(program_args);
}
else if (strcmp(program,"exit") == 0)
{
return myshell_exit(0);
}
pid = fork();
if(pid == 0)
{
if(in == 1)
{
close(pipefd[1]);
dup2(pipefd[0],0);
close(pipefd[0]);
}
if(sizeOfLine(redirection_string) != 0)
{
redirectionHandler(redirection_string,stdout); // This works. This just handles redirection properly
}
if(*stdout == 1 && myList->next !=NULL)
{
close(pipefd[0]);
dup2(pipefd[1],STDOUT_FILENO); // with this
close(pipefd[1]);
}
if(execvp(program,program_args) !=-1)
{
perror("myshell:");
myshell_exit(-1);
}
else{
myshell_exit(0);
}
}
else if (pid <0)
{
perror("myshell: ");
myshell_exit(-1);
}
else
{
wait(status);
}
in = 1;
myList = myList->next;
}
}
new solution:
int helper_execute(int in, int out, char* program, char ** program_args, char *redirection)
{
pid_t pid;
if ((pid = fork ()) == 0)
{
if (in != 0)
{
dup2 (in, 0);
close (in);
}
if (out != 1)
{
dup2 (out, 1);
close (out);
}
redirectionHandler(redirection);
return execvp (program, program_args);
}
return pid;
}
int myshell_execute(struct processNode* list, int a)
{
int i;
pid_t pid;
int in, fd [2];
struct processNode*new_list = list;
char ** newProgram = new_list->program_arguments;
char ** redirection = new_list->redirection;
char * program = new_list->program;
/* The first process should get its input from the original file descriptor 0. */
in = 0;
/* Note the loop bound, we spawn here all, but the last stage of the pipeline. */
while(new_list->next != NULL)
{
pipe (fd);
/* f [1] is the write end of the pipe, we carry `in` from the prev iteration. */
helper_execute (in, fd [1],program,newProgram,redirection);
/* No need for the write end of the pipe, the child will write here. */
close (fd [1]);
/* Keep the read end of the pipe, the next child will read from there. */
in = fd [0];
new_list = new_list->next;
}
/* Last stage of the pipeline - set stdin be the read end of the previous pipe
and output to the original file descriptor 1. */
if (in != 0)
dup2 (in, 0);
/* Execute the last stage with the current process. */
char* lastProgram = new_list->program;
char ** lastRedirection = new_list->redirection;
char * lastPrArguments = new_list->program_arguments;
redirectionHandler(redirection);
return execvp (lastProgram, lastPrArguments);
}
int main() {
int i=0;
char **input;
struct processNode* list;
int tracker = 0;
while ((input = getline()) != EOF) {
list = create_list(input);
myshell_execute(list,0);
}
return 0;
}
The only problem with this solution is that as soon as one command is executed, the main immediately detects the end of the file, so it exits the shell.
This is because
your parent process also holds the pipe open,
just after forking you call wait. If your first process (cat) fills its output pipe and there is not any process yet available to consume the read end of the pipe then the process stalls forever.
It would look like this:
#define MAX_PIPE_LEN 256
int myshell_execute(struct processNode* list, int a)
{
/* fd0 are the input and output descriptor for this command. fd1
are the input and output descriptor for the next command in the
pipeline. */
int fd0[2] = { STDIN_FILENO, STDOUT_FILENO },
fd1[2] = { -1, -1 };
pid_t pids[MAX_PIPE_LEN] = { 0 };
int pipe_len;
struct processNode* myList; // next node to be handled
int status;
int failed = 0;
for (pipe_len = 0, myList = list;
pipe_len < MAX_PIPE_LEN && myList != NULL;
pipe_len++, myList = myList->next) {
char* program = myList->program; // Get the program to be executed
char ** program_args = myList->program_arguments; // get the programs and arguments to be executed
char ** redirection_string = myList->redirection; //get the part of the command that contains redirection
if(strcmp(program,"cd") == 0) {
return myshell_cd(program_args);
}
else if (strcmp(program,"exit") == 0) {
return myshell_exit(0);
}
if (myList->next != NULL) {
/* The output of this command is piped into the next one */
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("pipe failed");
failed = 1;
break;
}
fd1[0] = pipefd[0];
fd1[1] = fd0[1];
fd0[1] = pipefd[1];
}
pids[pipe_len] = fork();
if (pids[pipe_len] < 0) {
perror("error: fork failed");
failed = 1;
break;
}
if (pids[pipe_len] == 0) {
if (fd0[0] != STDIN_FILENO) {
if (dup2(fd0[0], STDIN_FILENO) == -1) {
perror("error: dup2 input failed");
abort();
}
close(fd0[0]);
}
if (fd0[1] != STDOUT_FILENO) {
if (dup2(fd0[1], STDOUT_FILENO) == -1) {
perror("error: dup2 outut failed");
abort();
}
close(fd0[1]);
}
if (fd1[0] >= 0) {
close(fd1[0]);
}
if (fd1[1] >= 0) {
close(fd1[1]);
}
if(sizeOfLine(redirection_string) != 0) {
redirectionHandler(redirection_string,stdout); // This works. This just handles redirection properly
}
execvp(program, program_args);
perror("error: execvp failed");
abort();
}
if (fd0[0] != STDIN_FILENO) {
close(fd0[0]);
}
if (fd1[1] != STDOUT_FILENO) {
close(fd0[1]);
}
fd0[0] = fd1[0];
fd0[1] = fd1[1];
fd1[0] = fd1[1] = -1;
}
if (myList->next) {
fprintf(stderr, "ERROR: MAX_PIPE_LEN (%d) is too small\n",
MAX_PIPE_LEN);
}
if (fd0[0] >= 0 && fd0[0] != STDIN_FILENO) {
close(fd0[0]);
}
if (fd0[1] >= 0 && fd0[1] != STDOUT_FILENO) {
close(fd0[1]);
}
if (fd1[0] >= 0) {
close(fd1[0]);
}
if (fd1[1] >= 0 && fd1[1] != STDOUT_FILENO) {
close(fd1[1]);
}
/* Now wait for the commands to finish */
int i;
for (i = 0; i < pipe_len; i++) {
if (waitpid(pids[pipe_len - 1], &status, 0) == -1) {
perror("error: waitpid failed");
failed = 1;
}
}
if (failed)
status = -1;
myshell_exit(status);
}
I am trying to implement the GNU popen library but having issues with dup2. I want to duplicate the child pipe to both STDOUT_FILENO and STDERR_FILENO.
I am little confuse here with the dup2, because it says on the man page that dup2 closes the old descriptor, how how can use duplicate pipe onto STDERR_FILENO after doing this dup2(pipe_list[child_fd], child_fd). Because from what i do understand upon a call to dup2 the oldfp will be closed and this dup2(pipe_list[child_fd], STDERR_FILENO) will not work because pipe_list[child_fd] was already closed. Should i use dup and after that close fd or there is a way to achieve this with dup2?
#define tpsr(a,b) (*type == 'r' ? (b) : (a))
#define tpsw(a,b) (*type == 'w' ? (b) : (a))
static FILE *fp_stream = NULL;
static pid_t popen_pid = -1;
static const char * const shell_path = "/bin/sh";
FILE *mypopen(const char *command, const char *type) {
int pipe_list[2];
int parent_fd, child_fd;
if (type == NULL || type[1] != 0) {
errno = EINVAL;
return NULL;
}
if (type[0] != 'w' && type[0] != 'r') {
errno = EINVAL;
return NULL;
}
if (pipe(pipe_list) == -1) {
return NULL; //errno will be set
}
child_fd = tpsr(STDIN_FILENO,STDOUT_FILENO);
parent_fd = tpsw(STDIN_FILENO,STDOUT_FILENO);
/*The above (tpsr and tpsw) are the same as this
if type[0] == 'r'
child_fd = STDOUT_FILENO; //1 child will be doing the writing
parent_fd = STDIN_FILENO; //0 parent read
if type[0] == 'w'
child_fd = STDIN_FILENO; //0 child doing the reading
parent_fd = STDOUT_FILENO;//1 parent do the writing
}*/
if ((popen_pid = fork()) == -1) {
close(pipe_list[0]);
close(pipe_list[1]);
return NULL;
}
if (popen_pid == 0) {
// we got a child here
if (pipe_list[child_fd] != child_fd) {
if (dup2(pipe_list[child_fd], child_fd) == -1) {
(void) close(pipe_list[child_fd]);
_exit(EXIT_FAILURE);
}
//is this the right way after the oldfd is closed by dup2
if (child_fd == STDOUT_FILENO) {
if (dup2(pipe_list[child_fd], STDERR_FILENO) == -1){
(void) close(pipe_list[child_fd]);
_exit(EXIT_FAILURE);
}
}
(void) close(pipe_list[child_fd]);
}
(void) pipe_list[parent_fd];
(void) execl(shell_path, "sh", "-c", command, (char *) NULL);
_exit(EXIT_FAILURE); //exit(127) required by man page
} else {
(void) close(pipe_list[child_fd]);
if ((fp_stream = fdopen(pipe_list[parent_fd], type)) == NULL) {
(void) close(pipe_list[parent_fd]);
return NULL;
}
}
return fp_stream;
}
When you call dup2(fd1, fd2):
If fd1 is not an open file descriptor, the function will return -1 and set errno to EBADF. fd2 is not closed.
If fd2 is outside the allowed range, or if it is not open but the process already has the maximum number of open file descriptors (RLIMIT_NOFILE), the function will return -1 and set errno to EBADF.
If any other problem occurs, the function will return -1 with errno set to some error code. It is not specified whether fd2 is untouched or closed in this case.
The rest of the cases below assume the operation is successful.
If fd1 == fd2, the function will return fd1 (which is the same as d2).
If fd2 is not an open descriptor, then fd1 is duplicated to it. The two descriptors then refer to the same file description (the stuff like file position that the kernel keeps).
If fd2 is an open file descriptor, it gets closed when fd1 is duplicated to it. The closing is silent, meaning that any error due to the closing is silently ignored.
The main point is, that only fd2 may be closed, and only if fd2 != fd1 and if fd2 was previously open. In no case is fd1 closed.
I cannot really follow your logic with regards to your code. In particular, using the parent_fd and client_fd as indexes to pipe_list[] seems suspect to me. Furthermore, the function should return both a file handle, as well as the child process PID.
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
typedef struct {
FILE *child_pipe;
pid_t child_pid;
int exit_status;
} cmd;
/* open() analog, except moves the descriptor to the
number specified. You cannot use O_CREAT flag.
Returns 0 if success, -1 if an error occurred,
with reason in errno. */
static int open_fd(const int fd, const char *path, const int flags)
{
int tempfd;
if (fd == -1 || !path || !*path || (flags & O_CREAT)) {
errno = EINVAL;
return -1;
}
tempfd = open(path, flags);
if (tempfd == -1)
return -1;
if (tempfd != fd) {
if (dup2(tempfd, fd) == -1) {
const int failure = errno;
close(tempfd);
errno = failure;
return -1;
}
if (close(tempfd)) {
const int failure = errno;
close(fd);
errno = failure;
return -1;
}
}
return 0;
}
/* pipe[] analog, except ensures neither endpoint
matches STDIN_FILENO, STDOUT_FILENO, or STDERR_FILENO.
*/
static int create_pipe(int fd[2])
{
/* I like to initialize return parameters.. */
fd[0] = -1;
fd[1] = -1;
if (pipe(fd) == -1)
return -1;
else {
const int close_stdin = (fd[0] == STDIN_FILENO) || (fd[1] == STDIN_FILENO);
const int close_stdout = (fd[0] == STDOUT_FILENO) || (fd[1] == STDOUT_FILENO);
const int close_stderr = (fd[0] == STDERR_FILENO) || (fd[1] == STDERR_FILENO);
int failure = 0;
do {
while (fd[0] == STDIN_FILENO ||
fd[0] == STDOUT_FILENO ||
fd[0] == STDERR_FILENO) {
fd[0] = dup(fd[0]);
if (fd[0] == -1) {
failure = -1;
break;
}
}
if (failure)
break;
while (fd[1] == STDIN_FILENO ||
fd[1] == STDOUT_FILENO ||
fd[1] == STDERR_FILENO) {
fd[1] = dup(fd[1]);
if (fd[1] == -1) {
failure = -1;
break;
}
}
if (failure)
break;
if (close_stdin)
close(STDIN_FILENO);
if (close_stdout)
close(STDOUT_FILENO);
if (close_stderr)
close(STDERR_FILENO);
return 0;
} while (0);
/* Whoops, failed: cleanup time. */
failure = errno;
if (fd[0] != STDIN_FILENO &&
fd[0] != STDOUT_FILENO &&
fd[0] != STDERR_FILENO)
close(fd[0]);
if (fd[1] != STDIN_FILENO &&
fd[1] != STDOUT_FILENO &&
fd[1] != STDERR_FILENO)
close(fd[1]);
if (close_stdin)
close(STDIN_FILENO);
if (close_stdout)
close(STDOUT_FILENO);
if (close_stderr)
close(STDERR_FILENO);
errno = failure;
return -1;
}
}
#define CMD_PASS 0
#define CMD_READ 1
#define CMD_DISCARD 2
int cmd_read(cmd *pipe,
const char *path,
char *args[],
const int stdout_mode,
const int stderr_mode)
{
int pipefd[2], controlfd[2], cause;
FILE *handle;
pid_t child, p;
/* If pipe is not NULL, initialize it. */
if (pipe) {
pipe->child_pipe = NULL;
pipe->child_pid = 0;
pipe->exit_status = 0;
}
/* Verify the parameters make sense. */
if (!path || !args || !pipe ||
stdout_mode < 0 || stdout_mode > 2 ||
stderr_mode < 0 || stderr_mode > 2) {
errno = EINVAL;
return -1;
}
/* Do we need the pipe? */
if (stdout_mode == CMD_READ || stderr_mode == CMD_READ) {
if (create_pipe(pipefd) == -1)
return -1;
} else {
pipefd[0] = -1;
pipefd[1] = -1;
}
/* We use a control pipe to detect exec errors. */
if (create_pipe(controlfd) == -1) {
cause = errno;
if (pipefd[0] != -1)
close(pipefd[0]);
if (pipefd[1] != -1)
close(pipefd[1]);
errno = cause;
return -1;
}
/* Parent reads from the control pipe,
and the child writes to it, but only
if exec fails. We mark the write end
close-on-exec, so the parent notices
if the exec is successful. */
fcntl(controlfd[1], F_SETFD, O_CLOEXEC);
/* Fork the child process. */
child = fork();
if (child == (pid_t)-1) {
cause = errno;
close(controlfd[0]);
close(controlfd[1]);
if (pipefd[0] != -1)
close(pipefd[0]);
if (pipefd[1] != -1)
close(pipefd[1]);
errno = cause;
return -1;
}
if (!child) {
/* This is the child process. */
close(controlfd[0]);
if (pipefd[0] != -1)
close(pipefd[0]);
cause = 0;
do {
if (stdout_mode == CMD_READ) {
if (dup2(pipefd[1], STDOUT_FILENO) == -1) {
cause = -1;
break;
}
} else
if (stdout_mode == CMD_DISCARD) {
if (open_fd(STDOUT_FILENO, "/dev/null", O_WRONLY) == -1) {
cause = -1;
break;
}
}
if (cause)
break;
if (stderr_mode == CMD_READ) {
if (dup2(pipefd[1], STDERR_FILENO) == -1) {
cause = -1;
break;
}
} else
if (stderr_mode == CMD_DISCARD) {
if (open_fd(STDERR_FILENO, "/dev/null", O_WRONLY) == -1) {
cause = -1;
break;
}
}
if (cause)
break;
if (pipefd[1] != -1)
close(pipefd[1]);
if (path[0] == '/')
execv(path, (char *const *)args);
else
execvp(path, (char *const *)args);
/* Failed. */
} while (0);
/* Tell parent, why. */
cause = errno;
/* To silence the warn_unused_result warning: */
if (write(controlfd[1], &cause, sizeof cause))
;
close(controlfd[1]);
exit(127);
}
/* Parent process. */
close(controlfd[1]);
if (pipefd[1] != -1)
close(pipefd[1]);
do {
ssize_t n;
/* Read from the pipe to see if exec failed. */
n = read(controlfd[0], &cause, sizeof cause);
if (n == (ssize_t)sizeof cause)
break;
if (n != 0) {
cause = EIO;
kill(child, SIGKILL);
break;
}
close(controlfd[0]);
if (pipefd[0] != -1) {
handle = fdopen(pipefd[0], "r");
if (!handle) {
cause = errno;
kill(child, SIGKILL);
break;
}
} else
handle = NULL;
/* Success! */
pipe->child_pipe = handle;
pipe->child_pid = child;
return 0;
} while (0);
/* Failed; reason is in cause. */
if (pipefd[0] != -1)
close(pipefd[0]);
/* Reap child. */
while (1) {
p = waitpid(child, NULL, 0);
if ((p == child) || (p == (pid_t)-1 && errno != EINTR))
break;
}
errno = cause;
return -1;
}
int cmd_wait(cmd *pipe)
{
pid_t p;
if (!pipe || pipe->child_pid == -1)
return errno = EINVAL;
while (1) {
p = waitpid(pipe->child_pid, &(pipe->exit_status), 0);
if (p == pipe->child_pid) {
if (pipe->child_pipe)
fclose(pipe->child_pipe);
pipe->child_pipe = NULL;
pipe->child_pid = 0;
return 0;
} else
if (p != -1) {
if (pipe->child_pipe)
fclose(pipe->child_pipe);
pipe->child_pipe = NULL;
pipe->child_pid = 0;
errno = EIO;
return -1;
} else
if (errno != EINTR) {
const int cause = errno;
if (pipe->child_pipe)
fclose(pipe->child_pipe);
pipe->child_pipe = NULL;
pipe->child_pid = 0;
errno = cause;
return -1;
}
}
}
int main(int argc, char *argv[])
{
off_t total = 0;
char *line = NULL;
size_t size = 0;
ssize_t len;
int stdout_mode, stderr_mode;
cmd run;
if (argc < 3 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
printf("\n");
printf("Usage:\n");
printf(" %s [ -h | --help ]\n", argv[0]);
printf(" %s MODE COMMAND [ ARGS ... ]\n", argv[0]);
printf("Where MODE may contain:\n");
printf(" o to retain output,\n");
printf(" O to discard/hide output,\n");
printf(" e to retain errors, and\n");
printf(" E to discard/hide errors.\n");
printf("All other characters are ignored.\n");
printf("If there is no 'o' or 'O' in MODE, then output\n");
printf("is passed through to standard output; similarly,\n");
printf("if there is no 'e' or 'E' in MODE, then errors\n");
printf("are passed through to standard error.\n");
printf("\n");
return EXIT_SUCCESS;
}
if (strchr(argv[1], 'o'))
stdout_mode = CMD_READ;
else
if (strchr(argv[1], 'O'))
stdout_mode = CMD_DISCARD;
else
stdout_mode = CMD_PASS;
if (strchr(argv[1], 'e'))
stderr_mode = CMD_READ;
else
if (strchr(argv[1], 'E'))
stderr_mode = CMD_DISCARD;
else
stderr_mode = CMD_PASS;
if (cmd_read(&run, argv[2], argv + 2, stdout_mode, stderr_mode) == -1) {
fprintf(stderr, "%s: %s.\n", argv[2], strerror(errno));
return EXIT_FAILURE;
}
if (run.child_pipe) {
while (1) {
len = getline(&line, &size, run.child_pipe);
if (len == -1)
break;
total += (off_t)len;
#ifdef PRINT_PIPE_CONTENTS
if (len > 0)
fwrite(line, (size_t)len, 1, stdout);
#endif
}
if (ferror(run.child_pipe) || !feof(run.child_pipe)) {
fprintf(stderr, "%s: Error reading from pipe.\n", argv[2]);
return EXIT_FAILURE;
}
}
if (cmd_wait(&run) == -1) {
fprintf(stderr, "%s: Lost child process: %s.\n", argv[2], strerror(errno));
return EXIT_FAILURE;
}
fprintf(stderr, "Read %llu bytes from child process.\n", (unsigned long long)total);
if (WIFEXITED(run.exit_status))
fprintf(stderr, "Child exited with status %d.\n", WEXITSTATUS(run.exit_status));
else
if (WIFSIGNALED(run.exit_status))
fprintf(stderr, "Child terminated due to signal %d.\n", WTERMSIG(run.exit_status));
else
fprintf(stderr, "Child lost.\n");
return EXIT_SUCCESS;
}
If you save the above as pipe_example.c, you can compile it using e.g.
gcc -Wall -O2 pipe_example.c -o pipe_ex
and test it by running e.g.
./pipe_ex oe sh -c 'printf "OUT\n"; printf "ERR\n" >&2; exit 5'
./pipe_ex Oe sh -c 'printf "OUT\n"; printf "ERR\n" >&2;'
./pipe_ex -E sh -c 'printf "OUT\n"; printf "ERR\n" >&2; kill -TERM $$'
The above code is, as you can obviously see, quite complicated -- and overall, it is just a popen() analog, but with fewer features.
However, it does everything quite carefully (except perhaps passing the reason the exec failed from the child to the parent -- there I am simply assuming the write succeeds). As such, it might be useful as an example of how many details (and at least one way of dealing with such details) there are in practical multiprocess programs. And, of course, how useful standard POSIX.1 popen() is.
A bit more detailed example with stdout and stderr at the same time.
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main(int argc, char *argv[]) {
int fdout[2]; /* pipe fd array for the stdout */
int fderr[2]; /* pipe fd array for the stderr */
pid_t pid; /* process id */
char buf[4096]; /* stdout and stderr buffer */
int status; /* place to store child process status */
if (pipe(fdout)==-1) /* first pipe for the stdout */
perror("pipe out"); /* exit with error if something wrong */
if (pipe(fderr)==-1) /* second pipe for the stderr */
perror("pipe err");
/*
* On success, the PID of the child process is returned in the
* parent, and 0 is returned in the child. On failure, -1 is
* returned in the parent, no child process is created, and errno is
* set to indicate the error.
*/
if ((pid = fork()) == -1) /* fork current process and store pid */
perror("fork error");
if(pid == 0) { /* if pid == 0 than this is a child process */
dup2 (fdout[1], STDOUT_FILENO); /* send stdout to the pipe fdout */
dup2 (fderr[1], STDERR_FILENO); /* send stderr to the pipe fderr */
/* close both sides of the pipe in the child */
close(fdout[0]);
close(fdout[1]);
/* close both sides of the pipe in the child */
close(fderr[0]);
close(fderr[1]);
/* execvp is called in the child ps
* argv[1] is a ptr to the first arg
* &argv[1] is a ptr to a ptr to the first arg
*/
execvp(argv[1], &argv[1]);
/* this part is never reached if execvp success */
return errno;
} else { /* parent */
while(wait(&status) > 0){} /* wait for the child processes to finish */
/* close write ends of the pipes */
close(fdout[1]);
close(fderr[1]);
/* if child process finished with errorprint error
* and return error code from parent process
*/
if (WIFEXITED(status) && WEXITSTATUS(status)){
printf("%s\n", strerror(WEXITSTATUS(status)));
return WEXITSTATUS(status);
}
ssize_t size;
size = read(fdout[0], buf, sizeof(buf));
printf("OUT: \"%.*s\"\n", (int)size, buf);
size = read(fderr[0], buf, sizeof(buf));
printf("ERR: \"%.*s\"\n", (int)size, buf);
/* close read ends of the pipes */
close(fdout[0]);
close(fderr[0]);
}
return 0;
}
compile and test:
ruslan#localhost:~/exec$ gcc main.c -o a && ./a /bin/ls
OUT: "a
main.c
"
ERR: ""
ruslan#localhost:~/exec$ gcc main.c -o a && ./a /bin/ls ---sdfadsfsdf
OUT: ""
ERR: "/bin/ls: unrecognized option '---sdfadsfsdf'
Try '/bin/ls --help' for more information.
"
ruslan#localhost:~/exec$
I am building a simple shell in c using fork and execlp. I will be given a set of commands separated by pipes. eg: ls -l | wc -l . I am trying to implement a simple shell program in c using. For some strange reason, I am getting a broken pipe error.
void excueteCommand(commandNode* head, int input) {
int pfds[2] = { -1, -1 };
if (head->next != NULL) {
pipe(pfds);
}
if (fork() == 0) { /* child */
if (input != -1) {
dup2(input, 0);
close(input);
}
if (pfds[1] != -1) {
dup2(pfds[1], 1);
close(pfds[1]);
}
if (pfds[0] != -1) {
close(pfds[0]);
}
execlp(head->command, head->args, NULL);
exit(1);
}
else { /* parent */
if (input != -1) {
close(input);
}
if (pfds[1] != -1) {
close(pfds[1]);
}
if (head->next != NULL) {
thePipenizer(head->next, pfds[0]);
}
}
}
Hi i don't know what index is. I made a simple prog that work to show you some mistake that you make.
int main()
{
int pfds[2] = { -1, -1 };
int input;
char *argvv = "";
char *tab = malloc(500);
int i;
int nbRead;
int stat;
input = dup(0);
if (pipe(pfds) == -1)
exit(-1);
if (fork() == 0)
{ /* child */
dup2(pfds[1], 1);
close(pfds[0]);
execlp("ls", argvv, NULL);
exit(-1);
}
else
{ /* parent */
close(pfds[1]);
wait(&stat);
do
{
nbRead = read(pfds[0], tab, 500);
for (i = 0; i < nbRead; i++)
printf("%c", tab[i]);
} while (nbRead > 0);
close(pfds[0]);
}
return (0);
}
First : Instead of regarding pfds[0] and pfds[1] value, look at the return value of the function pipe. Function pipe return -1 in case of error (see man pipe).
Second : Don't forget to close all open fd, it's important for the system.
If you can told us more about
input
maybe we can found your problem.
In the parent, try resetting pfds. That is:
if( pfds[1] != -1 ) {
close( pfds[1] );
pfds[1] = -1; /* Add this line */
}
By leaving it set to its previous value, it is very likely that subsequent children are closing file descriptors that you do not expect them to be closing.