Problems with piping for basic shell program - c

I've compared this to previous posts involving piping and I can't seem to find the problem. Everything in the parent seems to be closed as it should. It works fine when I type in a valid command (ex "ls | grep a) but if it is not a valid command (ex "ls | grup a) the program stops responding to user input (it keeps running but it just doesn't do anything when you enter a command)
Main function:
int main() {
int i;
char **args;
int pipeCheck = 0;
int argCount = -1;
int blank = 0;
while(1) {
args = getln();
if (args[0] != NULL){
blank = 1;
if (strcmp(args[0],"exit")==0) exit(0);
}
for(i = 0; args[i] != NULL; i++) {
if (strcmp(args[i], "|")==0){
pipeCheck = i;
}
}
if (pipeCheck != 0){
args[pipeCheck] = NULL;
directPipe(args, pipeCheck, argCount, ampCheck);
}
}
}
This is the function for piping in my program:
int directPipe(char ** args, int fileNumber, int argCount,int ampCheck){
int fd[2];
int child1,child2;
int status;
int i;
char * piped[10000];
int count = 0;
for (i = (fileNumber+1); args[i] != NULL; i++){
piped[count] = args[i];
count++;
}
piped[count] = NULL;
printf("\nPipe attempted...\n");
pipe(fd);
child1 = fork();
if (child1==0){
close(1);
dup(fd[1]);
close(fd[0]);
close(fd[1]);
execvp(args[0], args);
printf("Unknown command, please try again.");
exit(0);
}
child2 = fork();
if (child2 ==0){
close(0);
close(fd[1]);
dup(fd[0]);
close(fd[0]);
execvp(piped[0], piped);
printf("Unknown command, please try again.");
exit(0);
}
close(fd[1]);
close(fd[0]);
if (ampCheck == 0){
while (wait(&status) != child1);
while (wait(&status) != child2);
}
else{
printf("\nampCheck = %d",ampCheck);
sigset(child2, printer());
}
return (0);
}

Your problem is the pair of wait() loops:
while (wait(&status) != child1);
while (wait(&status) != child2);
In your scenario, the second child dies before the first does, so your collect its corpse in the first loop, but ignore it. Then the second loop goes into a busy wait because there are no children left any more.
At minimum, you need to do:
int corpse;
while ((corpse = wait(&status)) != -1 && corpse != child1 && corpse != child2)
;
while ((corpse = wait(&status)) != -1 && corpse != child1 && corpse != child2)
;
This handles children dying in either order — but only the two children. For a more general pipeline (three or more processes), you have to work harder — and use a single loop. The more general form will be something like:
int corpse;
while ((corpse = wait(&status)) != -1)
{
if (record_death_of_child(corpse, status) == -1)
break;
}
where your process creation code records the PIDs of the created processes, and the record_death_of_child() code deals with that list of PIDS and returns -1 when there are no more children to wait for in the current pipeline (and 0 otherwise). Or you can have it use some other heuristic to determine when to exit the loop. Note that if you have long running jobs in the background, any of them could die and that corpse would be collected in the loop. The 'record death' function would need to deal with such processes too — they can no longer be brought into the foreground, for example, and you need to report that they exited, etc.
You might end up using waitpid(), too, since you can arrange for that to not hang while there's a background process that's still running using WNOHANG.

Related

Value modification inside child doesn't update

Im trying to modify an int inside a child process when fork(); but I cant get the program to update it more than once, my idea is to add a counter that displays to screen that shows how many bg where (when & is at the end of the line), but I don't know why it's not working. Now I'm trying this but it doesn't work either, maybe I should modify the value in the parent process? This it the code:
void execute(char **tokens, int token_Size, int *blk){
pid_t pid, wpid;
int status;
int result;
int flag;
int isPipe;
int output;
int input;
int isAmper;
pid = fork();
if (pid == -1)
{
perror("Fork:");
exit(1);
}
else if (pid == 0)
{
isAmper = needs_amper(tokens, token_Size);
output = needs_out_redir(tokens, token_Size);
input = needs_in_redir(tokens, token_Size);
isPipe = needs_pipe(tokens, token_Size);
static int bloq = 1;
if (isAmper != -1)
{
*blk +=1;
printf("[%d] %d \n", *blk, getppid()); //-> [blk] is the job number asigned to the job
tokens[isAmper] = NULL;
}
if (strcmp(tokens[0], "echo") == 0)
{
for (int i = 1; tokens[i]; i++)
{
printf("%s ", tokens[i]);
}
}
flag = 0;
if (output != -1)
{
redirect_output(tokens, output);
tokens[output] = NULL;
flag = 1;
}
if (input != -1)
{
redirect_input(tokens, input);
tokens[input] = NULL;
flag = 1;
}
if (isPipe != -1)
{
create_pipe(tokens, output, input, isPipe);
}
if (flag || isPipe == -1)
{
execvp(tokens[0], tokens);
perror("Unkown Command:");
exit(1);
}
// exit(0);
}
else // Main (parent) process after fork succeeds
{
while ((wpid = wait(&status)) > 0)
; // this way, the father waits for all the child processes
result = waitpid(pid, &status, 0);
if (result == 1) //If the process terminated correctly, result returns 0.
{
printf("The child process terminated with an error!.\n");
}
}}
I'm trying to modify the blk value passed to the execute function, I tried using an inside value also, but doesnt work either.
To make my self clearer. I want that when I type in my custom shell something with an '&' at the end it return something like this:
ivo#ivo-Surface-Pro-6:/home/ivo/Documents/SO1/soi-myshell-Ivoo25$ echo hola &
[1] 10853
hola
And the next time I type something with the & at the end..
[2] 10853
main.c main.o Makefile myshell myShell.c myShell.h myShell.o README.md
I think that what Im trying to do cant be done because the pid is the same in every execution, maybe is that?
I managed to get it, I added the same function
needs_amper(tokens, token_Size)
but outside the child process, before the fork inside an if function veryfing that it returns a value higher than 0

why after the exit(0) is called, but the child process still remains?

in the parent process I want to start a daemon to do a long time work, so I use
the double fork() to create the grandchild process to start the daemon. The question is
every once in a while, the child process does not exit successfully, I am sure that
the printf() before the exit(0) is called, but when I use the "ps" I can see the child process
pid listed, and it never exit. Do you know why this happened, should I call _exit(0) instead of
exit(0) to make the child process exit?
startProcess(const char *pidfile, const char *command)
pid_t pid;
char cmd[1024];
char *head = cmd;
char *end;
char *argv[128] = {0};
int argc = 0;
if( 0 == (pid=fork()) ) { //fork a child
if( 0 > setpriority(PRIO_PROCESS, 0, 0) ) {
perror();
return;
}
pid = fork(); //fork a grandchild
if(-1 == pid){
exit(-1);
}else if(0 == pid){ //grandchild process
if (snprintf(cmd, sizeof(cmd), "%s", command) <= sizeof(cmd) - 1) {
/* change command line to argc/argv format */
do {
if (argc >= sizeof(argv)/sizeof(argv[0]) - 1)
break;
for (; (*head == ' ' || *head == '\t'); head++); //remove space or TAB in the header
if (*head == '\0')
break;
if (NULL != (end = strpbrk(head, " \t"))) {
*end = '\0';
argv[argc++] = head;
head = end + 1;
} else {
argv[argc++] = head;
break;
}
} while (1);
argv[argc] = NULL; // Maximal value of argc is ARRAY_SIZE(argv) - 1.
/* execute command to start a daemon*/
execvp(argv[0], argv);
}
//should not enter here
exit(-1);
}else{ //still in child process
printf("child process exit\n");
exit(0); //child process exit, but sometimes this call seems fail
}
}else if(-1 == pid){
return;
}else{ //parent process
int status = 0;
int rv
pid_t r_pid = waitpid(pid, &status, WNOHANG);
if (pid == r_pid) {
if (WIFEXITED(status) && (0 == WEXITSTATUS(status))) {
rv = 0;
} else {
rv = -1;
}
}
return rv;
}
Using WNOHANG instead of 0 is surely wrong. If the parent calls waitpid (with WNOHANG) before the child exits, the child will remain in existence as a zombie waiting to be reaped until the parent exits too.

n-pipeline producing EOF on end

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
void tokenizer(char* input, char** output) { //My tokenizer
char* input_dup = strdup(input);
output[0] = strtok(input_dup, " ");
int i = 1;
while ((output[i] = strtok(NULL, " ")) != NULL) {
i++;
}
}
void run_command(char** args, int* fd) { //no pipe
pid_t pid = fork();
if (pid < 0) {
printf("Forking failed...\n");
}
else if (pid == 0) {
close(fd[0]);
if (fd[1] != 1)
dup2(fd[1], 1);
execvp(args[0], args);
printf("Command failed...\n");
exit(1);
}
else {
close(fd[1]);
wait(pid);
char buff[1];
while (read(fd[0], buff, 1) > 0) {
if (buff[0] == EOF || buff[0] == '\0') {
printf("Caught something, returning out...");
return;
}
else {
printf("%c", buff[0]);
}
}
}
}
//pipeline function
void run_pipe(char** args, int* fd) {
pid_t pid = fork();
if (pid < 0) {
printf("Forking failed...\n");
}
else if (pid == 0) {
if (fd[1] != 1) {
dup2(fd[1], 1);
}
execvp(args[0], args);
printf("Command failed...\n");
exit(1);
}
else {
close(fd[1]);
if (fd[0] != 0) {
dup2(fd[0], 0);
}
wait(pid);
}
}
int main(int argc, char** argv) {
printf ("Starting myshell (mysh) \n..\n..\n");
while (1) {
char cwd[1024];
printf ("mysh :: %s -> ", getcwd(cwd, sizeof(cwd)));
char ch[1024];
memset(ch, 0, 1023); //for cleanup
char c = 0;
int i = 0;
while (c != '\n') {
c = getchar();
if (c == EOF) {
printf ("EOF Received, exiting...\n");
return 0;
}
if (c != '\n')
ch[i] = c;
i++;
}
if (ch[0] != '\0') {
char* tokens[128];
tokenizer(ch, tokens);
//first check for keywords
if (strcmp(tokens[0], "cd") == 0) {
if (chdir(tokens[1]) < 0) {
printf("ERROR: Directory %s does not exist\n", tokens[1]);
}
}
else if (strcmp(tokens[0], "exit") == 0) {
printf("Leaving shell...\n");
return 0;
}
else {
char* commands[50];
memset(commands, 0, sizeof(commands));
int j = 0;
int k = 0;
int fd[2];
//try something different...
while (tokens[j] != NULL) {
if (strcmp(tokens[j], "|") == 0) {
commands[k] = NULL;
pipe(fd);
run_pipe(commands, fd);
j++;
k = 0;
}
//more cases here
else { //nothing special
commands[k] = tokens[j];
j++;
k++;
}
}
commands[k] = NULL;
pipe(fd);
run_command(commands, fd);
}
}
}
}
The above code is meant to simulate a shell. It handles single commands and it handles the pipelining properly (i.e. ps | sort | wc is returning the correct output) however when the pipelining is done it returns an EOF which is caught by the condition in the loop with getchar(). If I try to ignore this EOF it segfaults. Am I leaving a pipe open somewhere and stdin is getting flooded? Any help is appreciated.
Compilation fixes
You need to add #include <sys/wait.h> and then fix the calls to wait(). I used (twice):
int status;
int corpse = wait(&status);
printf("PID %d status 0x%.4X\n", corpse, status);
Arguably, that should be a loop looking for a specific PID, or you should use waitpid() instead. While debugging a shell, you want to know about every PID that exits and its status.
I ran `ps | wc' and got:
Starting myshell (mysh)
..
..
mysh :: /usr/local/google/home/jleffler/soq -> ps | wc
PID 25960 status 0x0000
PID 25961 status 0x0000
4 16 117
mysh :: /usr/local/google/home/jleffler/soq -> EOF Received, exiting...
If you mean "the code should have continued instead of getting EOF", then there's some more work to do.
Tangential issues
I note the line:
if (buff[0] == EOF || buff[0] == '\0')
The character in buff[0] is from a read() call. It will never be EOF meaningfully; EOF is distinct from every character (hence getchar() returns an int). This becomes significant later:
char c = 0;
while (c != '\n')
{
c = getchar();
if (c == EOF)
Since c is a char, you cannot reliably compare it with EOF. You must store the result of getchar() in an int.
I'm not yet convinced these are the cause of the trouble, but you must be careful.
Probable cause
I think the trouble is in run_pipe() in the parent code (as amended):
else
{
close(fd[1]);
if (fd[0] != 0)
{
dup2(fd[0], 0);
}
int status;
int corpse = wait(&status);
printf("PID %d status 0x%.4X\n", corpse, status);
}
The fd[0] != 0 condition will always be true (very improbable that it will be false), so you then change your shell's input to read from fd[0]. You should review that; it means that you're reading standard input from the read end of the pipe to the child. That's bad; you've lost your original input!
Your code also seems to have the parent waiting for the child to die, and then reads the pipe and echoes to standard output. This is not a good idea; it is better to make the child (last child in the pipeline) write to the standard output directly. There are two reasons for this:
The child might write more data than fits in a pipe, so it will block waiting for something to read its output, but the reader will be blocked waiting for the child to die, so you'll have a deadlock.
It slows things up, and output from the child may well be buffered instead of appearing timely on the terminal.
I'm a little sceptical about how a three-part pipeline would be handled. You need two pipes created before you run the middle process of the three; I don't see that in your code.
I was able to fix this issue. It's probably not the right way to do it, but I saved a copy of stdin and used dup2 to reset it when the pipelining was finished.
int in_bak = dup(0);
//stuff
dup2(in_bak, 0);
close(in_bak);

Why isn't my piped grep working?

I've written my own shell in C, and when I run ls | grep .c, I get nothing. Although unpiped commands are working fine, like ls. Here's my code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include "shell.h"
#include "builtins.h"
#include "makeargv.h"
void shell()
{
pid_t shell_pid;
int i;
int flag = 1;
int argc0;
int argc1;
int fdl[2];
int fdr[2];
size_t input_size;
char cwd[128]; //this is being toggled
char *delim0;
char *delim1;
char *lastarg;
char *input;
char *debugdescriptor;
char **argvp;
char **firstargs;
shell_pid = getpid();
do
{
// Retrieve PID & CWD of the parent process.
getcwd(cwd, (128 * sizeof(char)));
printf("{%i}%s$ ", shell_pid, cwd);
// Retrieve input from stdin.
input = NULL;
input_size = 0;
getline(&input, &input_size, stdin);
//seperates the input into pipe-delimited arguments("tokens")
delim1 = "|\n";
argc1 = makeargv(input, delim1, &argvp);
//got some debugging tools here
//debugdescriptor = "PIPE-SEPERATED";
//debug_args(&argvp, &argc1, debugdescriptor);
//check for quit and cd first
delim0 = " ";
argc0 = makeargv(argvp[0], delim0, &firstargs);
//more debugging tools here
//debugdescriptor = "FIRST ARGS";
//debug_args(&firstargs, &argc0, debugdescriptor);
//exit
if((i = strcmp(firstargs[0],"exit")) == 0 || (i = strcmp(firstargs[0],"quit")) == 0)
{
printf("===========SHELL TERMINATED==============\n\n");
flag = 0;
}
//cd
else if((i = strcmp(firstargs[0],"cd")) == 0)
{
chdir(firstargs[1]);
}
else // Create a child process to handle user input.
{
char **thisarg;
int childlayer = 0;
pid_t pid = fork();
wait(0);
if(pid == 0)
childlayer++;
int tokens = argc1 - 1;
if(argc1 == 1 && pid == 0)
{
makeargv(argvp[tokens], delim0, &thisarg);
execvp(thisarg[0], thisarg);
}
else //more than 1 arguement, (has pipes)
{
while(pid == 0 && childlayer < argc1){
if(childlayer == 1){ //rightmost
pipe(fdl);
pid = fork();
wait(0);
if(pid == 0)
childlayer++;
if(pid > 0){
close(fdl[1]);
dup2(fdl[0], STDIN_FILENO); //sets the final output to write to STDIN
execute(childlayer, argc1, &argvp);
}
}
else if(childlayer > 1 && childlayer < argc1-1){ //middle args
pipe(fdr);
fdr[1] = fdl[1];
fdr[0] = fdl[0];
dup2(fdr[1], STDOUT_FILENO);
pipe(fdl);
pid = fork();
wait(0);
if(pid == 0)
childlayer++;
if(pid > 0){
close(fdl[1]);
dup2(fdl[0], STDIN_FILENO);
execute(childlayer, argc1, &argvp);
}
}
else{ //leftmost
pipe(fdr);
fdr[0] = fdl[0];
fdr[1] = fdl[1];
close(fdr[0]);
dup2(fdr[1], STDOUT_FILENO);
execute(childlayer, argc1, &argvp);
}
}
}
}
}while(flag == 1);
}
I think I may be getting stuck in a child process when I use the pipes, but I haven't been able to see where. Thanks.
You are almost certainly failing to close all your file descriptors. One source of such an error is your dup2 calls.
After:
dup2(fdr[1], STDOUT_FILENO);
you should call
close(fdr[1]);
Why do you wait(0) immediately after fork()? In the child, this will return immediately with an error, but in the parent, it will block until the child exits. I'm having a hard time following how the pipeline is established because each child is forking off the next child in the pipeline. I'm guessing the wait(0) is creating a chicken-and-egg problem; the parent can't start until the child exits, but the child can't exit because it needs input from the parent. Wouldn't it be simpler if the shell process just looped over the pipeline components and forked each one itself, and then waited for them all to finish?

unexpected termination of program running in a loop

This is a cleaned code that I'm using in order to execute shell commands.
Though isExit is always 0 and loop should run as long as it is !isExit, my program terminates after one loop with the command ls as argument to execute.
Does anyone have any idea?
The output is OK (ls) but then program terminates.
The code is written in C, on Eclipse.
Running on Ubuntu 12 which is running on VM over Windows 7.
int main() {
int numberOfCommands, numOfWords, i, isExit = 0, isBackGround = 0, isSucces;
char input[256];
char *cmdAndArgs[256];
char *commands[256];
do{
// gets and parses user command...
ExecuteCmd(cmdAndArgs);
} while (!isExit);
return EXIT_SUCCESS;
}
void ExecuteCmd(char *cmdAndArgs[]){
pid_t pid;
pid = fork();
if (pid != 0) {
execvp(cmdAndArgs[0], cmdAndArgs);
exit(0);
} else {
waitpid(pid, &status, 0);
}
}
You're running the execvp in the parent process, not in the child. the logic:
pid_t pid;
pid = fork();
if (pid != 0) {
execvp(cmdAndArgs[0], cmdAndArgs);
exit(0);
} else {
waitpid(pid, &status, 0);
}
should be reversed to:
pid_t pid;
pid = fork();
if (pid == 0) { /* Child */
execvp(cmdAndArgs[0], cmdAndArgs);
exit(0);
} else if (pid == -1) {
perror("fork");
} else {
waitpid(pid, &status, 0);
}
the return codes from fork() are: -1 == fork failed (use errno to determine the reason). 0 == I am the child. > 0 == I am the parent. See the reference for fork under the RETURN VALUE section.
Is it possible that you have a buffer overflow in // gets and parses user command...? If you write past the allocated space, you can end up overwriting the value of isExit

Resources