]> ruderich.org/simon Gitweb - wall-notify/wall-notify.git/blobdiff - src/wall-notify.c
exit if the current X11 session terminates
[wall-notify/wall-notify.git] / src / wall-notify.c
index 343f3d0ff37bfbb5a6798ee70158dbbed0cceb4a..40cdfe587870bc46f6ae5cbfc6b8c0308a74d70e 100644 (file)
@@ -33,6 +33,7 @@
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <sys/types.h>
+#include <sys/wait.h>
 #include <unistd.h>
 
 #ifdef USE_UTEMPTER
 #ifdef USE_UTMPX
 # include <utmpx.h>
 #endif
+#ifndef DONT_USE_X11
+# include <pthread.h>
+# include <X11/Xlib.h>
+#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 <cmd args..>\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 <cmd args..>\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();