execvp in C not going through ar - c

I'm trying to use exec to execute a list of commands given as arguments.
Example input when In run the program would be ./assn2 ls date.
When I do this only the first command is executed.
#include<stdio.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<unistd.h>
int main(int argc, char *argv[])
{
int args = argc-1;
pid_t childpid = fork();
// error
if (childpid < 0)
{
perror("fork() error");
exit(-1);
}
// parent process
if (childpid != 0)
{
printf("Parent Process started, now waiting for ID: %d\n", childpid);
wait(NULL);
printf("Parent Process resumeed. Child exit code 0. Now terminating\n");
exit(0);
}
// child process
if (args > 0)
{
printf("Child process has begun. %d argument/s provided\n", args);
int i;
for (i = 1; i <= argc; i++)
{
execlp(argv[i], argv[i], NULL);
}
execvp(argv[1], argv);
}
else
{
printf("No arguments provided, terminating child\n");
}
return 0;
}

Once the first child process execs (and succeeds), the for loop no longer continues because the an execlp would just replace the current process image with the command being exec'ed.
What you want to do is to loop over the command line arguments in the parent process and exec once for each of the command. Something like is probably what you're after:
for(int i = 1; i < argc; i++) {
pid_t pid = fork();
if (pid == 0) {
execlp(argv[i] ,argv[i], (char*)0);
perror("exec");
} else if (pid > 0) {
wait(NULL);
} else {
perror("fork");
exit(1);
}
}

What are you trying to achieve with the sequential calls to execlp() and execvp()? These functions are not meant to return. I think you should read the ref:
The exec() family of functions replaces the current process image with a new process image. [..] The exec() functions only return if an error has occurred.
As a result you cannot execute them one after another in the same process.
Read about fork():
fork() creates a new process by duplicating the calling process.
Moreover, here:
for(i = 1; i <= argc; i++)
you go out of bounds, since argv starts indexing from 0, and ends at argc - 1.
Chnage it to:
for(i = 1; i < argc; i++)

Related

Why my atexit function not working in linux

I made processes with fork() and put exit(0) at the end and atexit(func) so I can get notice when process is exited or not to avoid zombie processes.
However, there are no output for atexit so I think maybe I have made zombie process. Can anyone tell me why my atexit output doesn't show?
//fork parent process (first process fork):
if ((pid = fork()) == 0) {
printf("parent1: %d in %d\n", getpid(), getpgid(pid));
atexit(endfunc);
char* argf[MAXARGS];
int a;
printf("buf: %s\n", buf);
if (strchr(cmdline, '|') != NULL) {
a = make_tokens(buf, 0, argf, "|");
printf("In pipe\n");
int fd[200][2];
pid_t pids[200];
for (int i = 0; i < a - 1; i++) {
pipe(fd[i]);
//somewhere in parent fork child:
if ((pids[0] = fork()) == 0) {
printf("child: %d in %d\n", getpid(), getpgid(pid));
atexit(endfunc);
close(fd[0][0]);
for (int i = 1; i < a - 1; i++) {
close(fd[i][0]);
close(fd[i][1]);
}
char* arg[MAXARGS];
parseline(argf[0], arg);
execvp(arg[0], arg);
exit(0);
}
//at the end of parent process wait for childrens
pid_t wpid;
for (int i = 0; i < a; i++) {
wpid = waitpid(pids[i], NULL, 0);
if (wpid < 0) {
perror("waitpids");
}
else if (wpid >= 0) {
printf("wait for %d\n", pids[i]);
}
exit(0);//parent process exit
//endfunc: function for atexit()
void endfunc(void) {
printf("process %d ended\n", getpid());
}
This is the output after I input ls -al | grep t:
mini> ls -al | grep t
parent1: 23154 in 23140
buf: ls -al | grep t
In pipe
child: 23155 in 23140
child: 23156 in 23140
//output for command
wait for 23155
wait for 23156
process 23154 ended
wait for 23154
As we can see, parent process has ended well and atexit printed. However, child processes has been made but atexit for child has not cameout.Has my child processes has not been exited?
From the atexit manual page:
Upon a successful call to one of the exec(3) functions, all registrations are removed.
Because a successful exec call will replace the process code, including the one you registered with atexit, your exit code can no longer be called and is thus "unregistered".

Execvp linux: trying to make my shell work in C

I'm trying to make simple shell, but with a specific condition, I have to use the following structure:
typedef struct cmd_struct{
char cmd[80];
char args[10][80];
int nargs;
} cmd_type;
Inside cmd I will save de main command and arguments in args.
Then I read from a file, different commands, and save them into and array of cmd_type. My program or fake shell, ask for a number and should take it from this array.
My function that executes the command looks like:
void execCmd(cmd_type* cmds_arg, int idxCmd){
pid_t pid;
printf("Father: my pid is %d\n", getpid());
char* buff;
pid = fork();
if (pid == 0) {
printf("Child process: My pid is %d\n", getpid());
printf("-------------- Child doing exec: %s\n", cmds_arg[idxCmd].cmd);
execvp(cmds_arg[idxCmd].cmd,&cmds_arg[idxCmd].args);
_exit(2);
_exit(1);
}
printf("Father: Gonna wait for Child\n");
int status;
wait(&status);
printf("-------------- Father: Child finished\n");
// WIFEXITED, WEXITSTATUS Macro of the gnu lib POSIX standard to recover end status
if ( WIFEXITED(status) ) {
const int es = WEXITSTATUS(status);
printf("Father: Child Complete with exit status %d\n", es);
if(es == 1) printf("Father: Child didn't execute any command\n");
else if(es == 2) printf("Father: Child command was not found\n");
}
}
As you can see, when I'm calling the execvp() system call, I'm doing it wrong. First argument I think that it is right, second one it's totally wrong.
First of all, I have a conversion problem right there, and the second problem is that the array should contain "main command", "arg1", "arg2" ... and mine only has the arguments. Am I wrong?
Is there a way to add the "main command" using services like sscanf ()? And the most important part, do I have any chance to make it work this way?
With char args[10][80];, execvp(cmds_arg[idxCmd].cmd,&cmds_arg[idxCmd].args); won't work (it won't even compile), because execvp needs a const char* pointer to each argument, and your .args doesn't have any pointers.
Something like this may work:
const char *p[11]; /* Contains up to 10 pointers + trailing NULL. */
cmd_type *this_cmd = &cmds_arg[idxCmd];
for (int i = 0; i < this_cmd->nargs; ++i) {
p[i] = &this_cmd->args[i];
}
p[this_cmd->nargs] = NULL;
execvp(this_cmd->cmd, p);
In the same spirit of the answer from #pts, you can copy the parameters for execvp() in a dynamically allocated table:
void execCmd(cmd_type* cmds_arg, int idxCmd){
pid_t pid;
printf("Father: my pid is %d\n", getpid());
char* buff;
pid = fork();
if (pid == 0) {
int i;
char **args = (char **)malloc((1 + cmds_arg[idxCmd].nargs + 1) * sizeof(char *));
args[0] = cmds_arg[idxCmd].cmd;
for (i = 1; i < (cmds_arg[idxCmd].nargs + 1); i ++) {
args[i] = cmds_arg[idxCmd].args[i - 1];
}
args[i] = NULL;
printf("Child process: My pid is %d\n", getpid());
printf("-------------- Child doing exec: %s\n", cmds_arg[idxCmd].cmd);
execvp(cmds_arg[idxCmd].cmd, args);
_exit(2);
}
printf("Father: Gonna wait for Child\n");
int status;
wait(&status);
printf("-------------- Father: Child finished\n");
// WIFEXITED, WEXITSTATUS Macro of the gnu lib POSIX standard to recover end status
if ( WIFEXITED(status) ) {
const int es = WEXITSTATUS(status);
printf("Father: Child Complete with exit status %d\n", es);
if(es == 1) printf("Father: Child didn't execute any command\n");
else if(es == 2) printf("Father: Child command was not found\n");
}
}

Create multiple child processes and run execvp

I have a function in C which creates a child process and makes it run execvp.
int Execute(char **arg)
{
pid_t pid;
int status;
if ((pid=fork()) == 0)
{
execvp(arg[0],arg);
perror("Execvp error");
exit(1);
}
else if (pid > 0)
{
waitpid(pid, &status, 0);
}
else
{
perror("Fork error");
exit(2);
}
}
Now I want to alter the function to actually run execvp several times (for example 5), and make the parent process wait for all the children to finish. Tried wrapping it all in for loop, but execvp gets executed just once. I know that basically execvp 'replaces' the current program code, but have no idea whether the iteration does not go on.
Thank you for your help!
First, loop around the process creation collecting the child PIDs
pid_t pid[5];
int i;
for (i = 0; i < 5; i++) {
if ((pid[i]=fork()) == 0) {
execvp(arg[0],arg);
perror("Execvp error");
_exit(1);
}
if (pid[i] < 0) {
perror("Fork error");
}
}
Second, loop around the waitpid call for every valid PID.
for (i = 0; i < 5; i++) {
if (pid[i] > 0) {
int status;
waitpid(pid[i], &status, 0);
if (status > 0) {
// handle a process sent exit status error
}
} else {
// handle a proccess was not started
}
}

Creating a UNIX Shell, confused about fork and child pids?

Here is my code for the evaluate function, which is called in the main.
void eval(char *cmdline)
{
char *argv[MAXARGS]; /* argv for execve() */
int bg; /* should the job run in bg or fg? */
pid_t pid; /* process id */
bg = parseline(cmdline, argv);
struct job_t tempJob;
if (builtin_cmd(argv) == 0)
{
if ((pid = fork()) == 0)
{ /* child runs user job */
if (execve(argv[0], argv, environ) < 0)
{
printf("%s: Command not found.\n", argv[0]);
exit(0);
}
}
if (!bg)
{ /* parent waits for fg job to terminate */
int status;
if (waitpid(pid, &status, 0) < 0)
unix_error("waitfg: waitpid error");
}
else /* otherwise, don’t wait for bg job */
{
printf("%d %s", pid, cmdline);
}
}
return;
}
Now when I run a background job, I expect that the pid of the job get printed twice twice, once in parent and once for child.I just need to understand what is going on in the code. It works the way it is supposed to but why?
Both the parent and child return from fork, but the condition == 0 is true only for the child:
if ((pid = fork()) == 0)
Thus only the child runs this:
if (execve(argv[0], argv, environ) < 0)
execve replaces the current process, and thus does not return if there is no error, i.e., the child's execution of this program ends either here or at the exit(0); that follows from the error condition.
The rest of the function is run only by the parent in all cases.

Program crash in forking process with pipes

I'm writing a basic shell for course homework that will find a command in the given list of paths, and execute the command. It is also meant to handle pipes.
However, when I fork a child process, I get a "Write error : Broken Pipe" message in gdb, and the program terminates abruptly.
I cannot seem to understand why this is happening, since I've been cautious about opening and closing correct pipes and process forking seems to work as desired. Can someone with more experience in C and unix programming please help me diagnose the problem? Is there something logically incorrect with my fork implementation / pipe implementation?
//commands is of the format {"ls -al", "more", NULL}
//it represents commands connected by pipes, ex. ls -al | more
char **commands = parseArgv(consoleinput, SPECIAL_CHARS[4]);
int numcommands = 0;
while( commands[numcommands]!=NULL )
{
numcommands++;
}
const int numpipes = 2*(numcommands-1);
int pipefds[numpipes];
int i=0;
for(i=0; i<numpipes;i=i+2)
{
pipe(pipefds+i);
}
int pipe_w = 1;
int pipe_r = pipe_w - 3;
int curcommand = 0;
while(curcommand < numcommands)
{
if(pipe_w < numpipes)
{
//open write end
dup2(pipefds[pipe_w], 1);
}
if(pipe_r > 0)
{
//open read end
dup2(pipefds[pipe_r], 0);
}
for(i=0;i<numpipes;i++) //close off all pipes
{
close(pipefds[i]);
}
//Parse current command and Arguments into format needed by execv
char **argv = parseArgv(commands[curcommand], SPECIAL_CHARS[0]);
//findpath() replaces argv[0], i.e. command name by its full path ex. ls by /bin/ls
if(findPath(argv) == 0)
{
int child_pid = fork();
//Program crashes after this point
//Reason: /bin/ls: write error, broken pipe
if(child_pid < 0)
{
perror("fork error:");
}
else if(child_pid == 0) //fork success
{
if(execv(argv[0], argv) == -1)
{
perror("Bad command or filename:");
}
}
else
{
int child_status;
child_pid = waitpid(child_pid, &child_status, 0);
if(child_pid < 0)
{
perror("waitpid error:");
}
}
}
else
{
printf("Bad command or filename");
}
free(argv);
curcommand++;
pipe_w = pipe_w + 2;
pipe_r = pipe_r + 2;
}
//int i=0;
for(i=0;i<numpipes;i++) //close off all pipes
{
close(pipefds[i]);
}
free(commands);
Duplicating the file descriptors after the fork() call, i.e. in the child process, is the correct way.
Also, the waitpid() call makes one child process wait for the other, and the shell hangs. The wait() call should be moved to after the loop, i.e. the parent should wait for all the children.

Resources