Execvp not being executed n times in a loop - c

Assume input is a pointer to array, which at each elements stores, "ls -l" at position 0, then cat helloworld.txt at position 1, and so forth, I wish to create the main parameter, which is ls, cat, pwd, and execute it. Essentially, what I am doing is I have a file with all those commands, I first store them in my input variable, which is declared as char *input[10]. Now I have what I need in that array, and I am able to extract the individual main commands, like ls, cat, and I wish to execute all of them.
For example,
if position 0 had ls -l, my first variable has ls and I wish to pass that to execvp and then position 1 might have cat sample.txt, now my variable first will be cat, and I pass that to execvp along with the entire cat sample.txt(which is input[i]), to execvp. For some strange reason, this is not working. How can I run all those commands in a loop in an execvp such that once it's done, all those commands have ran successfully. Here is my attempt, at the end of the first loop, I run an execvp, I expect that to finish, and then I extract further input, etc, etc.
Help me out.
for(i=0; i<lineCount; i++)
{
first = malloc(sizeof(char)*50);
for(j=0; j<strlen(input[i]); j++)
{
if(input[i][j]!=' ')
{
first[j]=input[i][j];
}
else
{
break;
}
}
execvp(first, input[i]);
}
I tried doing execp(first, input) but that didn't work either.

If you use execvp() once , the context of execution of the process that involved will be changed , except the pid of the process that called execvp , hence your loop won't work since once execvp() , there won't be any more iterations.
execvp() is mainly meant to be called by a child process , in your case for 'n' number of execvp() calls , there must have neen 'n' number of child processes forked,
Good Practices:
Using execl, execv, execle, execve, execlp, execvp , family of system calls , with child processes.
After the new process image is loaded to child , and after execution , collect the exit code of process launched , and perform any necessary error handling.
The child processes are now in a zombie state , the parent process must exexute wait()/waitpid() , and wait till all the child processes are terminated , and then exit.
-- Edit --
POC code for OP's reference
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
pid_t cpid;
int status = 0;
cpid = fork();
if (cpid == 0)
{
char *cmd[] = { "ls", "-l", (char *)0 };
int ret = execv ("/bin/ls", cmd);
}
wait(&status);
if(status < 0)
perror("Abnormal exit of program ls");
else
printf("Exit Status of ls is %d",status);
}

Here is what the opengroup exec manual says, in the first paragraph:
There shall be no return from a successful exec, because the calling
process image is overlaid by the new process image.
I suggest reading the opengroup fork manual, and using fork and exec in conjunction.

exec replaces the running process with the one you exec, and so it never returns on success because the process will be replaced.
If you want to run a bunch of processes, the simple way is to use a utility function like popen or system to run them. For complete control, use the usual UNIX fork/exec combo once for each command you want to run.

Related

Why is sleep in a child process blocking my program?

So I have this simple program that sleeps for 4 second if the value returned by fork is '0' meaning that the child process is executing, I've tried using sleep in child process but the program is blocked, and flushing standard output isn't working...
code:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[]) {
pid_t value = fork();
if (value == 0) {
sleep(4);
}
printf("Value returned by fork: %d\n", value);
printf("I'm the process N°%d\n", getpid());
return 0;
}
I'm running on Ubuntu 20.04.3 LTS.
Output:
Value returned by fork: 12618
I'm the process N°12617\
farouk#farouk-HP-Pavilion-Desktop-TP01-1xxx:~/sysexp$ Value returned by fork: 0
I'm the process N°12618
To allow this question to have an accepted answer.
The child process is not blocking the shell. The shell gave its prompt and the child wrote some output after the prompt, leaving the cursor at the start of a line without a shell prompt visible — because the shell prompt already appeared earlier.
There are a variety of ways around this.
The simplest is just to type a command such as ps and hit return, noting that the shell executes it, and that the ps output does not list the child process. If you type the ps command quick enough, you might see the child listed in the output before its output appears.
Another is to modify the program so that it waits for all child processes to exit before it exits — using wait() or waitpid(). The same code can be used in the child and the parent since the child will have no children of its own. The call to the wait function will return immediately with a 'no more children' status (error).
You can find extensive discussion of all this in the comments — I've chosen to make this a Community Wiki answer since there was a lot of activity in the comments that identified the gist of this answer.

Do *Unix shells call the pipe() function when encountering the "pipe character"? [duplicate]

I am working on a tiny shell(tsh) implemented in C(it's an assignment). One part of assignment belongs to PIPING. I have to pipe a command's output to another command. e.g:ls -l | sort
When I run the shell, every command that I execute on it, is processed by a child process that it spawns. After the child finishes the result is returned. For piping I wanted to implement a harcoded example first to check how it works. I wrote a method, that partially works. The problems is when I run the pipe command, after child process finishes, the whole program quits with it! Obviously I am not handling the child process signal properly(Method code below).
My Question:
How does process management with pipe() works? if i run a command ls -l | sort does it create a child process for ls -l and another process for sort ? From the piping examples that I have seen so far, only one process is created(fork()).
When the second command (sort from our example) is processed, how can i get its process ID?
EDIT: Also while running this code I get the result twice. don't know why it runs twice, there is no loop in there.
Here is my code:
pid_t pipeIt(void){
pid_t pid;
int pipefd[2];
if(pipe(pipefd)){
unix_error("pipe");
return -1;
}
if((pid = fork()) <0){
unix_error("fork");
return -1;
}
if(pid == 0){
close(pipefd[0]);
dup2(pipefd[1],1);
close(pipefd[1]);
if(execl("/bin/ls", "ls", (char *)NULL) < 0){
unix_error("/bin/ls");
return -1;
}// End of if command wasn't successful
}// End of pid == 0
else{
close(pipefd[1]);
dup2(pipefd[0],0);
close(pipefd[0]);
if(execl("/usr/bin/tr", "tr", "e", "f", (char *)NULL) < 0){
unix_error("/usr/bin/tr");
return -1;
}
}
return pid;
}// End of pipeIt
Yes, the shell must fork to exec each subprocess. Remember that when you call one of the execve() family of functions, it replaces the current process image with the exec'ed one. Your shell cannot continue to process further commands if it directly execs a subprocess, because thereafter it no longer exists (except as the subprocess).
To fix it, simply fork() again in the pid == 0 branch, and exec the ls command in that child. Remember to wait() for both (all) child processes if you don't mean the pipeline to be executed asynchronously.
Yes, you do need to call fork at least twice, once for each program in the pipeline. Remember that exec replaces the program image of the current process, so your shell stops existing the moment you start running sort or (tr).

using execvp to execute commands that I have in an array

I have a commands array and I want to execute each command in this array but I couldn't seem to get it working so I have
childPid = fork();
for(int i =0;i < numOfCommands;i++)
{
if(childPid == 0)
{
execvp(commands[i], argv);
perror("exec failure");
exit(1);
}
else
{
wait(&child_status);
}
}
What this does, is that it only executes the 1st command in my array but doesn't proceed any further, how would I continue ?
And what if i want the order for the commands to executed randomly and the results be intermixed so do I have to use fork then ?
You need to use fork in any case, if you want to execute more than one program. From man exec: (emphasis added)
The exec() family of functions replaces the current process image with a new process image.
…
The exec() functions return only if an error has occurred.
By using fork, you create a new process with the same image, and you can replace the image in the child process by calling exec without affecting the parent process, which is then free to fork and exec as many times as it wants to.
Don't forget to wait for the child processes to terminate. Otherwise, when they die they will become zombies. There is a complete example in the wait manpage, linked above.

C Program for shell: Execvp prints output and then segmentation fault occurs when executing a background process

I am writing a simple C program to create my own shell. It takes in input as commands and executes them. But when I try to execute a process in background( i.e. I fork a process from parent. The parent won't wait for the child process to finish, it just goes on to take more input commands while the child process runs in the background.) The execvp does execute the command but then gives a segmentation fault immediately.
Can you help me? I'll post my part of the code which I think is relevant. Let me know if you need to know anything more, i'll edit my question accordingly.
while(1){
pid = fork();
if(pid == 0)
executeCommand(info);
else
{
if(info->boolBackground ==1)
{
waitpid(pid , status , WNOHANG);
}
else
wait(NULL);
}
} //Info contains the command to be executed and it's arguments.
Here is me executeCommand function:
void executeCommand(parseInfo * info)
{
FILE *infile, *outfile;
struct commandType * com;
char * cmd;
int i , status;
cmd = (char*)malloc(1024);
strcpy(cmd , info->CommArray[0].command);
if(info->boolOutfile == 1)
{
outfile = fopen(info->outFile, "w");
dup2(fileno(outfile), 1);
}
if(info->boolInfile == 1)
{
infile = fopen(info->inFile, "r");
dup2(fileno(infile), 0);
}
status = execvp(cmd , info->CommArray[0].VarList); //VarList contains the arguments
if(status == -1){
printf("%s\n",strerror(errno));}
exit(0);
}
When I give an input command: ls &
(& means that ls should be executed in background.)
It forks a child process which executes ls and prints the list of files/directories in the directory and then gives segmentation fault. Can you spot the error? I tried running execvp in background with simply ls command. It also lead to a segmentation fault.
Yes, as Mark Plotnick pointed out in a comment, you'll need probably need &status. I'd use &info->status. Also, if you do detached jobs, you need to maintain a list of their info objects and do a waitpid loop on them:
forall (info in detached_detach_job_list) {
pid = waitpid(info->pid,&info->status,WNOHANG);
if (pid > 0) {
report_status(info);
remove_job_from_list(info);
}
}
Hopefully, the code frag you gave for your outer loop does something like this.
Also, I might not do "wait(NULL)" for a foreground. I'd treat it similarly to a detached job. Consider a case where a user does:det1 &
det2 &
...
det9000 &
run_long_30_minute_job
Because your shell is doing a hard wait on the foreground, it can't reap the detached jobs as they finish and you'll end up with zombie processes. Do the list/loop approach, just don't give user a prompt until the foreground completes (e.g. it's in the list, it's just the one with the background flag cleared). In other words, call the list something like child_list to denote all child processes, not just background. Put a sleep in the outer loop. Or, attach to SIGCHLD and do a single long sleep

Pipe and Process management

I am working on a tiny shell(tsh) implemented in C(it's an assignment). One part of assignment belongs to PIPING. I have to pipe a command's output to another command. e.g:ls -l | sort
When I run the shell, every command that I execute on it, is processed by a child process that it spawns. After the child finishes the result is returned. For piping I wanted to implement a harcoded example first to check how it works. I wrote a method, that partially works. The problems is when I run the pipe command, after child process finishes, the whole program quits with it! Obviously I am not handling the child process signal properly(Method code below).
My Question:
How does process management with pipe() works? if i run a command ls -l | sort does it create a child process for ls -l and another process for sort ? From the piping examples that I have seen so far, only one process is created(fork()).
When the second command (sort from our example) is processed, how can i get its process ID?
EDIT: Also while running this code I get the result twice. don't know why it runs twice, there is no loop in there.
Here is my code:
pid_t pipeIt(void){
pid_t pid;
int pipefd[2];
if(pipe(pipefd)){
unix_error("pipe");
return -1;
}
if((pid = fork()) <0){
unix_error("fork");
return -1;
}
if(pid == 0){
close(pipefd[0]);
dup2(pipefd[1],1);
close(pipefd[1]);
if(execl("/bin/ls", "ls", (char *)NULL) < 0){
unix_error("/bin/ls");
return -1;
}// End of if command wasn't successful
}// End of pid == 0
else{
close(pipefd[1]);
dup2(pipefd[0],0);
close(pipefd[0]);
if(execl("/usr/bin/tr", "tr", "e", "f", (char *)NULL) < 0){
unix_error("/usr/bin/tr");
return -1;
}
}
return pid;
}// End of pipeIt
Yes, the shell must fork to exec each subprocess. Remember that when you call one of the execve() family of functions, it replaces the current process image with the exec'ed one. Your shell cannot continue to process further commands if it directly execs a subprocess, because thereafter it no longer exists (except as the subprocess).
To fix it, simply fork() again in the pid == 0 branch, and exec the ls command in that child. Remember to wait() for both (all) child processes if you don't mean the pipeline to be executed asynchronously.
Yes, you do need to call fork at least twice, once for each program in the pipeline. Remember that exec replaces the program image of the current process, so your shell stops existing the moment you start running sort or (tr).

Resources