I made a simple minishell in C and it works, except for the cd command. When I try to run it nothing happens except it creates a child process that never actually ends. For example, after running cd in my minishell I need to type exit twice to exit the minishell, not the standard once.
Code:
int debug=0;
void safeQuit(){
if (debug==1)
printf("INFO: Parent process, pid=%d exiting.\n", getpid());
exit(0);
}
int main(int argc,char** argv)
{
int pid, stat;
char * Array_arg[50];
char command_line[200];//user input
if (argc>1)
if (strcmp(argv[1],"-debug")==0)
debug=1;
while (1){
printf("[minishell]> "+debug);
gets(command_line);
if (strcmp(command_line,"exit")==0)
safeQuit();
char * subcommand=strtok(command_line," "); //divides the string according to the spaces
int i=0;
while (subcommand!= NULL)//insert to array
{
Array_arg[i]=subcommand;
subcommand=strtok(NULL," ");
i++;
}
Array_arg[i]='\0';
if (fork()==0){
if (debug==1)
printf("INFO: child process, pid = %d, executing command %s\n", getpid(), command_line);
execvp(Array_arg[0],Array_arg); //execution of cmd
}
else{
pid=wait(&stat);
}
}
}
cd is necessarily a shell built-in, not an external utility. You want to change the current working directory of the current process (the shell itself), not of a child process. Call chdir instead of forking a child process.
Separately, check execvp for errors and defensively terminate the child after a failed exec. You'd have seen an informative error if you had done so:
... (child) ...
execvp(Array_arg[0], Array_arg);
perror("Error - exec failed"); // If we are here, we did *not* replace the process image
exit(0);
Related
I'm executing two bash command ps (which is valid bash command to show processes info) and abc (Invalid bash command) using execvp() inside a child process after creating it with by fork()
The problem is once execvp() fails parent process is taken over by child process as we can from the output of screenshot.
I had to type exit twice to exit out of infinite while loop. What's happening? I'm correctly executing execvp() inside a child process yet main(parent) process is taken over by child process?
Can anyone explain this behaviour & how I can fix this ? If execvp() fails I do not want that child process to take over parent process. How to handle this?
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>
int ExecCommand(char** args)
{
int childId = fork();
if(childId<0) printf("Error creating child process\n");
if(childId==0)
{
printf("Executing (%s) whose childId:%d\n", args[0], getpid());
int status=execvp(args[0],args);
printf("Failed executing %s with status %d whose childId:%d\n\n",args[0],status,getpid());
return 1;
}
else
{
int id=wait(NULL);
printf("waiting for child: %d\n\n",id);
}
}
int main()
{
char out[5]="exit\0";
while(1)
{
printf("From Main(): pid:%d ppid:%d\n",getpid(), getppid());
printf("Enter 'exit' to exit or Just hit Enter to execute below 2 commands:");
char input[25]={'\0'};
scanf("%24[^\n]",&input[0]);
getchar();//to remove \n from stdin
strncpy(out,input,4);
if(strcmp(out,"exit\0")==0)
{
exit(0);
}
char *cmd1[2] = {"ps", NULL}; //valid bash command
char *cmd2[2] = {"abc", NULL}; //invalid bash command
char** commands[2]={cmd1, cmd2};
for(int i=0;i<2;i++)
{
ExecCommand(commands[i]);
}
}
return 0;
}
Output:
The problem is that if execvp fails, you return to the main function in your child process. That leads to both the original (parent) and child process will continue in the loop.
If execvp fails you should probably call exit to exit the child process.
Also note that in the parent process the ExecCommand function doesn't return anything at all. If your compiler doesn't complain about that you need to enable more warnings when building.
I am creating a simple Linux command shell in C. I am having trouble understanding where my code is having problems. "commands" is a list of strings of Linux commands that I want to be executed concurrently as the children processes of one parent. When all are done executing, I want the parent to exit the function. However, when I call exit(0), the for loop continues and parses the current command again, causing the args to be executed again in execvp. Am I using fork() and wait() correctly here? I have tried using waitpid() as well with no luck.
void executeShell(char** commands){
char **arr = commands;
char *c;
pid_t pid, wpid;
int status = 0;
for (c = *arr; c; c=*++arr){
// printf("%d-\n", strcmp(command, "exit"));
if (strcmp(c, "exit") == 0){
EXIT = 1;
return;
}
printf("Running command \'%s\'...\n", c);
char** args = parseStringToTokenArray(c, " ");
free(args);
/* fork and execute the command */
pid = fork();
if(pid < 0){
perror("fork() error\n");
return;
}
/* child process executes command */
else if (pid == 0){
/* 'cd' is not a part of /bin library so handle it manually */
if (strcmp(args[0], "cd") == 0){
changeDirectory(args[1]);
}
else if (strcmp(args[0], "sdir") == 0){
searchDirectory(args[1]);
}else{
/* parse commands with arguments */
execvp(args[0], args);//execute the command
}
exit(0);// <-----command is finished, so why no exit?
}
}
/* wait for children to complete */
while((wpid = wait(&status)) > 0);
}
If execvp succeeds, the entire child process address space is replaced by the program invoked by execvp(). This means that the exit(0) will only ever be invoked following your two special cases i.e. cd and sdir. As far as your code is concerned execvp() should never return, unless there is an error.
A further problem is that you free args immediately after allocating it and then go on to use it in your child processes. This is undefined behaviour.
The only problem I see with your wait code is that, if any of the children block waiting for user input, the parent will block waiting for the child to exit.
The cd code, has no effect on any process except the child in which it is executed. The parent's current directory is not affected. As you state in the comments, this can bet fixed by handling cd in the parent without forking.
I am creating a simple Unix shell in C. I use the fork() to clone the process and exec to execute a new one. It works fine when you are inputting data for the first time in the shell. But when it comes the second iteration fork returns -1.
e.g
...>ls -l /
/results here/
...>ls -l /
forking failed
here is a part of the code:
int execute(char path[80],char *args[]){
pid_t pid;
if((pid=fork())<0){
printf("forking failed"); // The forking failed due to Cannot allocate memory error
exit(0);
}else if(pid==0){
execv(path,args);
}else{
wait(NULL);
}
return 0
}
where path is "bin/ls" and args "ls",NULL
the main is looks like
int main(){
while(1){
//read from keyboard
//find the program path
//fill args
execute(path,args);
}
}
Change your first if branch to this:
if((pid=fork())<0){
perror("forking failed");
exit(0);
}
This will tell you what went wrong.
I am trying to execute LP to print a PDF document and wait for it to exit. After
it exists i am trying to delete the file with unlink();
However the wait finishes even before execv execute LP. I am not quite sure
how to handle this and why the wait isn't waiting till execv finishes.
Is there any other way to accomplish this?
if(fork())
{
fprintf(stderr, "Executing command %s %s", "/usr/bin/lp", homedir);
char *const parmList[] = {"/usr/bin/lp", homedir, (char *)0};
execv("/usr/bin/lp", parmList );
}else
{
int pid, status;
fprintf(stderr, "Wait\n");
pid = wait(&status);
fprintf(stderr, "Finished waiting.\n");
unlink(homedir);
}
When executing the above code the ouput would look like this:
Wait
Finished waiting.
Executing command /usr/bin/lp /home/user/Docs/test.pdf
/usr/bin/lp: Error - unable to access "/home/user/Docs/test.pdf" - No such file or directory
fork() returns zero in the child process, and a positive value in the parent process (assuming the fork succeeds), and wait(...) only makes sense in the parent process, so you need to swap the contents of your if and else blocks.
EDIT:
I am trying to write a simple smoketest, where all options and reasonable parameters are tested.
I used popen() to execute the program that should be tested.
Using this approach does not work, because if the process dies with a signal (SIGINT, SIGSEGV...) the pipe from popen() does not tell me what happend.
Writing a signal handler did not help since popen creates a new process that receives the signals but not my smoketest.
Thanks to the answers i used pipe(), fork() and execv() to create my own popen()-version.
When the program now segfaults there is the problem that the pipe is useless (a read caused weird behavior -> blocked the process until i send a sigkill to the parent!)
To avoid this i tried different things and my solution is the following (it is simple but it took me a while to figure it out). so here is my example-code:
static int child_dead = 0;
void sigaction_sigchld(int signal) { /* Child died */
child_dead = 1;
}
int main(int argc, char *argv[], char *env[])
{
char *crashing_program = "/program_path/and_name";
int ret;
int byte;
pid = fork();
if(pid == 0) /* Child */
{
execve(crashing_program, argv, env);
/* if execve returns that it mus have failed! */
fprintf(stderr, "Exec failed\n");
_exit(-1);
} else /* Parent */
{
if(!child_dead)
{
byte = read(pipe_out[1], line, BUFFSIZE);
if(!byte){
perror("Smoketest:Line:xxx");
} else
{
fprintf(stdout, line);
}
}
wait(&child_status);
/*
check if the child died with SIGSEGV etc
*/
}
This seems to work fine as long as i only have one child at a time which is sufficient for me though. I anyone has a better idea or any tipps for me i would be glad to update this entry.
Last but not least: Of course using this method it is probably impossible to do any cleanup.
Cheers.
See the documentation for waitpid(2). There are a bunch of macros you can use to test how the child process was terminated. In particular, you can use WIFSIGNALED() and WTERMSIG() to test if the child process was terminated by a signal, and if so, which signal:
int status = pclose(...);
if (WIFSIGNALED(status))
{
// It was terminated by a signal
if (WTERMSIG(status) == SIGSEGV)
{
// It was terminated by a segfault
}
}
Edit: As stated in the comments, you'd rather make use of fork and exec, then use waitpid(2) to correctly update status.