Using Signal Handlers to Pause/Resume a Child Process - c

I'm currently trying to experiment with signals in C by using them to control a child process created with the fork() method. Essentially, I have a child process running the "yes" command from the linux terminal (this command just prints "y" and a newline until it is terminated). I want to be able to pause/resume this process with CTRL-Z. This is what i've got right now:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
pid_t CHILD_PROCESS;
pid_t PARENT_PROCESS;
int isPaused;
void pause_handler(int signo){
if(!isPaused){
printf("Ctrl-Z pressed. Pausing child.\n");
isPaused = 1;
kill(CHILD_PROCESS,SIGSTOP);
}
else if(isPaused){
printf("\nCtrl-Z pressed. Resuming child.\n");
kill(CHILD_PROCESS,SIGCONT);
isPaused = 0;
}
}
int main(int argc, char** argv){
pid_t pid;
PARENT_PROCESS = getpid();
pid = fork();
if(pid == 0){
system("yes");
}
isPaused = 0;
if(pid > 0){
signal(SIGTSTP, SIG_IGN);
signal(SIGSTOP, SIG_IGN);
CHILD_PROCESS = pid;
while(1){
if(signal(SIGTSTP,pause_handler) == SIG_ERR){
printf("Signal Failure");
}
}
}
}
When I run this, I can get "Ctrl-Z pressed. Pausing child." to print to console by pressing CTRL-Z, and I can get "Ctrl-Z pressed. Resuming child." to print to the console by pressing CTRL-Z again. However, it doesn't actually resume printing "y" over and over again. Any ideas as to why the child process isn't resuming?

As it turns out, system has an implicit fork call within it, so the PID that gets stored in CHILD_PROCESS ends up not actually being the child process, and instead an intermediate one.
From man 3 system:
The system() library function uses fork(2) to create a child process
that executes the shell command specified in command using execl(3) as
follows:
execl("/bin/sh", "sh", "-c", command, (char *) 0);
system() returns after the command has been completed.
So, if we replace the system("yes") call with execl("/bin/sh", "sh", "-c", "yes", NULL), then we avoid this extra fork and the program functions as desired.
The only other issue is that, by I comment I found on this post, using printf within a signal handler is undefined behavior. Not an issue to worry about here, but something to keep in mind for future code!

Related

fork() and execvp() unexpected outcome when used with sudo

So when i invoke this program without sudo. It works fine.
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char** argv)
{
if(fork() == 0) execvp(argv[1], &argv[1]);
// else wait(NULL);
}
But with sudo (when i need to input my password) it gives an odd output:
pasha#skynet:~$ sudo ./a.out bash
[sudo] password for pasha:
pasha#skynet:~$ root#skynet:~#
Then on any input the terminal terminates. Further it only happens on a newly spawned terminal. And when the parent waits for the child the problem with sudo disappears.
Can someone explain why ?
why this happens
You are forking your process, so there are two processes now.
One process is the parent, the process run by your shell, like shell -> fork() -> exec(sudo) -> exec(./a.out). The parent terminates, because fork returns with nonzero and then main() reaches closing }. main by default returns 0. So shell sees that your program terminated with exit status 0. And your shell greets you with a new pasha#skynet:~$ prompt line after your program is done.
The other process is the child, run from your program where fork returned zero, like shell -> fork() -> exec(sudo) -> exec(./a.out) -> fork() -> exec(bash). The child process is bash, it prints root#skynet:~# (it was run after sudo) and waits for input.
Those two processes are running at the same time - ie. your shell (from which you executed sudo ./a.out) and the newly bash run from your program. Both those programs try to read and write to the same input and output at the same time.
The child process, ie. bash, needs to have exclusive control over the input in the terminal. So the child process bash executes tcsetpgrp. But your shell is that one that is controlling your terminal, not the child process. So the child process either receives signal SIGTTOU or maybe SIGTTIN upon trying to read from the input. Then the child bash executed the default handler for the signals - it terminates.
Running sudo bash & from your shell would cause a similar problem to the one that your program causes.
Your program is correct; try it out with "ls" instead of "bash",
$ ./a.out ls -al /tmp
The reason why it does not seem to work with bash is that bash
expect the process to be the group leader of the terminal foreground
process
group, which
it isn't.
That said, while the program is correct, it's severe lack of error
handling is offending :-). For example, when calling a program that
does not exist, execvp() returns with an error (as opposed to not
returning at all) which is ignored. With the effect that ... well
... you can only guess if it worked.
$ ./a.out frobozzzzz
$ # (hm)
Here's my incarnation of it. Longer. Handling errors. Seeing how it
went after child terminated.
#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
int main(int argc, char** argv)
{
int status;
pid_t pid, terminated;
pid = fork();
if (pid == -1 /*unlikely*/) {
perror("fork()");
exit(EXIT_FAILURE);
}
if (pid == 0 /*child*/) {
if (execvp(argv[1], &argv[1]) != 0) { // when argv[1] is no
// progrm in path
perror("execvp()");
exit(EXIT_FAILURE);
}
else
assert(!"not getting here because successful exec() never returns");
}
// optional: wait for child to terminate, and print diagnostics
terminated = waitpid(pid, &status, 0);
if (terminated == -1) {
perror("waitpid()");
exit(EXIT_FAILURE);
}
if (terminated == pid) { // how come those not be equal?
if (WIFEXITED(status))
fprintf(stderr, "child terminated with exit status %d\n", WEXITSTATUS(status));
else if (WIFSIGNALED(status))
fprintf(stderr, "child terminated by %d\n", WTERMSIG(status));
else
fprintf(stderr, "see \"man waidpid\" for what that could be\n");
}
return 0;
}

How to execute multiple processes in C?

I need to create a program that takes input from the user and executes it just like it does in the terminal. I am using the execvp() function for this purpose. The requirement of the program is to keep taking input from the user unless the quit call is encountered. The problem here is that the current program is replaced after the execvp() call. So, using a goto is not an option either. I found this Fork–exec article but it doesn't tell how to create an indefinite number of processes. Here is my code:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
void main() {
char *args[4];
char inputCommand[100];
fgets (inputCommand, 100, stdin);
printf ("Splitting string \"%s\" into tokens:\n",inputCommand);
/* Perfrom string tokenization here */
execvp(args[0], args);
}
fork() can be called an indefinite number of times; as long as the return value indicates that you're the parent process, you can continue to operate as usual and call it again.
Thus, you can have a loop within which you fork, call an execvp() if you're the child process, and continue to the next iteration if you're the parent.
Execvp replaces the current process image with the command you run. So it cancels your C program. To produce the desired effect, you should fork before execvp. It would look something like this:
int status = 0;
pid_t pid = fork();
if(pid > 0) {
waitpid(pid, &status, 0);
// is parent
} else if(pid == 0) {
execvp(*args, args);
// is child
} else {
// fork didn't work
}

ps command linux vs unix different behavior in c program

I have a simple c program that executes 'ps' and pipes it to 'grep', basically 'ps | grep x'.
the code goes more or less something like this:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
int pipefd[2];
int pid;
pipe(pipefd);
pid=fork();
if (pid == 0){
close(pipefd[1]);
dup2(pipefd[0], 0);
close(pipefd[0]);
execlp("grep", "grep", "b", (char *) 0);
}
else{
close(pipefd[0]);
dup2(pipefd[1], 1);
close(pipefd[1]);
execlp("ps", "ps", (char *) 0);
}
exit(0);
}
The problem that i have is that when i run this on unix (Solaris) is works perfect, but when i run this on (Debian) it executes properly but gives me an error message.
error message:
Signal 17 (CHLD) caught by ps (procps-ng version 3.3.3).
ps:display.c:59: please report this bug
I have try the same program running different commands like 'ls' and 'grep' with no problem on either os. What makes 'ps' different?
EDIT:
added the included libraries to the code.
When your program calls fork, it creates a parent process and a child process. In the child process fork returns 0 and in the parent it returns 1. Whenever a child process terminates, a SIGCHLD signal is sent to the parent process.
Now, in your case you call execlp in both the parent and child process, which replaces the running process image but does not change the relationship. This means that ps is your parent process and grep is your child process. Normally this would not matter, as programs ignore SIGCHLD by default, but ps catches all unknown signals and quits with the message you see there. You can see the relevant function in the source code for ps (or rather procps).

Using C to send an exec process to the background?

My question sounds the same as this but it isn't:
Start a process in the background in Linux with C
I know how to do fork() but not how to send a process to the background. My program should work like a simple command unix shell that supports pipes and background processes. I could do pipe and fork but I don't know how to send a process to the background with & like the last line of the program:
~>./a.out uname
SunOS
^C
my:~>./a.out uname &
How to achieve the background process?
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#define TIMEOUT (20)
int main(int argc, char *argv[])
{
pid_t pid;
if(argc > 1 && strncmp(argv[1], "-help", strlen(argv[1])) == 0)
{
fprintf(stderr, "Usage: Prog [CommandLineArgs]\n\nRunSafe takes as arguments:\nthe program to be run (Prog) and its command line arguments (CommandLineArgs) (if any)\n\nRunSafe will execute Prog with its command line arguments and\nterminate it and any remaining childprocesses after %d seconds\n", TIMEOUT);
exit(0);
}
if((pid = fork()) == 0) /* Fork off child */
{
execvp(argv[1], argv+1);
fprintf(stderr,"Failed to execute: %s\n",argv[1]);
perror("Reason");
kill(getppid(),SIGKILL); /* kill waiting parent */
exit(errno); /* execvp failed, no child - exit immediately */
}
else if(pid != -1)
{
sleep(TIMEOUT);
if(kill(0,0) == 0) /* are there processes left? */
{
fprintf(stderr,"\Attempting to kill remaining (child) processes\n");
kill(0, SIGKILL); /* send SIGKILL to all child processes */
}
}
else
{
fprintf(stderr,"Failed to fork off child process\n");
perror("Reason");
}
}
The solution in plain English appears to be here:
How do I exec() a process in the background in C?
Catch SIGCHLD and in the the handler, call wait().
Am I on the right track?
Q: How do I send a process to the background?
A: In general, exactly what you're already doing: fork()/exec().
Q: What's not working as you expect?
I suspect maybe you also want a "nohup" (to completely disassociate the child from the parent).
The key to doing this is to run "setsid()" in the child process:
How to use fork() to daemonize a child process independant of it's parent?
http://www.enderunix.org/docs/eng/daemon.php

Finding pid of background process from c prog

I am writing a program for creating a shell which handles basic functionalities such as executing basic commands,piping,redirection,executing background process.However i am not being able to kill a background process, i need to know the pid() of the background process so that i can send a kill call along with the pid.Any idea how to get the pid() of a background process from a c pogram?
For running the commands I am taking input from commandline into an array,parsing it and putting the command in arr[0] and the subsequent arguments in the subsequent indexes,i am taking the PATH of the system into another array and storing them as strings by using strtok and delim option as :,after this i am concatenating the path with the command,and then doing an execv().
I am stuck with this part where i have to kill a background process.Any suggestion would be extremely helpful.
Thanks in advance.
fork returns the PID of the child in the parent process, store it someplace and then use it to kill?
You should do something like this:
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
pid_t pID = fork();
if (pID == 0) {
execl("/bin/ls", "/bin/ls", "-r", "-t", "-l", (char *) 0);
} else {
waitpid(pID, NULL, 0); // wait for child process
}
}
You can call getpid() (in the childprosses), or the pid of the child is returned to the parent when calling fork()

Resources