/*
* 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
#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;
sigaction(SIGUSR2, &action, NULL);
}
+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;
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. */
if (!public) {
- name = ptsname(ptm);
- if (!name) {
- return -1;
- }
- if (chmod(name, S_IRUSR | S_IWUSR) != 0) {
- return -1;
- }
+ name = ptsname(ptm);
+ if (!name) {
+ return -1;
+ }
+ if (chmod(name, S_IRUSR | S_IWUSR) != 0) {
+ return -1;
+ }
}
if (unlockpt(ptm) != 0) {
static int login(int ptm) {
#if defined(USE_UTEMPTER)
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. */
+ /* 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
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) {
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;
#endif
static void usage(const char *argv0) {
- fprintf(stderr, "usage: %s [-X] [-m] <cmd args..>\n", argv0);
+ fprintf(stderr, "usage: %s [-X] [-m] [-v] <cmd args..>\n", argv0);
fprintf(stderr, "Pass wall messages to <cmd args..>.\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");
int main(int argc, char **argv) {
int option, enable_x11, mesg_yes;
+ const char *argv0;
int ptm, pts;
char *name;
enable_x11 = 0;
mesg_yes = 0;
+ argv0 = argv[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
* 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, "+Xmh")) != -1) {
+ while ((option = getopt(argc, argv, "+Xmhv")) != -1) {
switch (option) {
case 'X':
enable_x11 = 1;
break;
case 'm':
mesg_yes = 1;
+ break;
case 'h':
- usage(argv[0]);
+ 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",
- argv[0], argv[optind - 1]);
- usage(argv[0]);
+ argv0, argv[optind - 1]);
+ usage(argv0);
break;
}
}
/* No arguments remaining, abort. */
if (!argv[optind]) {
- usage(argv[0]);
+ usage(argv0);
}
/* Arguments for notification program. */
argv += optind;
/* 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);