chiark / gitweb /
sleep: implement suspend/hibernate as first class targets
[elogind.git] / src / systemctl / systemctl.c
index e8b8b51d7634a40ce9c1723a218bfdbb5b521665..762b5be3463288b92cb0218535868f9887747c1d 100644 (file)
@@ -45,6 +45,7 @@
 #include "utmp-wtmp.h"
 #include "special.h"
 #include "initreq.h"
+#include "path-util.h"
 #include "strv.h"
 #include "dbus-common.h"
 #include "cgroup-show.h"
 #include "spawn-polkit-agent.h"
 #include "install.h"
 #include "logs-show.h"
+#include "path-util.h"
 
 static const char *arg_type = NULL;
 static char **arg_property = NULL;
 static bool arg_all = false;
 static const char *arg_job_mode = "replace";
 static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
-static bool arg_immediate = false;
 static bool arg_no_block = false;
 static bool arg_no_legend = false;
 static bool arg_no_pager = false;
@@ -96,6 +97,8 @@ static enum action {
         ACTION_REBOOT,
         ACTION_KEXEC,
         ACTION_EXIT,
+        ACTION_SUSPEND,
+        ACTION_HIBERNATE,
         ACTION_RUNLEVEL2,
         ACTION_RUNLEVEL3,
         ACTION_RUNLEVEL4,
@@ -561,7 +564,7 @@ static int compare_unit_file_list(const void *a, const void *b) {
                         return r;
         }
 
-        return strcasecmp(file_name_from_path(u->path), file_name_from_path(v->path));
+        return strcasecmp(path_get_file_name(u->path), path_get_file_name(v->path));
 }
 
 static bool output_show_unit_file(const UnitFileList *u) {
@@ -580,7 +583,7 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) {
                 if (!output_show_unit_file(u))
                         continue;
 
-                max_id_len = MAX(max_id_len, strlen(file_name_from_path(u->path)));
+                max_id_len = MAX(max_id_len, strlen(path_get_file_name(u->path)));
                 state_cols = MAX(state_cols, strlen(unit_file_state_to_string(u->state)));
         }
 
@@ -617,7 +620,7 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) {
                 } else
                         on = off = "";
 
-                id = file_name_from_path(u->path);
+                id = path_get_file_name(u->path);
 
                 e = arg_full ? NULL : ellipsize(id, id_cols, 33);
 
@@ -1350,18 +1353,19 @@ static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *me
 
         } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
                 uint32_t id;
-                const char *path, *result;
+                const char *path, *result, *unit;
                 dbus_bool_t success = true;
 
                 if (dbus_message_get_args(message, &error,
                                           DBUS_TYPE_UINT32, &id,
                                           DBUS_TYPE_OBJECT_PATH, &path,
+                                          DBUS_TYPE_STRING, &unit,
                                           DBUS_TYPE_STRING, &result,
                                           DBUS_TYPE_INVALID)) {
                         char *p;
 
-                        if ((p = set_remove(d->set, (char*) path)))
-                                free(p);
+                        p = set_remove(d->set, (char*) path);
+                        free(p);
 
                         if (*result)
                                 d->result = strdup(result);
@@ -1370,7 +1374,26 @@ static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *me
                 }
 #ifndef LEGACY
                 dbus_error_free(&error);
+                if (dbus_message_get_args(message, &error,
+                                          DBUS_TYPE_UINT32, &id,
+                                          DBUS_TYPE_OBJECT_PATH, &path,
+                                          DBUS_TYPE_STRING, &result,
+                                          DBUS_TYPE_INVALID)) {
+                        char *p;
+
+                        /* Compatibility with older systemd versions <
+                         * 183 during upgrades. This should be dropped
+                         * one day. */
+                        p = set_remove(d->set, (char*) path);
+                        free(p);
+
+                        if (*result)
+                                d->result = strdup(result);
 
+                        goto finish;
+                }
+
+                dbus_error_free(&error);
                 if (dbus_message_get_args(message, &error,
                                           DBUS_TYPE_UINT32, &id,
                                           DBUS_TYPE_OBJECT_PATH, &path,
@@ -1382,8 +1405,8 @@ static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *me
                          * 19 during upgrades. This should be dropped
                          * one day */
 
-                        if ((p = set_remove(d->set, (char*) path)))
-                                free(p);
+                        p = set_remove(d->set, (char*) path);
+                        free(p);
 
                         if (!success)
                                 d->result = strdup("failed");
@@ -1584,6 +1607,10 @@ static enum action verb_to_action(const char *verb) {
                 return ACTION_DEFAULT;
         else if (streq(verb, "exit"))
                 return ACTION_EXIT;
+        else if (streq(verb, "suspend"))
+                return ACTION_SUSPEND;
+        else if (streq(verb, "hibernate"))
+                return ACTION_HIBERNATE;
         else
                 return ACTION_INVALID;
 }
@@ -1602,7 +1629,9 @@ static int start_unit(DBusConnection *bus, char **args) {
                 [ACTION_RESCUE] = SPECIAL_RESCUE_TARGET,
                 [ACTION_EMERGENCY] = SPECIAL_EMERGENCY_TARGET,
                 [ACTION_DEFAULT] = SPECIAL_DEFAULT_TARGET,
-                [ACTION_EXIT] = SPECIAL_EXIT_TARGET
+                [ACTION_EXIT] = SPECIAL_EXIT_TARGET,
+                [ACTION_SUSPEND] = SPECIAL_SUSPEND_TARGET,
+                [ACTION_HIBERNATE] = SPECIAL_HIBERNATE_TARGET
         };
 
         int r, ret = 0;
@@ -1784,11 +1813,15 @@ static int start_special(DBusConnection *bus, char **args) {
         enum action a;
         int r;
 
-        assert(bus);
         assert(args);
 
         a = verb_to_action(args[0]);
 
+        if (arg_force >= 2 && geteuid() != 0) {
+                log_error("Must be root.");
+                return -EPERM;
+        }
+
         if (arg_force >= 2 &&
             (a == ACTION_HALT ||
              a == ACTION_POWEROFF ||
@@ -2378,12 +2411,22 @@ static void print_status_info(UnitStatusInfo *i) {
                 printf("\t  CGroup: %s\n", i->default_control_group);
 
                 if (arg_transport != TRANSPORT_SSH) {
-                        if ((c = columns()) > 18)
+                        unsigned k = 0;
+                        pid_t extra[2];
+
+                        c = columns();
+                        if (c > 18)
                                 c -= 18;
                         else
                                 c = 0;
 
-                        show_cgroup_by_path(i->default_control_group, "\t\t  ", c, false, arg_all);
+                        if (i->main_pid > 0)
+                                extra[k++] = i->main_pid;
+
+                        if (i->control_pid > 0)
+                                extra[k++] = i->control_pid;
+
+                        show_cgroup_and_extra_by_spec(i->default_control_group, "\t\t  ", c, false, arg_all, extra, k);
                 }
         }
 
@@ -3688,7 +3731,7 @@ static int enable_sysv_units(char **args) {
                 if (!isempty(arg_root))
                         argv[c++] = q = strappend("--root=", arg_root);
 
-                argv[c++] = file_name_from_path(p);
+                argv[c++] = path_get_file_name(p);
                 argv[c++] =
                         streq(verb, "enable") ? "on" :
                         streq(verb, "disable") ? "off" : "--level=5";
@@ -4166,7 +4209,9 @@ static int systemctl_help(void) {
                "  poweroff                        Shut down and power-off the system\n"
                "  reboot                          Shut down and reboot the system\n"
                "  kexec                           Shut down and reboot the system with kexec\n"
-               "  exit                            Ask for user instance termination\n",
+               "  exit                            Request user instance exit\n"
+               "  suspend                         Suspend the system\n"
+               "  hibernate                       Hibernate the system\n",
                program_invocation_short_name);
 
         return 0;
@@ -4515,7 +4560,7 @@ static int halt_parse_argv(int argc, char *argv[]) {
 
         if (utmp_get_runlevel(&runlevel, NULL) >= 0)
                 if (runlevel == '0' || runlevel == '6')
-                        arg_immediate = true;
+                        arg_force = 2;
 
         while ((c = getopt_long(argc, argv, "pfwdnih", options, NULL)) >= 0) {
                 switch (c) {
@@ -4538,7 +4583,7 @@ static int halt_parse_argv(int argc, char *argv[]) {
                         break;
 
                 case 'f':
-                        arg_immediate = true;
+                        arg_force = 2;
                         break;
 
                 case 'w':
@@ -5100,6 +5145,8 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
                 { "poweroff",              EQUAL, 1, start_special     },
                 { "reboot",                EQUAL, 1, start_special     },
                 { "kexec",                 EQUAL, 1, start_special     },
+                { "suspend",               EQUAL, 1, start_special     },
+                { "hibernate",             EQUAL, 1, start_special     },
                 { "default",               EQUAL, 1, start_special     },
                 { "rescue",                EQUAL, 1, start_special     },
                 { "emergency",             EQUAL, 1, start_special     },
@@ -5190,7 +5237,9 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
                         return 0;
                 }
 
-                if (!bus) {
+                if (((!streq(verbs[i].verb, "reboot") &&
+                      !streq(verbs[i].verb, "halt") &&
+                      !streq(verbs[i].verb, "poweroff")) || arg_force <= 0) && !bus) {
                         log_error("Failed to get D-Bus connection: %s",
                                   dbus_error_is_set(error) ? error->message : "No connection to service manager.");
                         return -EIO;
@@ -5340,7 +5389,7 @@ static int halt_main(DBusConnection *bus) {
 
                 if (arg_when <= 0 &&
                     !arg_dry &&
-                    !arg_immediate &&
+                    !arg_force &&
                     (arg_action == ACTION_POWEROFF ||
                      arg_action == ACTION_REBOOT)) {
                         r = reboot_with_logind(bus, arg_action);
@@ -5377,7 +5426,7 @@ static int halt_main(DBusConnection *bus) {
                 }
         }
 
-        if (!arg_dry && !arg_immediate)
+        if (!arg_dry && !arg_force)
                 return start_with_fallback(bus);
 
         if (!arg_no_wtmp) {