chiark / gitweb /
systemctl: warn users via wall that the system goes down
authorLennart Poettering <lennart@poettering.net>
Fri, 18 Jun 2010 00:28:35 +0000 (02:28 +0200)
committerLennart Poettering <lennart@poettering.net>
Fri, 18 Jun 2010 00:28:35 +0000 (02:28 +0200)
src/systemctl.c
src/util.c
src/util.h
src/utmp-wtmp.c
src/utmp-wtmp.h

index 850efe2dd44be04e3abf109ebb3f792b31ebfb1a..aad9ba039b32498765465def338d88d4e33e67fa 100644 (file)
@@ -53,7 +53,6 @@ enum action {
         ACTION_HALT,
         ACTION_POWEROFF,
         ACTION_REBOOT,
-        ACTION_RUNLEVEL1,
         ACTION_RUNLEVEL2,
         ACTION_RUNLEVEL3,
         ACTION_RUNLEVEL4,
@@ -117,6 +116,20 @@ static int columns(void) {
 
 }
 
+static void warn_wall(void) {
+        static const char *table[_ACTION_MAX] = {
+                [ACTION_HALT]     = "The system is going down for system halt NOW!",
+                [ACTION_REBOOT]   = "The system is going down for reboot NOW!",
+                [ACTION_POWEROFF] = "The system is going down for power-off NOW!",
+                [ACTION_RESCUE]   = "The system is going down to rescue mode NOW!"
+        };
+
+        if (!table[arg_action])
+                return;
+
+        utmp_wall(table[arg_action]);
+}
+
 static int list_units(DBusConnection *bus, char **args, unsigned n) {
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
@@ -662,7 +675,6 @@ static int start_unit(DBusConnection *bus, char **args, unsigned n) {
                 [ACTION_HALT] = "halt.target",
                 [ACTION_POWEROFF] = "poweroff.target",
                 [ACTION_REBOOT] = "reboot.target",
-                [ACTION_RUNLEVEL1] = "runlevel1.target",
                 [ACTION_RUNLEVEL2] = "runlevel2.target",
                 [ACTION_RUNLEVEL3] = "runlevel3.target",
                 [ACTION_RUNLEVEL4] = "runlevel4.target",
@@ -1648,7 +1660,7 @@ static int telinit_parse_argv(int argc, char *argv[]) {
         } table[] = {
                 { '0', ACTION_POWEROFF },
                 { '6', ACTION_REBOOT },
-                { '1', ACTION_RUNLEVEL1 },
+                { '1', ACTION_RESCUE },
                 { '2', ACTION_RUNLEVEL2 },
                 { '3', ACTION_RUNLEVEL3 },
                 { '4', ACTION_RUNLEVEL4 },
@@ -1907,6 +1919,8 @@ static int reload_with_fallback(DBusConnection *bus) {
 static int start_with_fallback(DBusConnection *bus) {
         int r;
 
+        warn_wall();
+
         if (bus) {
                 /* First, try systemd via D-Bus. */
                 if ((r = start_unit(bus, NULL, 0)) > 0)
@@ -1929,6 +1943,8 @@ static int halt_main(DBusConnection *bus) {
         if (!arg_immediate)
                 return start_with_fallback(bus);
 
+        warn_wall();
+
         if (!arg_no_wtmp)
                 if ((r = utmp_put_shutdown(0)) < 0)
                         log_warning("Failed to write utmp record: %s", strerror(-r));
@@ -2028,7 +2044,6 @@ int main(int argc, char*argv[]) {
                 retval = halt_main(bus) < 0;
                 break;
 
-        case ACTION_RUNLEVEL1:
         case ACTION_RUNLEVEL2:
         case ACTION_RUNLEVEL3:
         case ACTION_RUNLEVEL4:
index 4fdc8e57c5bbe93db072ee978e9ae36eb5055685..0988675f09be39f291cd565b446ffeb0dba4dd1f 100644 (file)
@@ -44,6 +44,8 @@
 #include <libgen.h>
 #include <ctype.h>
 #include <sys/prctl.h>
+#include <sys/utsname.h>
+#include <pwd.h>
 
 #include "macro.h"
 #include "util.h"
@@ -2217,6 +2219,68 @@ void sigset_add_many(sigset_t *ss, ...) {
         va_end(ap);
 }
 
+char* gethostname_malloc(void) {
+        struct utsname u;
+
+        assert_se(uname(&u) >= 0);
+
+        if (u.nodename[0])
+                return strdup(u.nodename);
+
+        return strdup(u.sysname);
+}
+
+char* getlogname_malloc(void) {
+        uid_t uid;
+        long bufsize;
+        char *buf, *name;
+        struct passwd pwbuf, *pw = NULL;
+        struct stat st;
+
+        if (isatty(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0)
+                uid = st.st_uid;
+        else
+                uid = getuid();
+
+        /* Shortcut things to avoid NSS lookups */
+        if (uid == 0)
+                return strdup("root");
+
+        if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) <= 0)
+                bufsize = 4096;
+
+        if (!(buf = malloc(bufsize)))
+                return NULL;
+
+        if (getpwuid_r(uid, &pwbuf, buf, bufsize, &pw) == 0 && pw) {
+                name = strdup(pw->pw_name);
+                free(buf);
+                return name;
+        }
+
+        free(buf);
+
+        if (asprintf(&name, "%lu", (unsigned long) uid) < 0)
+                return NULL;
+
+        return name;
+}
+
+char *getttyname_malloc(void) {
+        char path[PATH_MAX], *p;
+
+        if (ttyname_r(STDIN_FILENO, path, sizeof(path)) < 0)
+                return strdup("unknown");
+
+        char_array_0(path);
+
+        p = path;
+        if (startswith(path, "/dev/"))
+                p += 5;
+
+        return strdup(p);
+}
+
 static const char *const ioprio_class_table[] = {
         [IOPRIO_CLASS_NONE] = "none",
         [IOPRIO_CLASS_RT] = "realtime",
index f3aec1d43a3611529c373ac38ef02bf4d89ba541..e371f0b9e54a5f725c8864d48a731345bdcddca1 100644 (file)
@@ -258,6 +258,10 @@ void rename_process(const char name[8]);
 
 void sigset_add_many(sigset_t *ss, ...);
 
+char* gethostname_malloc(void);
+char* getlogname_malloc(void);
+char *getttyname_malloc(void);
+
 const char *ioprio_class_to_string(int i);
 int ioprio_class_from_string(const char *s);
 
index ba0273f7763aeac786eeec7b31bd3de29158de7d..5aafb7b722272b411c051746a2bc834f64e0475f 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"
@@ -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;
+}
index 34c3222c99e0cf0634c2e9f3c15193055ee4e4f1..abdf28a5f9efea1b3c25d7673b9a570be9e9b0ec 100644 (file)
@@ -30,4 +30,6 @@ int utmp_put_shutdown(usec_t timestamp);
 int utmp_put_reboot(usec_t timestamp);
 int utmp_put_runlevel(usec_t timestamp, int runlevel, int previous);
 
+int utmp_wall(const char *message);
+
 #endif