X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Futmp-wtmp.c;h=5aafb7b722272b411c051746a2bc834f64e0475f;hp=cb3f201322edbef648ca68b7bf5cb8933d231711;hb=ef2f1067d0ca0e6d7346aa3e082048821b670b54;hpb=e99e38bbdcca3fe5956823bdb3d38544ccf93221 diff --git a/src/utmp-wtmp.c b/src/utmp-wtmp.c index cb3f20132..5aafb7b72 100644 --- a/src/utmp-wtmp.c +++ b/src/utmp-wtmp.c @@ -24,6 +24,9 @@ #include #include #include +#include +#include +#include #include "macro.h" #include "utmp-wtmp.h" @@ -89,7 +92,7 @@ int utmp_get_runlevel(int *runlevel, int *previous) { return r; } -static void init_entry(struct utmpx *store, usec_t timestamp) { +static void init_entry(struct utmpx *store, usec_t t) { struct utsname uts; assert(store); @@ -97,11 +100,11 @@ static void init_entry(struct utmpx *store, usec_t timestamp) { zero(*store); zero(uts); - if (timestamp <= 0) - timestamp = now(CLOCK_REALTIME); + if (t <= 0) + t = now(CLOCK_REALTIME); - store->ut_tv.tv_sec = timestamp / USEC_PER_SEC; - store->ut_tv.tv_usec = timestamp % USEC_PER_SEC; + store->ut_tv.tv_sec = t / USEC_PER_SEC; + store->ut_tv.tv_usec = t % USEC_PER_SEC; if (uname(&uts) >= 0) strncpy(store->ut_host, uts.release, sizeof(store->ut_host)); @@ -162,10 +165,10 @@ static int write_entry_both(const struct utmpx *store) { return r; } -int utmp_put_shutdown(usec_t timestamp) { +int utmp_put_shutdown(usec_t t) { struct utmpx store; - init_entry(&store, timestamp); + init_entry(&store, t); store.ut_type = RUN_LVL; strncpy(store.ut_user, "shutdown", sizeof(store.ut_user)); @@ -173,10 +176,10 @@ int utmp_put_shutdown(usec_t timestamp) { return write_entry_both(&store); } -int utmp_put_reboot(usec_t timestamp) { +int utmp_put_reboot(usec_t t) { struct utmpx store; - init_entry(&store, timestamp); + init_entry(&store, t); store.ut_type = BOOT_TIME; strncpy(store.ut_user, "reboot", sizeof(store.ut_user)); @@ -184,7 +187,7 @@ int utmp_put_reboot(usec_t timestamp) { return write_entry_both(&store); } -int utmp_put_runlevel(usec_t timestamp, int runlevel, int previous) { +int utmp_put_runlevel(usec_t t, int runlevel, int previous) { struct utmpx store; int r; @@ -204,7 +207,7 @@ int utmp_put_runlevel(usec_t timestamp, int runlevel, int previous) { return 0; } - init_entry(&store, timestamp); + init_entry(&store, t); store.ut_type = RUN_LVL; store.ut_pid = (runlevel & 0xFF) | ((previous & 0xFF) << 8); @@ -212,3 +215,140 @@ int utmp_put_runlevel(usec_t timestamp, 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; +}