What is the idea behind the way the signals are used here? - c

While reading up and learning about signals, I found a program, that uses signals in a specific way. I tried understand it, but I am not sure, how all the parts of the code interact with another.
Below is the above mentioned code and I added comments, where I have difficulties:
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define CP 5
static volatile int curprocs =0; ;
static void die() {
exit(EXIT_FAILURE);
}
static void chldhandler() {
int e = errno;
// Why do we use waitpid here? What does it do?
while(waitpid(-1, NULL, WNOHANG) > 0) {
curprocs--;
}
errno = e;
}
void do_work() {
time_t t;
srand((unsigned) time(&t));
sleep(5+ rand() % 4);
}
int main() {
struct sigaction sa = {
.sa_handler = chldhandler,
.sa_flags = SA_RESTART,
};
sigemptyset(&sa.sa_mask);
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
exit(-1);
}
while(1) {
sigset_t chld, empty;
sigemptyset(&empty);
sigemptyset(&chld);
sigaddset(&chld, SIGCHLD);
// What do the following lines of code do??
sigprocmask(SIG_BLOCK, &chld, NULL);
while (curprocs >= CP) { // cap for the number of child processes
sigsuspend(&empty);
}
curprocs++;
sigprocmask(SIG_UNBLOCK, &chld, NULL);
pid_t p = fork();
if (p == -1) {
return -1;
}
if (p == 0) {
// code for the child processes to execute
do_work();
die();
} else {
// Parent process does nothing
}
}
return 0;
}
Obviously above program is intended to have a max amount of 42 child processes doing work. Whenever we want to have a new child process, we use fork, and adjust curprocs.
Whenever a child process finishes, chldhandler() is called and curprocs is adjusted as well.
However I don't understand the interplay of the two sigproc_mask, sigsuspend, waitpid and our two signalsets chld, empty.
Can someone explain what these lines do or why they are used the way they are?

sigprocmask(SIG_BLOCK, &chld, NULL); blocks SIGCHLD so that you can be sure that while you do while (curprocs >= 42) the SIGCHLD handler won't interrupt the code, changing curprocs in the middle of the check.
sigsuspends atomically unblocks it and waits for a SIGCHLD (any signal really, since your passing an empty mask), atomically reblocking it when it returns.
The waitpid(-1,/*...*/) in the handler reaps the status of any (that's what the -1 means) child that has a status change (typically termination notification) pending so that the data the kernel associates with the status change can be freed. The second argument would be where the status change info would go but since you passed NULL, the info will simply be dropped. WNOHANG means don't wait if there aren't any more status change notifications.
Since the handler is run in response to SIGCHLD, there should be at least one status change notification, but there could be more because SIGCHLDs can coalesce (it's also possible there isn't any — if you called waitpid from normal context while SIGCHLD was blocked). That's why the handler loops. The WNOHANG is important because without it, after all the status change notifications have been reaped, the waitpid call would block your process until a new notification arrived.

Related

How to cancel waitpid if child has no status change?

Disclaimer: Absolute newbie in C, i was mostly using Java before.
In many C beginner tutorials, waitpid is used in process management examples to wait for its child processes to finish (or have a status change using options like WUNTRACED). However, i couldn't find any information about how to continue if no such status change occurs, either by direct user input or programmatic (e.g. timeout). So what is a good way to undo waitpid? Something like SIGCONT for stopped processes, but instead for processes delayed by waitpid.
Alternatively if the idea makes no sense, it would be interesting to know why.
How about if I suggest using alarm()? alarm() delivers SIGALRM after the countdown passes (See alarm() man page for more details). But from the signals man page, SIGALRM default disposition is to terminate the process. So, you need to register a signal handler for handling the SIGALRM. Code follows like this...
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void sigalrm(int signo)
{
return; // Do nothing !
}
int main()
{
struct sigaction act, oldact;
act.sa_handler = sigalrm; // Set the signal handler
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
#ifdef SA_INTERRUPT // If interrupt defined set it to prevent the auto restart of sys-call
act.sa_flags |= SA_INTERRUPT;
#endif
sigaction(SIGALRM, &act, &oldact);
pid_t fk_return = fork();
if (fk_return == 0) { // Child never returns
for( ; ; );
}
unsigned int wait_sec = 5;
alarm(wait_sec); // Request for SIGALRM
time_t start = time(NULL);
waitpid(-1, NULL, 0);
int tmp_errno = errno; // save the errno state, it may be modified in between function calls.
time_t end = time(NULL);
alarm(0); // Clear a pending alarm
sigaction(SIGALRM, &oldact, NULL);
if (tmp_errno == EINTR) {
printf("Child Timeout, waited for %d sec\n", end - start);
kill(fk_return, SIGINT);
exit(1);
}
else if (tmp_errno != 0) // Some other fatal error
exit(1);
/* Proceed further */
return 0;
}
OUTPUT
Child Timeout, waited for 5 sec
Note: You don't need to worry about SIGCHLD because its default disposition is to ignore.
EDIT
For the completeness, it is guaranteed that SIGALRM is not delivered to the child. This is from the man page of alarm()
Alarms created by alarm() are preserved across execve(2) and are not inherited by children created via fork(2).
EDIT 2
I don't know why it didn't strike me at first. A simple approach would be to block SIGCHLD and call sigtimedwait() which supports timeout option. The code goes like this...
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
sigset_t sigmask;
sigemptyset(&sigmask);
sigaddset(&sigmask, SIGCHLD);
sigprocmask(SIG_BLOCK, &sigmask, NULL);
pid_t fk_return = fork();
if (fk_return == 0) { // Child never returns
for( ; ; );
}
if (sigtimedwait(&sigmask, NULL, &((struct timespec){5, 0})) < 0) {
if (errno == EAGAIN) {
printf("Timeout\n");
kill(fk_return, SIGINT);
exit(1);
}
}
waitpid(fk_return, NULL, 0); // Child should have terminated by now.
/* Proceed further */
return 0;
}
OUTPUT
Timeout
The third argument to waitpid takes a set of flags. You want to include the WNOHANG flag, which tells waitpid to return immediately if no child process has exited.
After adding this option, you would sit in a loop a sleep for some period of time and try again if nothing has exited. Repeat until either a child has returned or until your timeout has passed.
Waiting for process to die on a typical Unix system is an absolute PITA. The portable way would be to use various signals to interrupt wait function: SIGALARM for timeout, SIGTERM/SIGINT and others for "user input" event. This relies on a global state and thus might be impossible to do.
The non-portable way would be to use pidfd_open with poll/epoll on Linux, kqueue with a EVFILT_PROC filter on BSDs.
Note that on Linux this allows waiting for a process to terminate, you will still have to retrieve status via waitid with P_PIDFD.
If you still want to mix in "user events", add signalfd to the list of descriptors on Linux or EVFILT_SIGNAL filter of kqueue on BSDs.
Another possible solution is to spawn a "process reaper" thread which is responsible for reaping of all processes and setting some event in a process object of your choice: futex word, eventfd etc. Waiting on such objects can be done with a timeout. This requires everyone to agree to use the same interface for process spawning which might or might not be reasonable. Afaik Java implementations use this strategy.

Treating signals correctly inside system()

I have been reading "The Linux Programming Interface". Chapter 27, Program execution.
I understand that the author demonstrates how we could implement the system call using exec and fork. However, the challenging part is handling signals. In particular I am confused with the following text
The first signal to consider is SIGCHLD. Suppose that the program
calling system() is also directly creating children, and has
established a handler for SIGCHLD that performs its own wait(). In
this situation, when a SIGCHLD signal is generated by the termination
of the child created by system(), it is possible that the signal
handler of the main program will be invoked and collect the child’s
status before system() has a chance to call waitpid(). (This is an
example of a race condition.)
The following is the code example without signal handling
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
int system(char *command)
{
int status;
pid_t childPid;
switch(childPid = fork())
{
case -1: /* Error */
return -1;
case 0: /* Child */
execl("/bin/sh", "sh", "-c", command, (char*) NULL);
_exit(127); /* If reached this line than execl failed*/
default: /* Parent */
if (waitpid(childPid), &status, 0) == -1)
return -1;
else
return status;
}
}
I know what the race condition ism but don't understand the whole scenario the author describes. In particular, I don't understand what "the program calling system" might be. What is the "main program"? Which process creates child procs?
Could someone, please, explain by giving examples how a race condition can arise? In C or in pseudocode.
You could have a SIGCHLD handler installed that does int ws; wait(&ws);.
If such a SIGCHLD handler is allowed to run in response to a SIGCHLD, it will race with the waitpid done in system, preventing system from successfully retrieving the exit status of the child if the handler wins the race.
For this reason, POSIX prescribes that SIGCHLD be blocked in system.
You could still have races with wait calls done in other signal handlers or other threads, but that would be a design error that POSIX system won't help you with.
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
int system(char *command)
{
int status;
pid_t childPid;
switch(childPid = fork())
{
case -1: /* Error */
return -1;
case 0: /* Child */
execl("/bin/sh", "sh", "-c", command, (char*) NULL);
_exit(127); /* If reached this line than execl failed*/
default: /* Parent */
/*usleep(1);*/
if (waitpid(childPid, &status, 0) == -1)
return -1;
else
return status;
}
}
void sigchld(int Sig){ int er=errno; wait(0); errno=er; }
int main()
{
/*main program*/
//main program has a sigchld handler
struct sigaction sa;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sa.sa_handler = sigchld;
sigaction(SIGCHLD, &sa,0);
for(;;){
//the handler may occasionally steal the child status
if(0>system("true") && errno==ECHILD)
puts("Child status stolen!");
}
}

Does tcsetpgrp() succeds when the caller belong to a background process?

According to the POSIX specification, tcsetpgrp can cause SIGTTOU to be sent to the group of the calling process if it is a background process.
However I can't understand if in such case the foreground group is changed.
Also, if the foreground group is actually changed despite the signal generation, I wonder what happens to the session and to the terminal if the new foreground group is the one that is going to receive the SIGTTOU.
TL:DR:
No the foreground group does not change. This makes sense since the signal is supposed to be sent when the process is changing a setting on the terminal -- an output operation. The signal would also not be delivered to the process (now the foreground group) if the change succeeded, because then it could get stuck without someone to send SIGCONT.
Longer answer:
A simple example:
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
void sig(int signo) {
const char* msg = strsignal(signo); // XXX: Not async-signal-safe.
write(STDOUT_FILENO, msg, strlen(msg));
write(STDOUT_FILENO, "\n", 1);
}
int main() {
char cntl_tty[L_ctermid];
ctermid(cntl_tty);
signal(SIGTTOU, sig);
signal(SIGCONT, sig);
int fd = open(cntl_tty, O_RDONLY);
if (fd == -1) {
perror("open");
exit(1);
}
if (tcsetpgrp(fd, getpgrp()) == -1) {
perror("tcsetpgrp");
} else {
puts("foregrounded");
}
return 0;
}
When this code is started as a background process and SIGTTOU is handled, this loops forever printing that the signal is received. The perror is never called, which implies that the kernel restarts the system call. Sending SIGCONT does not matter. The foregrounding never succeeds. However when foregrounding the code through the shell, "foregrounded" is printed as expected.
When the signal disposition for SIGTTOU is changed to SIG_IGN, "foregrounded" is printed immediately.

my server doesn't handle all signals when I send it a lot of kill() commands

I have a problem with this little piece of code. I have a "server" and a "client". The server waits SIGUSR1 from the client. But when I send SIGUSR1 in a loop, the server doesn't handle every signal !
I do i++ each time I receive a signal, and I get 981 while I send 1000 signals.
usleep() and sleep() doesn't help.
here is the client code:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
int main(int ac, char **av)
{
int i = 0;
int status = 0;
if (ac < 2)
return (0);
printf("kill %s (%d)", av[1], atoi(av[1]));
for (i=0;i<=1000;i++)
{
printf("%d\n", i);
kill(atoi(av[1]), SIGUSR1);
}
kill(atoi(av[1]), SIGUSR2);
}
and the server code :
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int i = 0;
void sig2(int signum)
{
/* usleep(30); */
printf("%d\n", i);
i = 0;
}
void my_handler(int signum)
{
i++;
}
int main()
{
printf("pid: %d\n", getpid());
if (signal(SIGUSR1, my_handler) == SIG_ERR)
{
printf("rt\n");
exit(0);
}
signal(SIGUSR2, sig2);
while (1);
}
You're missing a few signals that are arriving "on top" of each other, too fast for the application to handle.
The standard says:
If a subsequent occurrence of a pending signal is generated, it is implementation-defined as to whether the signal is delivered or accepted more than once in circumstances other than those in which queuing is required.
... which is a somewhat obscure way of saying that ordinary UNIX signals need not queue. On most implementations, they do not. For example, if five SIGFOO signals are generated before they can be disposed of, only one will be held pending and the application will thus receive only one.
The behavior of signal is not consistent between platforms, on some systems signals are one-shot, on others it's repeating. Linux, specifically, uses System V behavior (unless the _BSD_SOURCE macro is defined) which is one-shot. After a signal have been handled, it resets to SIG_DFL.
To get consistent behavior you should be using sigaction instead where the behavior can be set using flags.

Parent process is firing signals at a child process but the child's signal handling needs to be improved

I'm doing some practice questions for an exam and one of the questions gives two pieces of code called parent.c and child.c . Parent creates a child and fires signals at it and child displays a message every time it receives a signal. Child will spend rest of it's time printing a message from main. The question is to describe what is wrong with the signal handling in child.c and to re-write the code to correct it. I get the general idea of signals but have a lot of difficulty implementing them. I'm not sure if procmask in child.c is working properly, I'm not completely comfortable with signals but I can't see why you'd put NULL as the last parameter so maybe that's part of why it's wrong? Could someone please point me in the right direction and give me an idea of what part of the code is wrong and why.
Parent.c
#include <unistd.h>
#include <signal.h>
int
main(int argc, char *argv[])
{
pid_t pid;
sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGUSR1);
sigprocmask(SIG_BLOCK, &set, NULL);
pid = fork();
if (pid == 0) {
execlp("./child", "./child", NULL);
}
while (1) {
kill(pid, SIGUSR1);
}
return (0);
}
Child.c
#define _XOPEN_SOURCE 500
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
static void
handler(int signo)
{
printf("This is the SIGUSR1 signal handler!\n");
}
int
main(void)
{
sigset_t set;
sigemptyset(&set);
sigset(SIGUSR1, handler);
sigprocmask(SIG_SETMASK, &set, NULL);
while (1) {
printf("This is main()!\n");
}
return (0);
}
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
The last parameter is used to store the old signal mask. When it's NULL, it means we don't need to store the old signal mask.
Note that you shouldn't use printf in a signal handler, because it's not reentrant, see How to avoid using printf in a signal handler?
And the usage of execlp is wrong, because NULL could be defined as 0, and the compiler may think it's an integer, not a null pointer.
execlp("./child", "./child", NULL);
The last parameter should be (char *)0, like this:
execlp("./child", (char *)0);
As man sigset states sigset is obsolete and you should use sigaction. Here is an example on how to use it.

Resources