I am trying to implement my own shell as a homework. In the shell I need a new command called 'status' which displays the current status of pids for example:
When I type it must display
myshell>>status
PID PGID STATUS PROG
1412 1412 pwd exit(0)
1454 1454 /opt/firefox/bin/firefox running
1462 1462 ls exit(1)
1463 1463 xterm stopped
However in my shell I have no child process error for exited process and it writes signaled(29) for stopped process.You can see my output from here
This is my process list struct
typedef struct
{
pid_t ppid;
pid_t ppgid;
char *prog;
char status[30];
}Process;
This is my fork for executing new process:
if(forkexec){
int pid=fork();
iterator=iterator+1;
processList[iterator].prog=ptr;
processList[iterator].ppid=pid;
processList[iterator].ppgid=getpgid(pid);
strcpy(ptr,worte[0]);
switch (pid){
case -1:
perror("Fehler bei fork");
return(-1);
case 0: //child process
if(umlenkungen(k))
exit(1);
if(!setpgid(0, 0))
{
do_execvp(k->u.einfach.wortanzahl, k->u.einfach.worte); //for executing program
}
abbruch("interner Fehler 001"); //error
default: //parent process
if(k->endeabwarten){
if(!setpgid(pid, 0))
{
tcsetpgrp(0,getpgid(pid));
waitpid(pid, NULL, WUNTRACED);
tcsetpgrp(0,getpgid(shellpid));
}
}
return 0;
}
}
In child process it calls do_execvp function which is:
void do_execvp(int argc, char **args){
if(execvp(*args, args)==-1)
{
perror("exec-Fehler");
fprintf(stderr, "bei Aufruf von \"%s\"\n", *args);
exit(1);
}
}
For new status command I have, it means if the user enter status this part will run:
if (strcmp(worte[0], "status")==0) {
int i;
fputs("PID: PGID: PROGRAM: STATUS: \n",stdout);
for(i=0; i<=iterator; i++)
{
find_status(i);
printf("%d %d %s %s\n", processList[i].ppid,processList[i].ppgid,processList[i].prog,processList[i].status);
}
return 0;
}
When I am iterating the process list to print with upper code I also call find_status function which is:
void find_status(int current)
{
pid_t w;
int status;
char stat[30];
w=waitpid(processList[current].ppid, &status, WNOHANG | WUNTRACED | WCONTINUED);
switch(w){
case -1:
strcpy(stat, "No child process");
break;
case 0:
strcpy(stat,"running");
default:
if (WIFEXITED(status)!=0) {
sprintf(stat, "exit(%d)", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)!=0) {
sprintf(stat, "signaled(%d)", WTERMSIG(status));
} else if (WIFSTOPPED(status)!=0) {
strcpy(stat,"stopped");
}
break;
}
strcpy(processList[current].status, stat);
}
By the way iterator variable is global variable which holds the index of the last element in the process list. Process list is also global variable. So, where is my mistake in the code why it is not displaying exited and stopped process' status? Thank you.
Impossible to be sure since you haven't shown all the code, but I speculate that you've already waited for those processes.
In your fork code, the parent branch (default case) has a waitpid call. Is that code being executed? Once you have successfully waited for a child process (at least one that has in fact exited), it will be removed from the kernel process table, and you can't (successfully) later call waitpid again: it no longer exists.
To discover what exactly is going on, you should print the errno value when waitpid fails (even better, print strerror(errno) so that you don't have to go lookup the errno value). This will tell you exactly why waitpid failed, not just that it failed.
Related
The program is below
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
int main(int argc,char **argv)
{
int fds[2];
int error=pipe(fds);
if(error==-1)
{
perror("pipe");
return 1;
}
int ret=fork();
if(ret<0)
{perror("fork failed");
return 1;
}
else if(ret==0)
{
close(fds[0]);
close(1);
dup(fds[1]); char *argv[3];
argv[0]="ls";
argv[1]="-l";
argv[2]=NULL;
execvp(argv[0],argv);
}
else
{
int rett=fork();
if(rett<0)
{
perror("fork failed");
return 1;
}
else if(rett==0)
{
close(fds[1]);///
close(0);
dup(fds[0]);
char *argv[2];
argv[0]="wc";
argv[1]=NULL;
execvp(argv[0],argv);
}
else
{
waitpid(ret,NULL,7); /// wait for child process
waitpid(rett,NULL,7); //// wait for another child process
printf("Process %d finished\n",ret);
printf("Process %d finished\n",rett);
}
}
return 0;
}
There is the outcome below; why is printf is executing before the "wc"?
Process 7189 finished
Process 7190 finished
zyy#ubuntu:~$ 24 209 1125
Check your return values functions that can return errors. And don't use magic numbers instead of correct flags.
Your waitpid calls don't actually wait for any process to finish and just return errors. What does your magic number "7" mean? That's not what the documentation tells you to do. One of the bits in the number 7 is WNOHANG, which tells waitpid to not actually wait for the children to finish. Another bit in those flags is valid, but quite useless since you're not doing any ptrace on your processes. The third bit is invalid and will make waitpid error our immediately. I'm not going to tell you which is which because you're not supposed to do it this way at all. Read the manual page.
Another problem that you have is that you need to close the pipe in the parent process as well. wc will try to read data from the pipe (on its stdin) until the pipe is closed and it still remains open in the parent. The reason wc always prints after the parent has exited is that it's the parent exit that closes the pipe and that makes wc finish reading.
I know that it is possible to read commands output with a pipe? But what about getting return value ? For example i want to execute:
execl("/bin/ping", "/bin/ping" , "-c", "1", "-t", "1", ip_addr, NULL);
How can i get returned value of ping command to find out if it returned 0 or 1?
Here is an example I wrote long time ago. Basically, after you fork a child process and you wait its exit status, you check the status using two Macros. WIFEXITED is used to check if the process exited normally, and WEXITSTATUS checks what the returned number is in case it returned normally:
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
int number, statval;
printf("%d: I'm the parent !\n", getpid());
if(fork() == 0)
{
number = 10;
printf("PID %d: exiting with number %d\n", getpid(), number);
exit(number) ;
}
else
{
printf("PID %d: waiting for child\n", getpid());
wait(&statval);
if(WIFEXITED(statval))
printf("Child's exit code %d\n", WEXITSTATUS(statval));
else
printf("Child did not terminate with exit\n");
}
return 0;
}
You can use waitpid to get the exit status of you child process as:
int childExitStatus;
waitpid( pID, &childExitStatus, 0); // where pID is the process ID of the child.
exec function familly does not return, the return int is here only when an error occurs at launch time (like not finding file to exec).
You have to catch return value from the signal sent to the process that forked before calling exec.
call wait() or waitpid() in your signal handler (well, you can also call wait() in your process without using any signal handler if it has nothing else to do).
Had trouble understanding and applying the existing answers.
In AraK's answer, if the application has more than one child process running, it is not possible to know which specific child process produced the exit status obtained. According the man page,
wait() and waitpid()
The wait() system call suspends execution of the calling process until one of its children terminates. The call wait(&status)
is equivalent to:
waitpid(-1, &status, 0);
The **waitpid()** system call suspends execution of the calling process until a **child specified by pid** argument has changed state.
So, to obtain the exit status of a specific child process we should rewrite the answer as :
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
int number, statval;
int child_pid;
printf("%d: I'm the parent !\n", getpid());
child_pid = fork();
if(child_pid == -1)
{
printf("could not fork! \n");
exit( 1 );
}
else if(child_pid == 0)
{
execl("/bin/ping", "/bin/ping" , "-c", "1", "-t", "1", ip_addr, NULL);
}
else
{
printf("PID %d: waiting for child\n", getpid());
waitpid( child_pid, &statval, WUNTRACED
#ifdef WCONTINUED /* Not all implementations support this */
| WCONTINUED
#endif
);
if(WIFEXITED(statval))
printf("Child's exit code %d\n", WEXITSTATUS(statval));
else
printf("Child did not terminate with exit\n");
}
return 0;
}
Feel free to turn this answer to an edit of AraK's answer.
You can wait on the child process and get its exit status.
The system call is wait(pid), try to read about it.
Instead of exec family, you can use popen. Then read the output using fgets or fscanf.
char buff[MAX_BUFFER_SIZE];
FILE *pf = popen("your command", "r");
fscanf(pf, "%s", buff);
pclose(pf);
I'm doing a homework assigment (the regular "write your own unix shell in c" assigment)
and cant make my child process run in the background properly, the are KILLED right before calling execvp
my intuition tells me the problem is in the signal handler or the usage of return in the parent process after fork().
This is how I've implemented it (just for the background process) :
void ExeExternal(char *args[MAX], char* cmd,ExecutionMode ExeMode) {
int pID;
switch(pID = fork())
{
case -1:
// Add your code here (error)
/*
your code
*/
perror("fail to fork in : ExeExternal(...)\n");
exit(-1);
case 0 :
// Child Process
signal(SIGTSTP, SIG_DFL);
signal(SIGINT, SIG_DFL);
signal(SIGCONT, SIG_DFL);
usleep(20000);
setpgrp();
printf("trying to execvp\n");
int run_result=execvp(args[0],args);
if(run_result==-1)
{
perror("execvp(...) result in execvp\n");
exit(-1);
}
break; //wont reach this part ever!
default://the parent. pID holds child's pID
setpgid(pID, pID); //note, this is also done in the child
if(ExeMode==BACKGROUND) //no need to WAIT()
{
return;
}
}
}
and this is my signal handler (registered in my main() with signal(SIGCHLD,handle_sigchld)
void handle_sigchld(int sig) {
pid_t pid;
int status;
pid=waitpid(WAIT_ANY,&status,WUNTRACED | WNOHANG);
if (pid >0)
{
if(WIFEXITED(status)) //if terminated normally, return or exit
{
printf("\n[%d] %d Done \n", ID, pid); //[1] a.out : 12340 214 secs
}
else if (WIFSIGNALED(status)) //killed with SIGINT
{
printf("handle_sigchld:[%d] pid %d KILLED\n",ID,pid);
}
else if (WIFSTOPPED(status)) //suspended with SIGCONT
{
printf("\n[%d] %s %d %d secs (stopped)\n", ID, value,pid,time);
}
}
}
I've tried to plant some prinft's and my child seems to be killed right before executing execvp(...)
Ive tried to print the args[0] and args[1] and it exited there, so it looks like it falls upon accessing them,
also - is that the right way to use return ? ive tried to replace it with waitpid(pid,status,WNOHANG) but it didnt helped
ANY help is greatly appreciated! even just a hint in the right direction
For my school project I am implementing a shell and I need help with job control.
If we type a command, say cat &, then because of the & it should run in background, but it's not working. I have this code:
{
int pid;
int status;
pid = fork();
if (pid == 0) {
fprintf(stderr, "Child Job pid = %d\n", getpid());
execvp(arg1, arg2);
}
pid=getpid();
fprintf(stderr, "Child Job pid is = %d\n", getpid());
waitpid(pid, &status, 0);
}
Rather than just going straight to waiting, you should set up a signal handler for the SIGCHLD signal. SIGCHLD is sent whenever a child process stops or is terminated. Check out the GNU description of process completion.
The end of this article has a sample handler (which I've more or less copied and pasted below). Try modeling your code off of it.
void sigchld_handler (int signum) {
int pid, status, serrno;
serrno = errno;
while (1) {
pid = waitpid(WAIT_ANY, &status, WNOHANG);
if (pid < 0) {
perror("waitpid");
break;
}
if (pid == 0)
break;
/* customize here.
notice_termination is in this case some function you would provide
that would report back to your shell.
*/
notice_termination (pid, status);
}
errno = serrno;
}
Another good source of information on this subject is Advanced Programming in the UNIX Environment, chapters 8 and 10.
The parent process is calling waitpid on the child, which will block until the child process changes state (i.e. terminates).
I know that it is possible to read commands output with a pipe? But what about getting return value ? For example i want to execute:
execl("/bin/ping", "/bin/ping" , "-c", "1", "-t", "1", ip_addr, NULL);
How can i get returned value of ping command to find out if it returned 0 or 1?
Here is an example I wrote long time ago. Basically, after you fork a child process and you wait its exit status, you check the status using two Macros. WIFEXITED is used to check if the process exited normally, and WEXITSTATUS checks what the returned number is in case it returned normally:
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
int number, statval;
printf("%d: I'm the parent !\n", getpid());
if(fork() == 0)
{
number = 10;
printf("PID %d: exiting with number %d\n", getpid(), number);
exit(number) ;
}
else
{
printf("PID %d: waiting for child\n", getpid());
wait(&statval);
if(WIFEXITED(statval))
printf("Child's exit code %d\n", WEXITSTATUS(statval));
else
printf("Child did not terminate with exit\n");
}
return 0;
}
You can use waitpid to get the exit status of you child process as:
int childExitStatus;
waitpid( pID, &childExitStatus, 0); // where pID is the process ID of the child.
exec function familly does not return, the return int is here only when an error occurs at launch time (like not finding file to exec).
You have to catch return value from the signal sent to the process that forked before calling exec.
call wait() or waitpid() in your signal handler (well, you can also call wait() in your process without using any signal handler if it has nothing else to do).
Had trouble understanding and applying the existing answers.
In AraK's answer, if the application has more than one child process running, it is not possible to know which specific child process produced the exit status obtained. According the man page,
wait() and waitpid()
The wait() system call suspends execution of the calling process until one of its children terminates. The call wait(&status)
is equivalent to:
waitpid(-1, &status, 0);
The **waitpid()** system call suspends execution of the calling process until a **child specified by pid** argument has changed state.
So, to obtain the exit status of a specific child process we should rewrite the answer as :
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
int number, statval;
int child_pid;
printf("%d: I'm the parent !\n", getpid());
child_pid = fork();
if(child_pid == -1)
{
printf("could not fork! \n");
exit( 1 );
}
else if(child_pid == 0)
{
execl("/bin/ping", "/bin/ping" , "-c", "1", "-t", "1", ip_addr, NULL);
}
else
{
printf("PID %d: waiting for child\n", getpid());
waitpid( child_pid, &statval, WUNTRACED
#ifdef WCONTINUED /* Not all implementations support this */
| WCONTINUED
#endif
);
if(WIFEXITED(statval))
printf("Child's exit code %d\n", WEXITSTATUS(statval));
else
printf("Child did not terminate with exit\n");
}
return 0;
}
Feel free to turn this answer to an edit of AraK's answer.
You can wait on the child process and get its exit status.
The system call is wait(pid), try to read about it.
Instead of exec family, you can use popen. Then read the output using fgets or fscanf.
char buff[MAX_BUFFER_SIZE];
FILE *pf = popen("your command", "r");
fscanf(pf, "%s", buff);
pclose(pf);