diff options
-rw-r--r-- | src/systemctl.c | 23 | ||||
-rw-r--r-- | src/util.c | 64 | ||||
-rw-r--r-- | src/util.h | 4 | ||||
-rw-r--r-- | src/utmp-wtmp.c | 140 | ||||
-rw-r--r-- | src/utmp-wtmp.h | 2 |
5 files changed, 229 insertions, 4 deletions
diff --git a/src/systemctl.c b/src/systemctl.c index 850efe2dd4..aad9ba039b 100644 --- a/src/systemctl.c +++ b/src/systemctl.c @@ -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: diff --git a/src/util.c b/src/util.c index 4fdc8e57c5..0988675f09 100644 --- a/src/util.c +++ b/src/util.c @@ -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", diff --git a/src/util.h b/src/util.h index f3aec1d43a..e371f0b9e5 100644 --- a/src/util.h +++ b/src/util.h @@ -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); 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; +} diff --git a/src/utmp-wtmp.h b/src/utmp-wtmp.h index 34c3222c99..abdf28a5f9 100644 --- a/src/utmp-wtmp.h +++ b/src/utmp-wtmp.h @@ -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 |