summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/systemctl.c23
-rw-r--r--src/util.c64
-rw-r--r--src/util.h4
-rw-r--r--src/utmp-wtmp.c140
-rw-r--r--src/utmp-wtmp.h2
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