X-Git-Url: https://ruderich.org/simon/gitweb/?p=wall-notify%2Fwall-notify.git;a=blobdiff_plain;f=src%2Fwall-notify.c;h=bbc783b5e884514ca1f793da674ba22d71bc5415;hp=dd37e897a03ccea27adb731e0466fea59b232f6f;hb=0177f8bdc6769e800ee986dec1b069be893f19e6;hpb=f03987a4a9b3d22308c08d336258660d21e3ebf0 diff --git a/src/wall-notify.c b/src/wall-notify.c index dd37e89..bbc783b 100644 --- a/src/wall-notify.c +++ b/src/wall-notify.c @@ -42,6 +42,10 @@ #ifdef USE_UTMPX # include #endif +#ifndef DONT_USE_X11 +# include +# include +#endif static void sig_handler(int signal) { @@ -199,15 +203,50 @@ static int wait_for_write(int fd, int timeout) { return 1; } +#ifdef USE_UTMPX +static void drop_privileges(void) { + uid_t uid, ruid, euid, suid; + gid_t gid, rgid, egid, sgid; + + uid = getuid(); + gid = getgid(); + + /* Drop all privileges. */ + if (setresuid(uid, uid, uid) != 0) { + perror("setresuid"); + exit(EXIT_FAILURE); + } + if (setresgid(gid, gid, gid) != 0) { + perror("setresgid"); + exit(EXIT_FAILURE); + } + + /* Verify all privileges were dropped. */ + if (getresuid(&ruid, &euid, &suid) != 0) { + perror("getresuid"); + exit(EXIT_FAILURE); + } + if (getresgid(&rgid, &egid, &sgid) != 0) { + perror("getresgid"); + exit(EXIT_FAILURE); + } + if (uid == ruid && uid == euid && uid == suid + && gid == rgid && gid == egid && gid == sgid) { + /* Everything fine. */ + return; + } + + fprintf(stderr, "failed to drop privileges, aborting\n"); + exit(EXIT_FAILURE); +} +#endif + static void pass_buffer_to_program(const char *buffer, size_t length, char **argv) { int fds[2]; FILE *fh; pid_t pid; - /* Skip argv[0]. */ - argv++; - if (pipe(fds) != 0) { perror("pipe"); return; @@ -228,6 +267,10 @@ static void pass_buffer_to_program(const char *buffer, size_t length, char **arg } else if (pid == 0) { /* child */ +#ifdef USE_UTMPX + drop_privileges(); +#endif + close(fds[1]); /* write side */ /* Pass read side as stdin to the program. */ @@ -293,17 +336,81 @@ 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 [-X] \n", argv0); + fprintf(stderr, "Pass wall messages to .\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "-X quit when the current X session terminates\n"); +#ifdef DONT_USE_X11 + fprintf(stderr, "\n"); + fprintf(stderr, "compiled without X11 support, -X disabled\n"); +#endif + exit(EXIT_FAILURE); +} int main(int argc, char **argv) { + int option, enable_x11; int ptm, pts; char *name; - if (argc < 2) { - fprintf(stderr, "usage: %s \n", argv[0]); - exit(EXIT_FAILURE); + /* Don't display error messages for unknown options. */ + opterr = 0; + + enable_x11 = 0; + + /* + * Glibc violates POSIX by default and skips over non-option arguments and + * parses later arguments which look like options as well. But we want to + * pass everything except the options unmodified to execvp(). Prefixing + * the optstring with "+" fixes this behaver. This is not POSIX + * compatible, but the option should be ignored on other systems. + */ + while ((option = getopt(argc, argv, "+Xh")) != -1) { + switch (option) { + case 'X': + enable_x11 = 1; + break; + case 'h': + usage(argv[0]); + break; + default: + fprintf(stderr, "%s: unknown option '%s'!\n\n", + argv[0], argv[optind - 1]); + usage(argv[0]); + break; + } } + /* No arguments remaining, abort. */ + if (!argv[optind]) { + usage(argv[0]); + } + /* Arguments for notification program. */ + argv += optind; + ptm = open_tty(); if (ptm < 0) { perror("open_tty"); @@ -327,6 +434,22 @@ int main(int argc, char **argv) { exit(EXIT_FAILURE); } + /* Start a thread which connects to X11. This way we die if the user logs + * out of its X11 session. */ + if (enable_x11) { +#ifdef DONT_USE_X11 + fprintf(stderr, "%s: option -X is disabled!\n\n", argv0); + usage(argv0); +#else + 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();