X-Git-Url: https://ruderich.org/simon/gitweb/?a=blobdiff_plain;f=src%2Fwall-notify.c;h=40cdfe587870bc46f6ae5cbfc6b8c0308a74d70e;hb=1eb18876bb0eb8f55567dd8a92ab10091d045df7;hp=343f3d0ff37bfbb5a6798ee70158dbbed0cceb4a;hpb=5b093d27e49c3fd96d262b11b08f790dc290454e;p=wall-notify%2Fwall-notify.git diff --git a/src/wall-notify.c b/src/wall-notify.c index 343f3d0..40cdfe5 100644 --- a/src/wall-notify.c +++ b/src/wall-notify.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #ifdef USE_UTEMPTER @@ -41,6 +42,10 @@ #ifdef USE_UTMPX # include #endif +#ifndef DONT_USE_X11 +# include +# include +#endif static void sig_handler(int signal) { @@ -60,9 +65,6 @@ static void setup_signals(void) { sigaction(SIGQUIT, &action, NULL); sigaction(SIGUSR1, &action, NULL); sigaction(SIGUSR2, &action, NULL); - - /* Collect zombies automatically without having to call waitpid(2). */ - signal(SIGCHLD, SIG_IGN); } static int open_tty(void) { @@ -145,7 +147,16 @@ static int set_utmpx(short type, int ptm) { #endif static int login(int ptm) { #if defined(USE_UTEMPTER) - return utempter_add_record(ptm, NULL); + int result = utempter_add_record(ptm, NULL); + /* Exit value of utempter_*() is not correct on all systems, e.g. + * FreeBSD() always returns 0. Checking the utmpx database manually is + * difficult because we don't know the exact values for ut_id and ut_line, + * therefore we only check the return value on systems known to return a + * useful value. */ +# ifndef __linux + result = 1; +# endif + return result; #elif defined(USE_UTMPX) return set_utmpx(USER_PROCESS, ptm); #else @@ -154,7 +165,12 @@ static int login(int ptm) { } static int logout(int ptm) { #if defined(USE_UTEMPTER) - return utempter_remove_record(ptm); + int result = utempter_remove_record(ptm); + /* See above. */ +# ifndef __linux + result = 1; +# endif + return result; #elif defined(USE_UTMPX) return set_utmpx(DEAD_PROCESS, ptm); #else @@ -193,9 +209,6 @@ static void pass_buffer_to_program(const char *buffer, size_t length, char **arg pid_t pid; - /* Skip argv[0]. */ - argv++; - if (pipe(fds) != 0) { perror("pipe"); return; @@ -225,9 +238,21 @@ static void pass_buffer_to_program(const char *buffer, size_t length, char **arg } close(fds[0]); - execvp(argv[0], argv); - perror("execvp"); - exit(EXIT_FAILURE); + /* Double fork so `wall-notify` doesn't have to wait for the + * notification process to terminate. We can't just use + * signal(SIGCHLD, SIG_IGN); because utempter on at least FreeBSD + * doesn't work if SIGCHLD is ignored. */ + pid = fork(); + if (pid < 0) { + perror("fork"); + exit(EXIT_FAILURE); + } else if (pid == 0) { + execvp(argv[0], argv); + perror("execvp"); + exit(EXIT_FAILURE); + } + + exit(EXIT_SUCCESS); } /* father */ @@ -239,6 +264,10 @@ static void pass_buffer_to_program(const char *buffer, size_t length, char **arg out: close(fds[0]); /* read side */ fclose(fh); + + if (waitpid(pid, NULL, 0) < 0) { + perror("waitpid"); + } } static void handle_wall(int fd, char **argv) { char buffer[4096]; @@ -265,15 +294,45 @@ static void handle_wall(int fd, char **argv) { pass_buffer_to_program(buffer, (size_t)r, argv); } } +#ifndef DONT_USE_X11 +static void *x11_event_loop_thread(void *unused) { + Display *display; + XEvent event; + + (void)unused; + + pthread_detach(pthread_self()); + + display = XOpenDisplay(NULL); + if (!display) { + fprintf(stderr, "failed to connect to X server\n"); + exit(EXIT_FAILURE); + } + + /* Do nothing. We just want to die if the X11 session is closed. */ + while (1) { + XNextEvent(display, &event); + } +} +#endif + +static void usage(const char *argv0) { + fprintf(stderr, "usage: %s \n", argv0); + exit(EXIT_FAILURE); +} int main(int argc, char **argv) { int ptm, pts; char *name; + const char *argv0; + + argv0 = argv[0]; + /* Don't pass our argv[0] to the notification program. */ + argv++; if (argc < 2) { - fprintf(stderr, "usage: %s \n", argv[0]); - exit(EXIT_FAILURE); + usage(argv0); } ptm = open_tty(); @@ -299,6 +358,19 @@ int main(int argc, char **argv) { exit(EXIT_FAILURE); } +#ifndef DONT_USE_X11 + /* Start a thread which connects to X11. This way we die if the user logs + * out of its X11 session. */ + { + pthread_t tid; + + if (pthread_create(&tid, NULL, x11_event_loop_thread, NULL) != 0) { + perror("pthread_create"); + exit(EXIT_FAILURE); + } + } +#endif + /* Cleanup on signals. Necessary before login(). */ setup_signals();