command hangs on read end of pipe - c

I've doing custom shell for an assignment, and I wanted to implement the pseudo code from emphemient here for multiple piping. However, my code is still hanging on the read end of the pipe (in my test command, I do echo hello | wc and it's hanging on wc).
Is my implementation incorrect based on the pseudocode? I think I close the pipes properly
void piped()
{
// test command
char args[] = "echo hello | wc";
int status;
pid_t pid;
int cmdsRun = 0;
int newfds[2];
int oldfds[2];
char *nextCmd;
char *prevCmd = NULL;
char *cmdToken;
char *cmdSavePter;
cmdToken = strtok_r(args, "|", &cmdSavePter);
while (cmdToken)
{
nextCmd = strtok_r(NULL, "|", &cmdSavePter);
fprintf(stderr, "cmdToken: %s, nextCmd: %s, prev: %s\n", cmdToken, nextCmd, prevCmd);
struct command *commands = parseW(cmdToken);
// if next command make new pipes
if (nextCmd)
pipe(newfds);
// fork
pid = fork();
if (pid == 0) // child
{
// if there was prev cmd
if (prevCmd)
{
dup2(oldfds[0], STDIN_FILENO);
close(oldfds[0]);
close(oldfds[1]);
}
// if next cmd
if (nextCmd)
{
close(newfds[0]);
dup2(newfds[1], STDOUT_FILENO);
close(newfds[1]);
}
// this function simply execvps the commands
runSimple(commands);
}
else if (pid < 0)
{
perror("error");
exit(EXIT_FAILURE);
}
else // parent
{
// if there was a prev command
if (prevCmd)
{
close(oldfds[0]);
close(oldfds[1]);
}
// if next command
if (nextCmd)
{
memcpy(oldfds, newfds, sizeof(oldfds));
}
}
waitpid(pid, &status, 0);
prevCmd = cmdToken;
cmdToken = nextCmd;
cmdsRun++;
}
// parent: if multiple cmds, close
if (cmdsRun > 1)
{
close(oldfds[0]);
close(oldfds[1]);
}
}

Related

How to prevent background jobs from printing next to prompt?

I am trying to implement my own shell and am experimenting with background jobs with using WNOHANG option in waitpid. But whenever I run a command in shell it just prints the output next to my prompt like this:
user#hostmachine:/.../$: [output]
Where am I going wrong?
`
while (1) {
int childPid, status;
// Display prompt and read input
char *buffer = print_prompt();
char *input = readline(buffer);
check_and_free(buffer)
// add input to readline history.
add_history(input);
time_t t;
time(&t);
add_history_time(ctime(&t));
// Check for EOF.
if (!input)
break;
parseInfo *result = parse(input);
if (result == NULL) {
goto free;
}
print_info(result);
commandType *input_command = &result->CommArray[0];
// execute builtin command in parent process
int commType = isBuiltInCommand(input_command->command);
if (commType == EXIT) {
free_info(result);
check_and_free(input)
exit(0);
}
else if (commType != NO_SUCH_BUILTIN) {
executeBuiltInCommand(input_command, commType, history_get_history_state());
} else {
// create a child process to execute command
childPid = fork();
if (childPid == 0) {
// calls execvp
printf("Executing child process...\n\n");
executeCommand(input_command, result);
} else {
waitpid(childPid, &status, WNOHANG);
if (status != 0) {
printf("Error! Child exited with error code %d\n", WEXITSTATUS(status));
}
}
}
// Free buffer that was allocated by readline
free:
free_info(result);
check_and_free(input)
}
return 0;
}
`
I tried to execute a job in background and for quick ones like "ls" it just prints the output next to my prompt!

Creating Shell in C - Piping and External Commands

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();

Why does wc command in execvp() return the wrong results when taking input from a pipe in my shell?

I'm working on writing a shell in C for learning purposes and I'm trying to allow for a variable number of pipes. In general, it seems to work great. But I noticed a problem with the wc command.
When I pipe some output of another program into wc like ls | wc it always returns
1 3 35 no matter what I pipe into it. Other commands work as expected when I pipe into them. In my normal zsh shell wc works fine. I'm struggling to find the problem. I've tried adding waitpid after the forks but no dice.
Here's the main shell loop in the main function:
while (1) {
printf("\033[31;1mshell:\033[0m ");
line = read_cmds();
if (strstr(line, "|")) {
// check for pipes first
pipe_exec(line);
} else {
// we have a single command
tokens = split(line, " \t\r\n");
if (*tokens != NULL) shell_exec(tokens);
free(tokens);
}
}
Here is the function that loops through the commands:
void pipe_exec(char *line)
{
int in, status;
int pipe_no; // keep track of ptr to bring it back to free
int pfd[2];
pid_t rc;
char **cmd, **pipe_cmds;
// split takes a string and splits into array of strings based on delimiter
pipe_cmds = split(line, "|");
in = 0;
pipe_no = 0;
while (*pipe_cmds) {
cmd = split(*pipe_cmds, " \t\r\n");
if (pipe(pfd) < 0) perror("pipe");
make_proc(in, pfd[1], cmd);
close(pfd[1]);
in = pfd[0];
pipe_cmds++; // move ptr ahead one
pipe_no++;
}
// move pointer back and free
pipe_cmds -= pipe_no;
free(pipe_cmds);
rc = fork();
if (rc == 0) {
if (in != 0) dup2(in, STDIN_FILENO);
execvp(*cmd, cmd);
}
}
And then the make_proc function that the above function calls:
void make_proc(int in, int out, char **cmd)
{
pid_t rc;
rc = fork();
if (rc == 0) {
if (in != STDIN_FILENO) {
dup2(in, STDIN_FILENO);
close(in);
}
if (out != STDOUT_FILENO) {
dup2(out, STDOUT_FILENO);
close(out);
}
execvp(*cmd, cmd);
}
}
I took out some of the error checking to save space here.
Any help is appreciated!
You execute the last command twice and pipe its first instance to the second. Adding something like:
while (*pipe_cmds) {
cmd = split(*pipe_cmds, " \t\r\n");
if (!pipe_cmds[1]) {
break;
}
if (pipe(pfd) < 0) perror("pipe");
make_proc(in, pfd[1], cmd);
close(pfd[1]);
in = pfd[0];
pipe_cmds++; // move ptr ahead one
pipe_no++;
}
would prevent the unnecessary instance, although I would rather have refactored this function a bit.

Creating a shell in C that handles redirection and pipes

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);
}

Creating multiple pipe in C

I'm writing a simple shell and I'd like to change my program to add the possibility of multiple pipe commands like "echo foo | cat | cat | cat | cat | wc". I have written for two commands but for multiple i can't.
Here is the source code of my program:
if (pid == 0) // in the child process
{
for (i = 0; i < command; i++) // for each cmd
{
if (argv[i][0] == '|')
{
j = i;
}
}
if (j > 0)
{
if (pipe(p))
{
fprintf(stderr, "pipe");
exit(1);
}
argv[j] = NULL;
if (fork() == 0) // child
{
j = -1;
close(p[0]);
dup2(p[1],1);
close(p[1]);
}
// parent
close(p[1]);
dup2(p[0], 0);
close(p[0]);
}
for (i = 0; dirs[i] != 0; i++)
{
snprintf(pathname, sizeof(pathname), "%s/%s", dirs[i], argv[j+1]);
execv(pathname, &argv[j+1]);
}
}
else
{
while (wait(0) != pid) // parent: wait child
}
Thank you in advance for help.
I come with an example of what you are trying to do. I use constants as commands, I leave the command line parsing to you.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
static char *my_command0[] = {"cat", "stackoverflow.c", NULL};
static char *my_command1[] = {"grep", "return", NULL};
static char *my_command2[] = {"sed", "s/^ *//g", NULL};
static char **const my_commands[] = {
my_command0,
my_command1,
my_command2,
NULL
};
int create_sub_process(char *const command[], int input_stream)
{
int pipefd[2] = {-1, -1};
pid_t fk;
if (pipe(pipefd) < 0)
{
perror("pipe");
close(input_stream);
return -1;
}
if ((fk = fork()) < 0)
{
perror("fork");
close(pipefd[0]);
close(pipefd[1]);
close(input_stream);
return -1;
}
if (fk == 0)
{
close(pipefd[0]);
close(0);
dup(input_stream);
close(input_stream);
close(1);
dup(pipefd[1]);
close(pipefd[1]);
execvp(command[0], command);
perror("execvp");
exit(1);
}
close(input_stream);
close(pipefd[1]);
return pipefd[0];
}
int main()
{
int fd = dup(0);
for (int i = 0; my_commands[i] != NULL; i++)
{
fd = create_sub_process(my_commands[i], fd); // replace my_commands[i] by whatever you need to execute - check: man execvp
if (fd < 0)
{
exit(1);
}
}
// Also adapt the following lines to whatever you want to do with last child results
close(0);
dup(fd);
close(fd);
execlp("cat", "cat", (char *)NULL);
perror("execlp");
return 1;
}
create_sub_process() creates a pipe and creates a sub process to execute given command, taking inputs from given input stream and sending output to the stream it returns to parent.

Resources