diff options
Diffstat (limited to 'src/utmp-wtmp.c')
-rw-r--r-- | src/utmp-wtmp.c | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/src/utmp-wtmp.c b/src/utmp-wtmp.c index ba0273f776..5aafb7b722 100644 --- a/src/utmp-wtmp.c +++ b/src/utmp-wtmp.c @@ -24,6 +24,9 @@ #include <assert.h> #include <string.h> #include <sys/utsname.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/poll.h> #include "macro.h" #include "utmp-wtmp.h" @@ -212,3 +215,140 @@ int utmp_put_runlevel(usec_t t, int runlevel, int previous) { return write_entry_both(&store); } + +#define TIMEOUT_MSEC 50 + +static int write_to_terminal(const char *tty, const char *message) { + int fd, r; + const char *p; + size_t left; + usec_t end; + + assert(tty); + assert(message); + + if ((fd = open(tty, O_WRONLY|O_NDELAY|O_NOCTTY|O_CLOEXEC)) < 0) + return -errno; + + if (!isatty(fd)) { + r = -errno; + goto finish; + } + + p = message; + left = strlen(message); + + end = now(CLOCK_MONOTONIC) + TIMEOUT_MSEC*USEC_PER_MSEC; + + while (left > 0) { + ssize_t n; + struct pollfd pollfd; + usec_t t; + int k; + + t = now(CLOCK_MONOTONIC); + + if (t >= end) { + r = -ETIME; + goto finish; + } + + zero(pollfd); + pollfd.fd = fd; + pollfd.events = POLLOUT; + + if ((k = poll(&pollfd, 1, (end - t) / USEC_PER_MSEC)) < 0) + return -errno; + + if (k <= 0) { + r = -ETIME; + goto finish; + } + + if ((n = write(fd, p, left)) < 0) { + + if (errno == EAGAIN) + continue; + + r = -errno; + goto finish; + } + + assert((size_t) n <= left); + + p += n; + left -= n; + } + + r = 0; + +finish: + close_nointr_nofail(fd); + + return r; +} + +int utmp_wall(const char *message) { + struct utmpx *u; + char date[26]; + char *text, *hn, *un, *tty; + int r; + time_t t; + + if (!(hn = gethostname_malloc()) || + !(un = getlogname_malloc()) || + !(tty = getttyname_malloc())) { + r = -ENOMEM; + goto finish; + } + + time(&t); + assert_se(ctime_r(&t, date)); + delete_chars(date, "\n\r"); + + if (asprintf(&text, + "\a\r\n" + "Broadcast message from %s@%s on %s (%s):\r\n\r\n" + "%s\r\n\r\n", + un, hn, tty, date, message) < 0) { + r = -ENOMEM; + goto finish; + } + + setutxent(); + + r = 0; + + while ((u = getutxent())) { + int q; + const char *path; + char *buf = NULL; + + if (u->ut_type != USER_PROCESS || u->ut_user[0] == 0) + continue; + + if (path_startswith(u->ut_line, "/dev/")) + path = u->ut_line; + else { + if (asprintf(&buf, "/dev/%s", u->ut_line) < 0) { + r = -ENOMEM; + goto finish; + } + + path = buf; + } + + if ((q = write_to_terminal(path, text)) < 0) + r = q; + + free(buf); + } + +finish: + free(hn); + free(un); + free(tty); + free(text); + + return r; +} |