diff options
Diffstat (limited to 'src/systemctl.c')
-rw-r--r-- | src/systemctl.c | 160 |
1 files changed, 154 insertions, 6 deletions
diff --git a/src/systemctl.c b/src/systemctl.c index baae019e0f..e517031cea 100644 --- a/src/systemctl.c +++ b/src/systemctl.c @@ -49,6 +49,7 @@ #include "path-lookup.h" #include "conf-parser.h" #include "sd-daemon.h" +#include "shutdownd.h" static const char *arg_type = NULL; static char **arg_property = NULL; @@ -68,6 +69,9 @@ static bool arg_full = false; static bool arg_force = false; static bool arg_defaults = false; static char **arg_wall = NULL; +static usec_t arg_when = 0; +static bool arg_skip_fsck = false; +static bool arg_force_fsck = false; static enum action { ACTION_INVALID, ACTION_SYSTEMCTL, @@ -84,6 +88,7 @@ static enum action { ACTION_RELOAD, ACTION_REEXEC, ACTION_RUNLEVEL, + ACTION_CANCEL_SHUTDOWN, _ACTION_MAX } arg_action = ACTION_SYSTEMCTL; static enum dot { @@ -3832,7 +3837,10 @@ static int shutdown_help(void) { " -r --reboot Reboot the machine\n" " -h Equivalent to --poweroff, overriden by --halt\n" " -k Don't halt/power-off/reboot, just send warnings\n" - " --no-wall Don't send wall message before halt/power-off/reboot\n", + " --no-wall Don't send wall message before halt/power-off/reboot\n" + " -f Skip fsck on reboot\n" + " -F Force fsck on reboot\n" + " -c Cancel a pending shutdown\n", program_invocation_short_name); return 0; @@ -4099,6 +4107,53 @@ static int halt_parse_argv(int argc, char *argv[]) { return 1; } +static int parse_time_spec(const char *t, usec_t *_u) { + assert(t); + assert(_u); + + if (streq(t, "now")) + *_u = 0; + else if (t[0] == '+') { + uint64_t u; + + if (safe_atou64(t + 1, &u) < 0) + return -EINVAL; + + *_u = now(CLOCK_REALTIME) + USEC_PER_MINUTE * u; + } else { + char *e = NULL; + long hour, minute; + struct tm tm; + time_t s; + usec_t n; + + errno = 0; + hour = strtol(t, &e, 10); + if (errno != 0 || *e != ':' || hour < 0 || hour > 23) + return -EINVAL; + + minute = strtol(e+1, &e, 10); + if (errno != 0 || *e != 0 || minute < 0 || minute > 59) + return -EINVAL; + + n = now(CLOCK_REALTIME); + s = (time_t) n / USEC_PER_SEC; + assert_se(localtime_r(&s, &tm)); + + tm.tm_hour = (int) hour; + tm.tm_min = (int) minute; + + assert_se(s = mktime(&tm)); + + *_u = (usec_t) s * USEC_PER_SEC; + + while (*_u <= n) + *_u += USEC_PER_DAY; + } + + return 0; +} + static int shutdown_parse_argv(int argc, char *argv[]) { enum { @@ -4115,12 +4170,12 @@ static int shutdown_parse_argv(int argc, char *argv[]) { { NULL, 0, NULL, 0 } }; - int c; + int c, r; assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "HPrhkt:a", options, NULL)) >= 0) { + while ((c = getopt_long(argc, argv, "HPrhkt:afFc", options, NULL)) >= 0) { switch (c) { case ARG_HELP: @@ -4157,6 +4212,18 @@ static int shutdown_parse_argv(int argc, char *argv[]) { /* Compatibility nops */ break; + case 'f': + arg_skip_fsck = true; + break; + + case 'F': + arg_force_fsck = true; + break; + + case 'c': + arg_action = ACTION_CANCEL_SHUTDOWN; + break; + case '?': return -EINVAL; @@ -4166,10 +4233,13 @@ static int shutdown_parse_argv(int argc, char *argv[]) { } } - if (argc > optind && !streq(argv[optind], "now")) - log_warning("First argument '%s' isn't 'now'. Ignoring.", argv[optind]); + if (argc > optind) + if ((r = parse_time_spec(argv[optind], &arg_when)) < 0) { + log_error("Failed to parse time specification: %s", argv[optind]); + return r; + } - /* We ignore the time argument */ + /* We skip the time argument */ if (argc > optind + 1) arg_wall = argv + optind + 1; @@ -4624,6 +4694,62 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError return verbs[i].dispatch(bus, argv + optind, left); } +static int send_shutdownd(usec_t t, char mode) { + int fd = -1; + struct msghdr msghdr; + struct iovec iovec; + union sockaddr_union sockaddr; + struct ucred *ucred; + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; + } control; + struct shutdownd_command c; + + zero(c); + c.elapse = t; + c.mode = mode; + + if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) + return -errno; + + zero(sockaddr); + sockaddr.sa.sa_family = AF_UNIX; + sockaddr.un.sun_path[0] = 0; + strncpy(sockaddr.un.sun_path+1, "/org/freedesktop/systemd1/shutdownd", sizeof(sockaddr.un.sun_path)-1); + + zero(iovec); + iovec.iov_base = (char*) &c; + iovec.iov_len = sizeof(c); + + zero(control); + control.cmsghdr.cmsg_level = SOL_SOCKET; + control.cmsghdr.cmsg_type = SCM_CREDENTIALS; + control.cmsghdr.cmsg_len = CMSG_LEN(sizeof(struct ucred)); + + ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr); + ucred->pid = getpid(); + ucred->uid = getuid(); + ucred->gid = getgid(); + + zero(msghdr); + msghdr.msg_name = &sockaddr; + msghdr.msg_namelen = sizeof(sa_family_t) + 1 + sizeof("/org/freedesktop/systemd1/shutdownd") - 1; + + msghdr.msg_iov = &iovec; + msghdr.msg_iovlen = 1; + msghdr.msg_control = &control; + msghdr.msg_controllen = control.cmsghdr.cmsg_len; + + if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) { + close_nointr_nofail(fd); + return -errno; + } + + close_nointr_nofail(fd); + return 0; +} + static int reload_with_fallback(DBusConnection *bus) { if (bus) { @@ -4677,6 +4803,24 @@ static int halt_main(DBusConnection *bus) { return -EPERM; } + if (arg_force_fsck) { + if ((r = touch("/forcefsck")) < 0) + log_warning("Failed to create /forcefsck: %s", strerror(-r)); + } else if (arg_skip_fsck) { + if ((r = touch("/fastboot")) < 0) + log_warning("Failed to create /fastboot: %s", strerror(-r)); + } + + if (arg_when > 0) { + if ((r = send_shutdownd(arg_when, + arg_action == ACTION_HALT ? 'H' : + arg_action == ACTION_POWEROFF ? 'P' : + 'r')) < 0) + log_warning("Failed to talk to shutdownd, proceeding with immediate shutdown: %s", strerror(-r)); + else + return 0; + } + if (!arg_dry && !arg_immediate) return start_with_fallback(bus); @@ -4790,6 +4934,10 @@ int main(int argc, char*argv[]) { retval = reload_with_fallback(bus) < 0; break; + case ACTION_CANCEL_SHUTDOWN: + retval = send_shutdownd(0, 0) < 0; + break; + case ACTION_INVALID: case ACTION_RUNLEVEL: default: |