~charles/cdaniels.net

6d8bc256a9d538908a661f9b4c5ab8cf91ee16ed — Charles Daniels a month ago b13b1f1
add a signal-safe example to sigusr post
1 files changed, 88 insertions(+), 0 deletions(-)

M src/sigusr.md
M src/sigusr.md => src/sigusr.md +88 -0
@@ 128,6 128,94 @@ Waiting... (Mon Jun 22 20:15:27 2020)
Caught signal 10 atMon Jun 22 20:15:35 2020
```

<h2 class=navhdr id="undefined-behavior">
	Eliminating Undefined Behavior
	<a href=#undefined-behavior class=navhandle>¶ </a>
</h2>

*After publishing the original version of this article, my friend [Joshua
Nelson](https://jyn514.github.io/) reminded me that the example presented above
exhibits undefined behavior: `printf()` is not
[signal-safe](https://man7.org/linux/man-pages/man7/signal-safety.7.html).*

Fortunately, eliminating this undefined behavior is quite easy: we can simply
have a global flag which the handler sets, with the main function simply
sitting in an idle loop until the flag is changed by the signal handler. We
could simply poll in a hot loop, or perhaps add a short delay to avoid burning
too many CPU cycles, but fortunately there is a function to wait until a signal
is received: [pause(2)](https://linux.die.net/man/2/pause). This leads us to
a slightly modified variation of our previous program which does not contain
any undefined behavior...

```c
#define _GNU_SOURCE
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <err.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>

int flag_sigusr1;

/**
 * @brief Displays the current local time on standard out.
 */
void print_time(void) {
	/* get the current time and convert to local time */
	time_t t;
	struct tm* l;
	time (&t);
	l = localtime(&t);

	/* generate a timestamp string, and remove the trailing newline */
	char* ts;
	asprintf(&ts, "%s", asctime(l));
	ts[strlen(ts)-1] = '\0'; 

	/* display the timestamp */
	printf("%s", ts);
	free(ts);
}

/**
 * @brief Handler for SIGUSR1
 *
 * @param signo
 */
void handler(int signo) {
	flag_sigusr1 = 1;
}

int main(void) {
	flag_sigusr1 = 0;

	/* register the signal handler */
	if (signal(SIGUSR1, handler) == SIG_ERR) {
		err(1, "Failed to register signal handler");
	}

	/* wait forever, to give us time to send the signal */
	printf("Beginning wait at ");
	print_time();
	printf("\n");

	while(1) {

		/* wait for some signal to be received by the program */
		pause();

		if (flag_sigusr1 != 0) {
			printf("Caught signal SIGUSR1 at: ");
			print_time();
			printf("\n");
			flag_sigusr1 = 0;
		}
	}
}
```

<h2 class=navhdr id="why">
	Why This is Useful
	<a href=#why class=navhandle>¶ </a>