chiark / gitweb /
systemctl: sort 'list-units' output
[elogind.git] / src / utmp-wtmp.c
index cb3f201322edbef648ca68b7bf5cb8933d231711..46dfba336aaa7317ba9824cbc230b20e71e0be6f 100644 (file)
@@ -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"
@@ -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;
 
@@ -199,12 +202,12 @@ int utmp_put_runlevel(usec_t timestamp, int runlevel, int previous) {
 
                         previous = 0;
                 }
-
-                if (previous == runlevel)
-                        return 0;
         }
 
-        init_entry(&store, timestamp);
+        if (previous == runlevel)
+                return 0;
+
+        init_entry(&store, t);
 
         store.ut_type = RUN_LVL;
         store.ut_pid = (runlevel & 0xFF) | ((previous & 0xFF) << 8);
@@ -212,3 +215,142 @@ 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 = NULL, *hn = NULL, *un = NULL, *tty = NULL;
+        int r;
+        time_t t;
+
+        if (!(hn = gethostname_malloc()) ||
+            !(un = getlogname_malloc())) {
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if ((r = getttyname_malloc(&tty)) < 0)
+                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;
+}