chiark / gitweb /
relicense to LGPLv2.1 (with exceptions)
[elogind.git] / src / systemctl.c
index 4b27b69c557f4e1dcacde21d8a1e77d0bafcf867..28bdfa96a4e06d9b9c393e7b3357dd96b28f79e8 100644 (file)
@@ -6,16 +6,16 @@
   Copyright 2010 Lennart Poettering
 
   systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
   (at your option) any later version.
 
   systemd is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  General Public License for more details.
+  Lesser General Public License for more details.
 
-  You should have received a copy of the GNU General Public License
+  You should have received a copy of the GNU Lesser General Public License
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
@@ -36,6 +36,7 @@
 #include <dbus/dbus.h>
 
 #include <systemd/sd-daemon.h>
+#include <systemd/sd-shutdown.h>
 
 #include "log.h"
 #include "util.h"
 #include "list.h"
 #include "path-lookup.h"
 #include "conf-parser.h"
-#include "shutdownd.h"
 #include "exit-status.h"
 #include "bus-errors.h"
 #include "build.h"
 #include "unit-name.h"
 #include "pager.h"
-#include "spawn-agent.h"
+#include "spawn-ask-password-agent.h"
+#include "spawn-polkit-agent.h"
 #include "install.h"
 #include "logs-show.h"
 
@@ -78,7 +79,7 @@ static bool arg_dry = false;
 static bool arg_quiet = false;
 static bool arg_full = false;
 static int arg_force = 0;
-static bool arg_ask_password = false;
+static bool arg_ask_password = true;
 static bool arg_failed = false;
 static bool arg_runtime = false;
 static char **arg_wall = NULL;
@@ -154,7 +155,7 @@ static void pager_open_if_enabled(void) {
         pager_open();
 }
 
-static void agent_open_if_enabled(void) {
+static void ask_password_agent_open_if_enabled(void) {
 
         /* Open the password agent as a child process if necessary */
 
@@ -164,7 +165,20 @@ static void agent_open_if_enabled(void) {
         if (arg_scope != UNIT_FILE_SYSTEM)
                 return;
 
-        agent_open();
+        ask_password_agent_open();
+}
+
+static void polkit_agent_open_if_enabled(void) {
+
+        /* Open the polkit agent as a child process if necessary */
+
+        if (!arg_ask_password)
+                return;
+
+        if (arg_scope != UNIT_FILE_SYSTEM)
+                return;
+
+        polkit_agent_open();
 }
 
 static const char *ansi_highlight_red(bool b) {
@@ -242,7 +256,8 @@ static void warn_wall(enum action a) {
         if (arg_wall) {
                 char *p;
 
-                if (!(p = strv_join(arg_wall, " "))) {
+                p = strv_join(arg_wall, " ");
+                if (!p) {
                         log_error("Failed to join strings.");
                         return;
                 }
@@ -1600,7 +1615,7 @@ static int start_unit(DBusConnection *bus, char **args) {
 
         assert(bus);
 
-        agent_open_if_enabled();
+        ask_password_agent_open_if_enabled();
 
         if (arg_action == ACTION_SYSTEMCTL) {
                 method =
@@ -1682,7 +1697,8 @@ finish:
         return ret;
 }
 
-/* ask systemd-logind, which might grant access to unprivileged users through polkit */
+/* Ask systemd-logind, which might grant access to unprivileged users
+ * through PolicyKit */
 static int reboot_with_logind(DBusConnection *bus, enum action a) {
 #ifdef HAVE_LOGIND
         const char *method;
@@ -1693,6 +1709,8 @@ static int reboot_with_logind(DBusConnection *bus, enum action a) {
 
         dbus_error_init(&error);
 
+        polkit_agent_open_if_enabled();
+
         switch (a) {
 
         case ACTION_REBOOT:
@@ -1771,16 +1789,13 @@ static int start_special(DBusConnection *bus, char **args) {
 
         a = verb_to_action(args[0]);
 
-        if (arg_force >= 2 && a == ACTION_HALT)
-                halt_now(ACTION_HALT);
-
-        if (arg_force >= 2 && a == ACTION_POWEROFF)
-                halt_now(ACTION_POWEROFF);
-
-        if (arg_force >= 2 && a == ACTION_REBOOT)
-                halt_now(ACTION_REBOOT);
+        if (arg_force >= 2 &&
+            (a == ACTION_HALT ||
+             a == ACTION_POWEROFF ||
+             a == ACTION_REBOOT))
+                halt_now(a);
 
-        if (arg_force &&
+        if (arg_force >= 1 &&
             (a == ACTION_HALT ||
              a == ACTION_POWEROFF ||
              a == ACTION_REBOOT ||
@@ -1788,14 +1803,13 @@ static int start_special(DBusConnection *bus, char **args) {
              a == ACTION_EXIT))
                 return daemon_reload(bus, args);
 
-        if (geteuid() != 0) {
-                /* first try logind, to allow authentication with polkit */
-                if (a == ACTION_POWEROFF ||
-                    a == ACTION_REBOOT) {
-                        r = reboot_with_logind(bus, a);
-                        if (r >= 0)
-                                return r;
-                }
+        /* first try logind, to allow authentication with polkit */
+        if (geteuid() != 0 &&
+            (a == ACTION_POWEROFF ||
+             a == ACTION_REBOOT)) {
+                r = reboot_with_logind(bus, a);
+                if (r >= 0)
+                        return r;
         }
 
         r = start_unit(bus, args);
@@ -4292,9 +4306,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        /* Only when running as systemctl we ask for passwords */
-        arg_ask_password = true;
-
         while ((c = getopt_long(argc, argv, "ht:p:aqfs:H:Pn:o:", options, NULL)) >= 0) {
 
                 switch (c) {
@@ -4630,6 +4641,7 @@ static int shutdown_parse_argv(int argc, char *argv[]) {
                 { "halt",      no_argument,       NULL, 'H'         },
                 { "poweroff",  no_argument,       NULL, 'P'         },
                 { "reboot",    no_argument,       NULL, 'r'         },
+                { "kexec",     no_argument,       NULL, 'K'         }, /* not documented extension */
                 { "no-wall",   no_argument,       NULL, ARG_NO_WALL },
                 { NULL,        0,                 NULL, 0           }
         };
@@ -4661,6 +4673,10 @@ static int shutdown_parse_argv(int argc, char *argv[]) {
                                 arg_action = ACTION_REBOOT;
                         break;
 
+                case 'K':
+                        arg_action = ACTION_KEXEC;
+                        break;
+
                 case 'h':
                         if (arg_action != ACTION_HALT)
                                 arg_action = ACTION_POWEROFF;
@@ -4693,7 +4709,8 @@ static int shutdown_parse_argv(int argc, char *argv[]) {
         }
 
         if (argc > optind) {
-                if ((r = parse_time_spec(argv[optind], &arg_when)) < 0) {
+                r = parse_time_spec(argv[optind], &arg_when);
+                if (r < 0) {
                         log_error("Failed to parse time specification: %s", argv[optind]);
                         return r;
                 }
@@ -5192,39 +5209,42 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
 }
 
 static int send_shutdownd(usec_t t, char mode, bool dry_run, bool warn, const char *message) {
-        int fd = -1;
+        int fd;
         struct msghdr msghdr;
-        struct iovec iovec;
+        struct iovec iovec[2];
         union sockaddr_union sockaddr;
-        struct shutdownd_command c;
+        struct sd_shutdown_command c;
+
+        fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
+        if (fd < 0)
+                return -errno;
 
         zero(c);
-        c.elapse = t;
+        c.usec = t;
         c.mode = mode;
         c.dry_run = dry_run;
         c.warn_wall = warn;
 
-        if (message)
-                strncpy(c.wall_message, message, sizeof(c.wall_message));
-
-        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, "/run/systemd/shutdownd", sizeof(sockaddr.un.sun_path));
 
-        zero(iovec);
-        iovec.iov_base = (char*) &c;
-        iovec.iov_len = sizeof(c);
-
         zero(msghdr);
         msghdr.msg_name = &sockaddr;
         msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + sizeof("/run/systemd/shutdownd") - 1;
 
-        msghdr.msg_iov = &iovec;
-        msghdr.msg_iovlen = 1;
+        zero(iovec);
+        iovec[0].iov_base = (char*) &c;
+        iovec[0].iov_len = offsetof(struct sd_shutdown_command, wall_message);
+
+        if (isempty(message))
+                msghdr.msg_iovlen = 1;
+        else {
+                iovec[1].iov_base = (char*) message;
+                iovec[1].iov_len = strlen(message);
+                msghdr.msg_iovlen = 2;
+        }
+        msghdr.msg_iov = iovec;
 
         if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
                 close_nointr_nofail(fd);
@@ -5314,8 +5334,15 @@ static int halt_main(DBusConnection *bus) {
         int r;
 
         if (geteuid() != 0) {
-                if (arg_action == ACTION_POWEROFF ||
-                    arg_action == ACTION_REBOOT) {
+                /* Try logind if we are a normal user and no special
+                 * mode applies. Maybe PolicyKit allows us to shutdown
+                 * the machine. */
+
+                if (arg_when <= 0 &&
+                    !arg_dry &&
+                    !arg_immediate &&
+                    (arg_action == ACTION_POWEROFF ||
+                     arg_action == ACTION_REBOOT)) {
                         r = reboot_with_logind(bus, arg_action);
                         if (r >= 0)
                                 return r;
@@ -5327,12 +5354,12 @@ static int halt_main(DBusConnection *bus) {
 
         if (arg_when > 0) {
                 char *m;
-                char date[FORMAT_TIMESTAMP_MAX];
 
                 m = strv_join(arg_wall, " ");
                 r = send_shutdownd(arg_when,
                                    arg_action == ACTION_HALT     ? 'H' :
                                    arg_action == ACTION_POWEROFF ? 'P' :
+                                   arg_action == ACTION_KEXEC    ? 'K' :
                                                                    'r',
                                    arg_dry,
                                    !arg_no_wall,
@@ -5342,6 +5369,8 @@ static int halt_main(DBusConnection *bus) {
                 if (r < 0)
                         log_warning("Failed to talk to shutdownd, proceeding with immediate shutdown: %s", strerror(-r));
                 else {
+                        char date[FORMAT_TIMESTAMP_MAX];
+
                         log_info("Shutdown scheduled for %s, use 'shutdown -c' to cancel.",
                                  format_timestamp(date, sizeof(date), arg_when));
                         return 0;
@@ -5354,8 +5383,11 @@ static int halt_main(DBusConnection *bus) {
         if (!arg_no_wtmp) {
                 if (sd_booted() > 0)
                         log_debug("Not writing utmp record, assuming that systemd-update-utmp is used.");
-                else if ((r = utmp_put_shutdown()) < 0)
-                        log_warning("Failed to write utmp record: %s", strerror(-r));
+                else {
+                        r = utmp_put_shutdown();
+                        if (r < 0)
+                                log_warning("Failed to write utmp record: %s", strerror(-r));
+                }
         }
 
         if (!arg_no_sync)
@@ -5395,7 +5427,8 @@ int main(int argc, char*argv[]) {
         log_parse_environment();
         log_open();
 
-        if ((r = parse_argv(argc, argv)) < 0)
+        r = parse_argv(argc, argv);
+        if (r < 0)
                 goto finish;
         else if (r == 0) {
                 retval = EXIT_SUCCESS;
@@ -5483,7 +5516,8 @@ finish:
         strv_free(arg_property);
 
         pager_close();
-        agent_close();
+        ask_password_agent_close();
+        polkit_agent_close();
 
         return retval;
 }