books/apitue/sample-code/07/pending.c

173 lines
5.1 KiB
C

/* This file is part of the sample code and exercises
* used by the class "Advanced Programming in the UNIX
* Environment" taught by Jan Schaumann
* <jschauma@netmeister.org> at Stevens Institute of
* Technology.
*
* This file is in the public domain.
*
* You don't have to, but if you feel like
* acknowledging where you got this code, you may
* reference me by name, email address, or point
* people to the course website:
* https://stevens.netmeister.org/631/
*/
/* This program illustrates a number of perhaps confusing features of
* signal delivery. It shows how signals can be blocked, how pending
* signals are merged, how you can jump out of a signal handler into
* another, how resetting signal handlers differ from non-resetting signal
* handlers and how to check whether or not pending signals are of a
* certain type.
*
* You want to run this program a number of times to illustrate all of the
* different features. Hopefully the commentary in the code below will
* help you remember all the things you want to demonstrate.
*/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifndef SLEEP
#define SLEEP 5
#endif
int s = 0;
void
sig_quit_reset(int signo) {
fprintf(stderr, "sig_quit_reset: caught SIGQUIT (%d), sleeping and resetting.\n", ++s);
sleep(SLEEP);
if (signal(SIGQUIT, SIG_DFL) == SIG_ERR) {
fprintf(stderr, "can't reset SIGQUIT\n");
exit(1);
}
fprintf(stderr, "sig_quit_reset: restored SIGQUIT handler to default.\n");
}
void
sig_quit(int signo) {
fprintf(stderr, "sig_quit: caught SIGQUIT (%d), now sleeping\n", ++s);
sleep(SLEEP);
fprintf(stderr, "sig_quit: exiting (%d)\n", s);
}
void
sig_int(int signo) {
fprintf(stderr, "sig_int: caught SIGINT (%d), returning immediately\n", ++s);
}
int main() {
sigset_t newmask, oldmask, pendmask;
int ismember = 0;
printf("\n=> Establishing initial signal hander via signal(3).\n");
if(signal(SIGQUIT, sig_quit) == SIG_ERR) {
fprintf(stderr, "can't set signal handler\n");
exit(1);
}
if(signal(SIGINT, sig_int) == SIG_ERR) {
fprintf(stderr, "can't set signal handler\n");
exit(1);
}
/* Hit ^\ to jump into sig_quit.
* Hit ^\ again and note that we're not interrupting the sleep
* inside the sig_quit handler. However, the signal gets
* delivered once sig_quit finishes, so we re-enter sig_quit.
*
* Multiple signals of the same kind are merged, so hitting ^\
* multiple times while in sig_quit only yields a single signal
* being delivered after we finish in sig_quit.
*
* However: hit ^\, then ^C and note that sig_int executes
* immediately. We were transferred out of sig_quit, then
* returned immediately back into sig_quit.
*
* If we start with one ^\ followed by ^C, then we immediately
* move on establishing the resetting signal handler; if we hit ^\
* multiple times followed by ^C, then our first ^\ is caught, the
* subsequent ones are merged and queued, sig_int is executed and
* the queued ^\ delivered.
*/
sleep(SLEEP);
printf("\n=> Time for a second interruption.\n");
sleep(SLEEP);
printf("\n=> Establishing a resetting signal hander via signal(3).\n");
if(signal(SIGQUIT, sig_quit_reset) == SIG_ERR) {
fprintf(stderr, "can't set signal handler\n");
exit(1);
}
/* If we repeat the same thing here, things are rather different:
* Hit ^\ and we jump into sig_quit_reset, which resets the signal
* disposition to the original default (which is to quit with a
* core dump). If you hit ^\ while in sig_quit_reset, the signal
* is queued and delivered as soon as the signal handler
* terminates. At that point, however, the default signal handler
* has been installed, and the program exits.
*
* If we do not deliver a second ^\, then we establish a blocking
* mask for this signal, allowing multiple subsequent ^\ to be
* blocked. We then report on whether or not we have any, and
* finally unblock them, ready to be delivered (to the default
* handler).
*/
sleep(SLEEP);
printf("\n=> Time for a second interruption.\n");
sleep(SLEEP);
sigemptyset(&newmask);
sigaddset(&newmask, SIGQUIT);
printf("\n=> Blocking delivery of SIGQUIT...\n");
if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) {
fprintf(stderr, "SIG_BLOCK error\n");
exit(1);
}
printf("\n=> Now going to sleep for %d seconds...\n", SLEEP);
sleep(SLEEP);
printf("\n=> Checking if any signals are pending...\n");
if (sigpending(&pendmask) < 0) {
fprintf(stderr, "sigpending error\n");
exit(1);
}
ismember = sigismember(&pendmask, SIGQUIT);
if (ismember < 0) {
fprintf(stderr, "sigismember error\n");
exit(1);
} else if (ismember == 1) {
printf("Pending SIGQUIT found.\n");
}
printf("\n=> Unblocking SIGQUIT...\n");
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) {
fprintf(stderr, "SIG_SETMASK error\n");
exit(1);
}
printf("SIGQUIT unblocked - sleeping some more...\n");
/* Finally, if we have not delivered a second ^\ after the
* resetting handler, then we get here and can simply deliver
* another ^\ now, exiting the program.
*/
sleep(SLEEP);
printf("Now exiting.\n");
exit(0);
}