Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix handling of multiple SIGUSR1 signals #318

Merged
merged 2 commits into from
Oct 7, 2023

Conversation

mstoeckl
Copy link
Contributor

@mstoeckl mstoeckl commented Oct 7, 2023

The first commit fixes an issue in which, on receiving a second SIGUSR1, swaylock would exit without unlocking, printing User defined signal 1. The second fixes a deadlock which can occur if too many signals are sent in too short of a time; depending on how quickly signals are sent and on the pipe buffer size, the deadlock may prevent swaylock from exiting, possibly before it can send the unlock message.

To test this, I used a short program that sends signals in a tight loop:

signal_flood.c
// gcc signal_flood.c -o signal_flood
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>

int main(int argc, char **argv) {
	if (argc != 4) {
		printf("Usage: signal_flood PID SIGNUM NREPEAT\n");
		printf("SIGINT = 2\n");
		printf("SIGKILL = 9\n");
		printf("SIGUSR1 = 10\n");
		printf("SIGUSR2 = 12\n");
		printf("SIGTERM = 15\n");
		printf("SIGCONT = 18\n");
		printf("SIGSTOP = 19\n");
		return EXIT_FAILURE;
	}
	char *end;
	int sig = strtol(argv[2], &end, 10);
	if (*end) {
		printf("SIGNUM (%s) was not a number\n", argv[2]);
		return EXIT_FAILURE;
	}
	int nrepeat = strtol(argv[3], &end, 10);;
	if (*end) {
		printf("NREPEAT (%s) was not a number\n", argv[3]);
		return EXIT_FAILURE;
	}
	pid_t pid = strtol(argv[1], &end, 10);;
	if (*end) {
		printf("PID (%s) was not a number\n", argv[1]);
		return EXIT_FAILURE;
	}
	if (pid == 0) {
		printf("PID should be nonzero\n");
		return EXIT_FAILURE;
	}
	for (int i = 0; i < nrepeat; i++) {
		if (kill(pid, sig) == -1) {
			printf("Iteration %d, sending signal %d to %d failed\n",i,sig,pid);
			break;
		}
	}

	return EXIT_SUCCESS;
}

If signal() is used to set a signal handler, only the first signal
received will use the handler; any later signals use the default
behavior (making swaylock terminate immediately, without unlocking).
Using sigaction() ensures that the handler will be used every time.
If a large number of signals is sent, it is possible to fill the
buffer for sigusr_fds[1] before the main loop has a chance to read
from it; then the signal handler do_sigusr() will block on write.
main.c Show resolved Hide resolved
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants