X-Git-Url: https://ruderich.org/simon/gitweb/?a=blobdiff_plain;f=src%2Fwall-notify.c;h=0e77e97ecce2d8638fd91ec27cc83a88d6cec8ba;hb=702d46155ea3f168dea38e513748b15f256d8b2e;hp=292b71a1d9d026208db979be56cc5c03bdffb4d0;hpb=3553a29a54f7755debfa189db830c4b8de51826f;p=wall-notify%2Fwall-notify.git diff --git a/src/wall-notify.c b/src/wall-notify.c index 292b71a..0e77e97 100644 --- a/src/wall-notify.c +++ b/src/wall-notify.c @@ -1,7 +1,7 @@ /* * Receive wall messages and pass them to a notification program via stdin. * - * Copyright (C) 2014 Simon Ruderich + * Copyright (C) 2014-2015 Simon Ruderich * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -48,8 +48,13 @@ #endif +static sig_atomic_t signaled = 0; + + static void sig_handler(int signal) { (void)signal; + + signaled = 1; } static void setup_signals(void) { struct sigaction action; @@ -67,7 +72,18 @@ static void setup_signals(void) { sigaction(SIGUSR2, &action, NULL); } -static int open_tty(void) { +static int set_cloexec(int fd) { + int flags = fcntl(fd, F_GETFD); + if (flags == -1) { + return 0; + } + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) { + return 0; + } + return 1; +} + +static int open_tty(int public) { int ptm; const char *name; @@ -78,15 +94,20 @@ static int open_tty(void) { if (grantpt(ptm) != 0) { return -1; } + if (!set_cloexec(ptm)) { + return -1; + } /* Prevent write access for other users so they can't use wall to send * messages to this program. */ - name = ptsname(ptm); - if (!name) { - return -1; - } - if (chmod(name, S_IRUSR | S_IWUSR) != 0) { - return -1; + if (!public) { + name = ptsname(ptm); + if (!name) { + return -1; + } + if (chmod(name, S_IRUSR | S_IWUSR) != 0) { + return -1; + } } if (unlockpt(ptm) != 0) { @@ -212,14 +233,14 @@ static void drop_privileges(void) { 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); } + if (setresuid(uid, uid, uid) != 0) { + perror("setresuid"); + exit(EXIT_FAILURE); + } /* Verify all privileges were dropped. */ if (getresuid(&ruid, &euid, &suid) != 0) { @@ -316,7 +337,7 @@ static void handle_wall(int fd, char **argv) { ssize_t r; assert(SSIZE_MAX <= SIZE_MAX); - while ((r = read(fd, buffer, sizeof(buffer))) > 0) { + while (!signaled && (r = read(fd, buffer, sizeof(buffer))) > 0) { size_t space; ssize_t r2; @@ -359,25 +380,74 @@ static void *x11_event_loop_thread(void *unused) { #endif static void usage(const char *argv0) { - fprintf(stderr, "usage: %s \n", argv0); + fprintf(stderr, "usage: %s [-X] [-m] [-v] \n", argv0); + fprintf(stderr, "Pass wall messages to .\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "-X quit when the current X session terminates\n"); + fprintf(stderr, "-m allow messages from normal users (like `mesg y`)\n"); + fprintf(stderr, "-v display version\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, mesg_yes; + const char *argv0; int ptm, pts; char *name; - const char *argv0; + + /* Don't display error messages for unknown options. */ + opterr = 0; + + enable_x11 = 0; + mesg_yes = 0; argv0 = argv[0]; - /* Don't pass our argv[0] to the notification program. */ - argv++; - if (argc < 2) { + /* + * 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, "+Xmhv")) != -1) { + switch (option) { + case 'X': + enable_x11 = 1; + break; + case 'm': + mesg_yes = 1; + break; + case 'h': + usage(argv0); + break; + case 'v': + printf("%s%s\n", + PACKAGE_STRING, + strlen(GIT_VERSION) ? " (Git " GIT_VERSION ")" : ""); + exit(EXIT_SUCCESS); + break; + default: + fprintf(stderr, "%s: unknown option '%s'!\n\n", + argv0, argv[optind - 1]); + usage(argv0); + break; + } + } + + /* No arguments remaining, abort. */ + if (!argv[optind]) { usage(argv0); } + /* Arguments for notification program. */ + argv += optind; - ptm = open_tty(); + ptm = open_tty(mesg_yes); if (ptm < 0) { perror("open_tty"); exit(EXIT_FAILURE); @@ -394,24 +464,27 @@ int main(int argc, char **argv) { /* We need to open the slave or reading from the master yields EOF after * the first wall write to it. */ - pts = open(name, O_RDWR); + pts = open(name, O_RDWR | O_CLOEXEC); if (pts < 0) { perror(name); 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. */ - { + 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();