I am killing a child from the parent using:
kill(pid, SIGTERM);
It outputs to Terminal
Terminated
How can "Terminated" not be shown in Terminal?
pid_t pid ;
pid = fork();
if(pid == 0){ // pid is process index id in parent process
wait(NULL); // wait for state changes in child
if(execlp(argv[1],argv[1],NULL) == -1){ // argv1 can be either valid or invalid (for example ls or alsjdf)
char str[128];
sprintf(str, "./multifork: %s",argv[1]);
perror(str);
kill(pid, SIGINT);
}
}
If argv[1] is not a legit system call, I want to exit all programs. Both parent and children, which are created after that.
Can you try this instead of kill instruction:
sprintf(str, "kill -SIGINT %d >> /dev/null 2>&1",pid);
system(str);
system is used to execute the command in shell.
">> /dev/null 2>&1" is redirecting printing to /dev/null, in other words discarding printing of the answer ("terminated").
Related
I'm trying to write a mini-shell in C using this template. But whenever I try to use interactive commands such as less and vi, the shell gets stuck on waitpid (with WUNTRACED enabled these commands return immediately because they are stopped by a job control signal as indicated by ps) . Other commands that don't require input such as ls are fine. The root cause is setpgid, which seems to puts the forked child process (such as less and vi) into a different process group which no longer shares a terminal. The child process is therefore stopped by a job control signal. Deleting setpgid will make the mini-shell work again, but it can't be removed since the mini-shell needs to control its foreground processes as a group (e.g. if the foreground process P forks additional processes P1 and P2, the shell, upon receiving a SIGTSTP from the user, should stop P, P1, and P2. This can be conveniently done if P, P1, P2 are in the same process group whose pgid is the same as P's pid. We can just send SIGTSTP to the entire process group).
I have tried to use tcsetpgrp to fix my shell. Although it'll make commands such as vi functional again, the mini-shell will automatically exit upon completion of the forked child, presumably because the parent shell mistakenly views the completion of the forked child as also the completion of the mini-shell.
Is there a fix which will still allow me to keep setpgid?
// full code is in the provided link
if (!builtin_command(argv)) {
if ((pid = Fork()) == 0) { /* Child runs user job */
if (execve(argv[0], argv, environ) < 0) {
execvp(argv[0], argv);
printf("%s: Command not found.\n", argv[0]);
exit(0);
}
}
// call wrapper function for error handling
// set process group id of child to the pid of child
Setpgid(pid, 0);
if (!bg) {
// foreground process, should wait for completion
// tcsetpgrp does make vi and less work,
// but their completion also terminates the mini-shell
// tcsetpgrp(STDERR_FILENO, pid);
int status;
if (waitpid(pid, &status, 0) < 0) {
unix_error("waitfg: waitpid error");
}
} else {
// background process
printf("%d %s", pid, cmdline);
}
}
The solution is to relinquish control of the tty to the other process group using tcsetpgrp, and when the child is done, take back the control of the tty using tcsetpgrp again. Note that tcsetpgrp sends SIGTTOU to its caller if the calling process belongs to a background process group, so SIGTTOU and SIGTTIN must be blocked.
// error handling is omitted for brevity
// these two signal functions can be anywhere before tcsetpgrp
// alternatively, they can be masked during tcsetpgrp
signal(SIGTTOU, SIG_IGN);
signal(SIGTTIN, SIG_IGN);
if (!builtin_command(argv)) {
if ((pid = Fork()) == 0) {
if (execve(argv[0], argv, environ) < 0) {
execvp(argv[0], argv);
printf("%s: Command not found.\n", argv[0]);
exit(0);
}
}
if (!bg) {
setpgid(pid, 0);
tcsetpgrp(STDERR_FILENO, pid);
int status;
if (waitpid(pid, &status, 0) < 0) {
unix_error("waitfg: waitpid error");
}
tcsetpgrp(STDERR_FILENO, getpgrp());
} else {
printf("%d %s", pid, cmdline);
}
}
This is a rather crude implementation. Consult Craig's comments for this question for where to find bash's implementation.
I am trying to terminate my program which takes a line that is full of commands from a file and then process each command using execvp
However,Whenever I encounter quit, I want to immediately exit processing the commands and ignore all other commands that are coming after it.
I tried to do this the following way using exit()
for(int i =0;i < numOfCommands;i++)
{
childPid = fork();
if(childPid == 0)
{
if(execvp(commands[i].cmd[0],commands[i].cmd) == -1)
{
/*if(strcmp(commands[i].cmd[0],"quit"))
{
done = true;
return;
}*/
if(strcmp(commands[i].cmd[0],"quit")==0)
{
printf("Quit command found ! \n Quitting .");
done = true;
//return;
exit(0);
}
printf("Command %s is unknown \n", commands[i].cmd[0]);
}
}
else
{
//parent process
wait(&child_status);
}
}
}
And this happens inside of the child process, after forking of course. But the problem is that my program keeps processing the remaining commands that comes after quit before exiting the program !
You can use kill(2) to send a signal to the process group. You can do this in the parent or any of the children.
int kill(pid_t pid, int sig);
If pid equals 0, then sig is sent to every process in the process group of the calling process.
For example:
kill(0, SIGTERM);
I think a better way to deal with this is to check for the quit command in the parent process before forking the child.
But if you want to do it in the child, you can send a signal to the parent.
kill(getppid(), SIGUSR1);
The parent process will need to establish a signal handler for SIGUSR1 that cleans everything up and exits. Or you could send a signal like SIGINT, whose default action is to kill the process, but it's better to implement a clean exit.
Also, in your code, you should check for the quit command before calling execvp. Otherwise, if there's a quit program in the user's path, it will never match your built-in quit, since execvp will succeed and not return.
The following code works like this:
I create a pipe to connect two processes. One process will be the command who, in order to know which users are logged on. The other process will be the command grep, which takes who's command output and looks for "user" into this output. Main program's exit code must be 1 if the user is logged on, and 0 if the user's not logged on. Where do I have to take exit code?
Here's the code:
int fd[2];
pipe(fd);
int pid1,pid2;
pid1 = fork();
if (pid1 != 0) {
pid2 = fork();
if(pid2 == 0) {
close(0); dup(fd[0]); close(fd[0]), close(fd[1]);
execlp("grep","grep ","user",NULL);
} else {
close(fd[0]); close(fd[1]);
}
} else {
close(1);
dup(fd[1]);
close(fd[0]);
close(fd[1]);
execlp("who","who",NULL);
}
exec* calls usually do not return. If they return then only because exec failed to load the new process image. In that case the error is returned in errno.
If you are interested in the exit status of your child process then you have to either install a signal handler for SIGCHLD or wait for your child in the parent and retrieve the exit status that way.
For your own convenience you may want to consider using system instead:
int exit_status = system("who | grep user");
execlp() overwrites the address space with the executable command and runs it. So it will return only if there is some error in executing the command. In this case return value is -1.
I am trying to write a function that spawns a child process, lets it run for a certain amount of time and then kills it if it hasn't finished:
int sysExecTimeout(const char * exePath, int timeoutSec);
In the function, I use fork and execl to spawn the child, and, when it times out, I use kill(pid, SIGTERM) and kill(pid, SIGKILL) after 2 seconds, to ensure the child dies:
pid_t pid = fork();
if(pid == 0) {
execl("/bin/sh", "sh", "-c", exePath);
exit(127);
} else if(pid != -1) {
// timeout code
if(timeout) {
kill(pid, SIGTERM);
sleep(2);
kill(pid, SIGKILL);
}
}
I am using Linux, and it seems that when a parent process dies, the child is not killed automatically. So the two kill calls will just kill the /bin/sh process and leave the exePath command running, since it is a child process of /bin/sh.
I am trying to write the sysExecTimeout function such that it kills the entire process tree rooted at pid, where pid is the PID from pid = fork()
I need this because the exePath command will spawn other commands, which can also spawn other commands, which could get stuck and consume resources.
I do not have control over the exePath binaries/scripts that get executed, so I cannot write my own parent-dies-so-kill-the-children logic in them.
I tried using kill(0, SIGTERM), which almost did the job, except it also killed my own process :)
I am wondering if there is a flag I can turn on programatically in C that says "hey man, when I die, take all my children and kill them, and repeat recursively for their children" such that the entire process tree started from that program dies (assuming the PID/PPID chain can be followed).
I could use that flag here:
if(pid == 0) {
turnOnRecursiveDeathFlag();
system(exePath);
//execl("/bin/sh", "sh", "-c", exePath);
exit(127);
}
Is there a way to do that? I've been searching for a while, but all I can find are hacks using ps -ef, or modifying the children processes that you are running etc.
Use setpgid in the child to set its GPID equal to its own PID. The parent then can kill(-pid,...) to signal the entire group.
pid_t pid = fork();
if(pid == 0) {
setpgid(0, 0);
execl("/bin/sh", "sh", "-c", exePath);
exit(127);
} else if(pid == -1) {
// timeout code
if(timeout) {
kill(-pid, SIGTERM);
sleep(2);
kill(-pid, SIGKILL);
}
}
That should do it.
One more thing, when you spawn a shell, make sure it doesn't enable job control. Otherwise, it will create its own process groups. Your "/bin/sh -c" is fine.
how to print the current process state
main ()
{
pid_t pid;
printf("parent : %d\n", getpid());
pid = fork();
if (pid == 0)
{
printf("child : %d\n", getpid());
sleep(2);
printf("child exit\n");
exit(1);
}
while (1)
{ /*after the child exit, its a zombie process */
system("clear");
system("ps -x | grep t"); /* if a.out is Z means zombie process */
sleep(8);
}
}
Is there any other method or way to view the current process state in output
itself..?
Determining the scheduling state of a process is OS specific. On Linux, top and similar read it from the third field in /proc/$PID/stat.
You can always open and read /proc files in C
/proc/[pid]/stat and /proc/[pid]/status would give the status of the current proces.
The third field in /proc/<pid>/stat contains the process status: R if it's Running, S if it's Sleeping (there's a few others too, like D for Disk Wait and Z for Zombie).
Or you can parse the output of ps command using popen. But remember ps command itself is implemented based on the information from /proc filesystem