chiark / gitweb /
dbus: make more cgroup attributes runtime settable
[elogind.git] / src / systemctl / systemctl.c
index 58a6fd4044ef96c458ac2f68e5693364221e1810..5048b529e14b6dcae8ae91cf73b0f07a5da157df 100644 (file)
@@ -129,7 +129,8 @@ static enum transport {
         TRANSPORT_SSH,
         TRANSPORT_POLKIT
 } arg_transport = TRANSPORT_NORMAL;
-static const char *arg_host = NULL;
+static char *arg_host = NULL;
+static char *arg_user = NULL;
 static unsigned arg_lines = 10;
 static OutputMode arg_output = OUTPUT_SHORT;
 static bool arg_plain = false;
@@ -2377,150 +2378,6 @@ static int kill_unit(DBusConnection *bus, char **args) {
         return 0;
 }
 
-static int set_cgroup(DBusConnection *bus, char **args) {
-        _cleanup_free_ char *n = NULL;
-        const char *method, *runtime;
-        char **argument;
-        int r;
-
-        assert(bus);
-        assert(args);
-
-        method =
-                streq(args[0], "set-cgroup")   ? "SetUnitControlGroup" :
-                streq(args[0], "unset-cgroup") ? "UnsetUnitControlGroup"
-                                               : "UnsetUnitControlGroupAttribute";
-
-        runtime = arg_runtime ? "runtime" : "persistent";
-
-        n = unit_name_mangle(args[1]);
-        if (!n)
-                return log_oom();
-
-        STRV_FOREACH(argument, args + 2) {
-
-                r = bus_method_call_with_reply(
-                                bus,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
-                                method,
-                                NULL,
-                                NULL,
-                                DBUS_TYPE_STRING, &n,
-                                DBUS_TYPE_STRING, argument,
-                                DBUS_TYPE_STRING, &runtime,
-                                DBUS_TYPE_INVALID);
-                if (r < 0)
-                        return r;
-        }
-
-        return 0;
-}
-
-static int set_cgroup_attr(DBusConnection *bus, char **args) {
-        _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
-        DBusError error;
-        DBusMessageIter iter;
-        _cleanup_free_ char *n = NULL;
-        const char *runtime;
-        int r;
-
-        assert(bus);
-        assert(args);
-
-        dbus_error_init(&error);
-
-        runtime = arg_runtime ? "runtime" : "persistent";
-
-        n = unit_name_mangle(args[1]);
-        if (!n)
-                return log_oom();
-
-        m = dbus_message_new_method_call(
-                        "org.freedesktop.systemd1",
-                        "/org/freedesktop/systemd1",
-                        "org.freedesktop.systemd1.Manager",
-                        "SetUnitControlGroupAttribute");
-        if (!m)
-                return log_oom();
-
-        dbus_message_iter_init_append(m, &iter);
-        if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &n) ||
-            !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &args[2]))
-                return log_oom();
-
-        r = bus_append_strv_iter(&iter, args + 3);
-        if (r < 0)
-                return log_oom();
-
-        if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &runtime))
-                return log_oom();
-
-        reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
-        if (!reply) {
-                log_error("Failed to issue method call: %s", bus_error_message(&error));
-                dbus_error_free(&error);
-                return -EIO;
-        }
-
-        return 0;
-}
-
-static int get_cgroup_attr(DBusConnection *bus, char **args) {
-        _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
-        _cleanup_free_ char *n = NULL;
-        char **argument;
-        int r;
-
-        assert(bus);
-        assert(args);
-
-        n = unit_name_mangle(args[1]);
-        if (!n)
-                return log_oom();
-
-        STRV_FOREACH(argument, args + 2) {
-                _cleanup_strv_free_ char **list = NULL;
-                DBusMessageIter iter;
-                char **a;
-
-                r = bus_method_call_with_reply(
-                                bus,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
-                                "GetUnitControlGroupAttribute",
-                                &reply,
-                                NULL,
-                                DBUS_TYPE_STRING, &n,
-                                DBUS_TYPE_STRING, argument,
-                                DBUS_TYPE_INVALID);
-                if (r < 0)
-                        return r;
-
-                if (!dbus_message_iter_init(reply, &iter)) {
-                        log_error("Failed to initialize iterator.");
-                        return -EIO;
-                }
-
-                r = bus_parse_strv_iter(&iter, &list);
-                if (r < 0) {
-                        log_error("Failed to parse value list.");
-                        return r;
-                }
-
-                STRV_FOREACH(a, list) {
-                        if (endswith(*a, "\n"))
-                                fputs(*a, stdout);
-                        else
-                                puts(*a);
-                }
-        }
-
-        return 0;
-}
-
 typedef struct ExecStatusInfo {
         char *name;
 
@@ -2638,7 +2495,7 @@ typedef struct UnitStatusInfo {
 
         const char *fragment_path;
         const char *source_path;
-        const char *default_control_group;
+        const char *control_group;
 
         char **dropin_paths;
 
@@ -2657,6 +2514,7 @@ typedef struct UnitStatusInfo {
         pid_t main_pid;
         pid_t control_pid;
         const char *status_text;
+        const char *pid_file;
         bool running:1;
 
         usec_t start_timestamp;
@@ -2700,20 +2558,10 @@ static void print_status_info(UnitStatusInfo *i) {
                 on_tty() * OUTPUT_COLOR |
                 !arg_quiet * OUTPUT_WARN_CUTOFF |
                 arg_full * OUTPUT_FULL_WIDTH;
-        int maxlen = 8; /* a value that'll suffice most of the time */
         char **t, **t2;
 
         assert(i);
 
-        STRV_FOREACH_PAIR(t, t2, i->listen)
-                maxlen = MAX(maxlen, (int)(sizeof("Listen") - 1 + strlen(*t)));
-        if (i->accept)
-                maxlen = MAX(maxlen, (int)sizeof("Accept") - 1);
-        if (i->main_pid > 0)
-                maxlen = MAX(maxlen, (int)sizeof("Main PID") - 1);
-        else if (i->control_pid > 0)
-                maxlen = MAX(maxlen, (int)sizeof("Control") - 1);
-
         /* This shows pretty information about a unit. See
          * print_property() for a low-level property printer */
 
@@ -2725,7 +2573,7 @@ static void print_status_info(UnitStatusInfo *i) {
         printf("\n");
 
         if (i->following)
-                printf(" %*s: unit currently follows state of %s\n", maxlen, "Follow", i->following);
+                printf("   Follow: unit currently follows state of %s\n", i->following);
 
         if (streq_ptr(i->load_state, "error")) {
                 on = ansi_highlight_red(true);
@@ -2736,17 +2584,17 @@ static void print_status_info(UnitStatusInfo *i) {
         path = i->source_path ? i->source_path : i->fragment_path;
 
         if (i->load_error)
-                printf(" %*s: %s%s%s (Reason: %s)\n",
-                       maxlen, "Loaded", on, strna(i->load_state), off, i->load_error);
+                printf("   Loaded: %s%s%s (Reason: %s)\n",
+                       on, strna(i->load_state), off, i->load_error);
         else if (path && i->unit_file_state)
-                printf(" %*s: %s%s%s (%s; %s)\n",
-                       maxlen, "Loaded", on, strna(i->load_state), off, path, i->unit_file_state);
+                printf("   Loaded: %s%s%s (%s; %s)\n",
+                       on, strna(i->load_state), off, path, i->unit_file_state);
         else if (path)
-                printf(" %*s: %s%s%s (%s)\n",
-                       maxlen, "Loaded", on, strna(i->load_state), off, path);
+                printf("   Loaded: %s%s%s (%s)\n",
+                       on, strna(i->load_state), off, path);
         else
-                printf(" %*s: %s%s%s\n",
-                       maxlen, "Loaded", on, strna(i->load_state), off);
+                printf("   Loaded: %s%s%s\n",
+                       on, strna(i->load_state), off);
 
         if (!strv_isempty(i->dropin_paths)) {
                 char ** dropin;
@@ -2755,7 +2603,7 @@ static void print_status_info(UnitStatusInfo *i) {
 
                 STRV_FOREACH(dropin, i->dropin_paths) {
                         if (! dir || last) {
-                                printf("  %*s ", maxlen, dir ? "" : "Drop-In:");
+                                printf(dir ? "        " : "  Drop-In: ");
 
                                 free(dir);
 
@@ -2764,7 +2612,7 @@ static void print_status_info(UnitStatusInfo *i) {
                                         return;
                                 }
 
-                                printf("%s\n %*s  %s", dir, maxlen, "",
+                                printf("%s\n           %s", dir,
                                        draw_special_char(DRAW_TREE_RIGHT));
                         }
 
@@ -2788,11 +2636,11 @@ static void print_status_info(UnitStatusInfo *i) {
                 on = off = "";
 
         if (ss)
-                printf(" %*s: %s%s (%s)%s",
-                       maxlen, "Active",  on, strna(i->active_state), ss, off);
+                printf("   Active: %s%s (%s)%s",
+                       on, strna(i->active_state), ss, off);
         else
-                printf(" %*s: %s%s%s",
-                       maxlen, "Active", on, strna(i->active_state), off);
+                printf("   Active: %s%s%s",
+                       on, strna(i->active_state), off);
 
         if (!isempty(i->result) && !streq(i->result, "success"))
                 printf(" (Result: %s)", i->result);
@@ -2819,26 +2667,26 @@ static void print_status_info(UnitStatusInfo *i) {
                 s2 = format_timestamp(since2, sizeof(since2), i->condition_timestamp);
 
                 if (s1)
-                        printf(" %*s start condition failed at %s; %s\n", maxlen, "", s2, s1);
+                        printf("          start condition failed at %s; %s\n", s2, s1);
                 else if (s2)
-                        printf(" %*s start condition failed at %s\n", maxlen, "", s2);
+                        printf("          start condition failed at %s\n", s2);
         }
 
         if (i->sysfs_path)
-                printf(" %*s: %s\n", maxlen, "Device", i->sysfs_path);
+                printf("   Device: %s\n", i->sysfs_path);
         if (i->where)
-                printf(" %*s: %s\n", maxlen, "Where", i->where);
+                printf("    Where: %s\n", i->where);
         if (i->what)
-                printf(" %*s: %s\n", maxlen, "What", i->what);
+                printf("     What: %s\n", i->what);
 
         STRV_FOREACH(t, i->documentation)
-                printf(" %*s %s\n", maxlen+1, t == i->documentation ? "Docs:" : "", *t);
+                printf(" %*s %s\n", 9, t == i->documentation ? "Docs:" : "", *t);
 
         STRV_FOREACH_PAIR(t, t2, i->listen)
-                printf(" %*s %s (%s)\n", maxlen+1, t == i->listen ? "Listen:" : "", *t2, *t);
+                printf(" %*s %s (%s)\n", 9, t == i->listen ? "Listen:" : "", *t2, *t);
 
         if (i->accept)
-                printf(" %*s: %u; Connected: %u\n", maxlen, "Accepted", i->n_accepted, i->n_connections);
+                printf(" Accepted: %u; Connected: %u\n", i->n_accepted, i->n_connections);
 
         LIST_FOREACH(exec, p, i->exec) {
                 _cleanup_free_ char *argv = NULL;
@@ -2849,7 +2697,7 @@ static void print_status_info(UnitStatusInfo *i) {
                         continue;
 
                 argv = strv_join(p->argv, " ");
-                printf(" %*s: %u %s=%s ", maxlen, "Process", p->pid, p->name, strna(argv));
+                printf("  Process: %u %s=%s ", p->pid, p->name, strna(argv));
 
                 good = is_clean_exit_lsb(p->code, p->status, NULL);
                 if (!good) {
@@ -2886,7 +2734,7 @@ static void print_status_info(UnitStatusInfo *i) {
 
         if (i->main_pid > 0 || i->control_pid > 0) {
                 if (i->main_pid > 0) {
-                        printf(" %*s: %u", maxlen, "Main PID", (unsigned) i->main_pid);
+                        printf(" Main PID: %u", (unsigned) i->main_pid);
 
                         if (i->running) {
                                 _cleanup_free_ char *comm = NULL;
@@ -2917,7 +2765,7 @@ static void print_status_info(UnitStatusInfo *i) {
                 if (i->control_pid > 0) {
                         _cleanup_free_ char *c = NULL;
 
-                        printf(" %*s: %u", i->main_pid ? 0 : maxlen, "Control", (unsigned) i->control_pid);
+                        printf(" %8s: %u", i->main_pid ? "" : " Control", (unsigned) i->control_pid);
 
                         get_process_comm(i->control_pid, &c);
                         if (c)
@@ -2928,20 +2776,18 @@ static void print_status_info(UnitStatusInfo *i) {
         }
 
         if (i->status_text)
-                printf(" %*s: \"%s\"\n", maxlen, "Status", i->status_text);
+                printf("   Status: \"%s\"\n", i->status_text);
 
-        if (i->default_control_group &&
-            (i->main_pid > 0 || i->control_pid > 0 || cg_is_empty_by_spec(i->default_control_group, false) == 0)) {
+        if (i->control_group &&
+            (i->main_pid > 0 || i->control_pid > 0 || cg_is_empty_by_spec(i->control_group, false) == 0)) {
                 unsigned c;
 
-                printf(" %*s: %s\n", maxlen, "CGroup", i->default_control_group);
+                printf("   CGroup: %s\n", i->control_group);
 
                 if (arg_transport != TRANSPORT_SSH) {
                         unsigned k = 0;
                         pid_t extra[2];
-                        char prefix[maxlen + 4];
-                        memset(prefix, ' ', sizeof(prefix) - 1);
-                        prefix[sizeof(prefix) - 1] = '\0';
+                        char prefix[] = "           ";
 
                         c = columns();
                         if (c > sizeof(prefix) - 1)
@@ -2955,7 +2801,7 @@ static void print_status_info(UnitStatusInfo *i) {
                         if (i->control_pid > 0)
                                 extra[k++] = i->control_pid;
 
-                        show_cgroup_and_extra_by_spec(i->default_control_group, prefix,
+                        show_cgroup_and_extra_by_spec(i->control_group, prefix,
                                                       c, false, extra, k, flags);
                 }
         }
@@ -3064,10 +2910,16 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn
                                 i->fragment_path = s;
                         else if (streq(name, "SourcePath"))
                                 i->source_path = s;
+#ifndef LEGACY
                         else if (streq(name, "DefaultControlGroup"))
-                                i->default_control_group = s;
+                                i->control_group = s;
+#endif
+                        else if (streq(name, "ControlGroup"))
+                                i->control_group = s;
                         else if (streq(name, "StatusText"))
                                 i->status_text = s;
+                        else if (streq(name, "PIDFile"))
+                                i->pid_file = s;
                         else if (streq(name, "SysFSPath"))
                                 i->sysfs_path = s;
                         else if (streq(name, "Where"))
@@ -3465,8 +3317,44 @@ static int print_property(const char *name, DBusMessageIter *iter) {
                         }
 
                         return 0;
+
+                } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "DeviceAllow")) {
+                        DBusMessageIter sub, sub2;
+
+                        dbus_message_iter_recurse(iter, &sub);
+                        while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
+                                const char *path, *rwm;
+
+                                dbus_message_iter_recurse(&sub, &sub2);
+
+                                if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) >= 0 &&
+                                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &rwm, false) >= 0)
+                                        printf("%s=%s %s\n", name, strna(path), strna(rwm));
+
+                                dbus_message_iter_next(&sub);
+                        }
+                        return 0;
+
+                } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && (streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth"))) {
+                        DBusMessageIter sub, sub2;
+
+                        dbus_message_iter_recurse(iter, &sub);
+                        while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
+                                const char *path;
+                                uint64_t bandwidth;
+
+                                dbus_message_iter_recurse(&sub, &sub2);
+
+                                if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) >= 0 &&
+                                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &bandwidth, false) >= 0)
+                                        printf("%s=%s %" PRIu64 "\n", name, strna(path), bandwidth);
+
+                                dbus_message_iter_next(&sub);
+                        }
+                        return 0;
                 }
 
+
                 break;
         }
 
@@ -3558,9 +3446,19 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo
 
         if (!streq_ptr(info.active_state, "active") &&
             !streq_ptr(info.active_state, "reloading") &&
-            streq(verb, "status"))
+            streq(verb, "status")) {
                 /* According to LSB: "program not running" */
-                r = 3;
+                /* 0: program is running or service is OK
+                 * 1: program is dead and /var/run pid file exists
+                 * 2: program is dead and /var/lock lock file exists
+                 * 3: program is not running
+                 * 4: program or service status is unknown
+                 */
+                if (info.pid_file && access(info.pid_file, F_OK) == 0)
+                        r = 1;
+                else
+                        r = 3;
+       }
 
         while ((p = info.exec)) {
                 LIST_REMOVE(ExecStatusInfo, exec, info.exec, p);
@@ -3698,6 +3596,138 @@ static int show(DBusConnection *bus, char **args) {
         return ret;
 }
 
+static int append_assignment(DBusMessageIter *iter, const char *assignment) {
+        const char *eq;
+        char *field;
+        DBusMessageIter sub;
+        int r;
+
+        assert(iter);
+        assert(assignment);
+
+        eq = strchr(assignment, '=');
+        if (!eq) {
+                log_error("Not an assignment: %s", assignment);
+                return -EINVAL;
+        }
+
+        field = strndupa(assignment, eq - assignment);
+        eq ++;
+
+        if (!dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &field))
+                return log_oom();
+
+        if (streq(field, "CPUAccounting") ||
+            streq(field, "MemoryAccounting") ||
+            streq(field, "BlockIOAccounting")) {
+                dbus_bool_t b;
+
+                r = parse_boolean(eq);
+                if (r < 0) {
+                        log_error("Failed to parse boolean assignment %s.", assignment);
+                        return -EINVAL;
+                }
+
+                b = r;
+                if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "b", &sub) ||
+                    !dbus_message_iter_append_basic(&sub, DBUS_TYPE_BOOLEAN, &b))
+                        return log_oom();
+
+        } else if (streq(field, "MemoryLimit") || streq(field, "MemorySoftLimit")) {
+                off_t bytes;
+                uint64_t u;
+
+                r = parse_bytes(eq, &bytes);
+                if (r < 0) {
+                        log_error("Failed to parse bytes specification %s", assignment);
+                        return -EINVAL;
+                }
+
+                u = bytes;
+                if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "t", &sub) ||
+                    !dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT64, &u))
+                        return log_oom();
+
+        } else if (streq(field, "CPUShares") || streq(field, "BlockIOWeight")) {
+                uint64_t u;
+
+                r = safe_atou64(eq, &u);
+                if (r < 0) {
+                        log_error("Failed to parse %s value %s.", field, eq);
+                        return -EINVAL;
+                }
+
+                if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "t", &sub) ||
+                    !dbus_message_iter_append_basic(&sub, DBUS_TYPE_UINT64, &u))
+                        return log_oom();
+
+        } else {
+                log_error("Unknown assignment %s.", assignment);
+                return -EINVAL;
+        }
+
+        if (!dbus_message_iter_close_container(iter, &sub))
+                return log_oom();
+
+        return 0;
+}
+
+static int set_property(DBusConnection *bus, char **args) {
+
+        _cleanup_free_ DBusMessage *m = NULL, *reply = NULL;
+        DBusMessageIter iter, sub;
+        dbus_bool_t runtime;
+        DBusError error;
+        char **i;
+        int r;
+
+        dbus_error_init(&error);
+
+        m = dbus_message_new_method_call(
+                        "org.freedesktop.systemd1",
+                        "/org/freedesktop/systemd1",
+                        "org.freedesktop.systemd1.Manager",
+                        "SetUnitProperties");
+        if (!m)
+                return log_oom();
+
+        dbus_message_iter_init_append(m, &iter);
+
+        runtime = arg_runtime;
+
+        if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &args[1]) ||
+            !dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &runtime) ||
+            !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sv)", &sub))
+                return log_oom();
+
+        STRV_FOREACH(i, args + 2) {
+                DBusMessageIter sub2;
+
+                if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
+                        return log_oom();
+
+                r = append_assignment(&sub2, *i);
+                if (r < 0)
+                        return r;
+
+                if (!dbus_message_iter_close_container(&sub, &sub2))
+                        return log_oom();
+
+        }
+
+        if (!dbus_message_iter_close_container(&iter, &sub))
+                return log_oom();
+
+        reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
+        if (!reply) {
+                log_error("Failed to issue method call: %s", bus_error_message(&error));
+                dbus_error_free(&error);
+                return -EIO;
+        }
+
+        return 0;
+}
+
 static int dump(DBusConnection *bus, char **args) {
         _cleanup_free_ DBusMessage *reply = NULL;
         DBusError error;
@@ -3747,7 +3777,7 @@ static int snapshot(DBusConnection *bus, char **args) {
         dbus_error_init(&error);
 
         if (strv_length(args) > 1)
-                n = snapshot_name_mangle(args[1]);
+                n = unit_name_mangle_with_suffix(args[1], ".snapshot");
         else
                 n = strdup("");
         if (!n)
@@ -3822,7 +3852,7 @@ static int delete_snapshot(DBusConnection *bus, char **args) {
                 _cleanup_free_ char *n = NULL;
                 int r;
 
-                n = snapshot_name_mangle(*name);
+                n = unit_name_mangle_with_suffix(*name, ".snapshot");
                 if (!n)
                         return log_oom();
 
@@ -4610,7 +4640,7 @@ static int systemctl_help(void) {
                "                      the 'list-unit-files' command instead.\n"
                "     --reverse        Show reverse dependencies with 'list-dependencies'\n"
                "     --failed         Show only failed units\n"
-               "     --full           Don't ellipsize unit names on output\n"
+               "  -l --full           Don't ellipsize unit names on output\n"
                "     --fail           When queueing a new job, fail if conflicting jobs are\n"
                "                      pending\n"
                "     --irreversible   Create jobs which cannot be implicitly cancelled\n"
@@ -4662,17 +4692,11 @@ static int systemctl_help(void) {
                "  status [NAME...|PID...]         Show runtime status of one or more units\n"
                "  show [NAME...|JOB...]           Show properties of one or more\n"
                "                                  units/jobs or the manager\n"
+               "  set-property [NAME] [ASSIGNMENT...]\n"
+               "                                  Sets one or more properties of a unit\n"
                "  help [NAME...|PID...]           Show manual for one or more units\n"
                "  reset-failed [NAME...]          Reset failed state for all, one, or more\n"
                "                                  units\n"
-               "  get-cgroup-attr [NAME] [ATTR] ...\n"
-               "                                  Get control group attrubute\n"
-               "  set-cgroup-attr [NAME] [ATTR] [VALUE] ...\n"
-               "                                  Set control group attribute\n"
-               "  unset-cgroup-attr [NAME] [ATTR...]\n"
-               "                                  Unset control group attribute\n"
-               "  set-cgroup [NAME] [CGROUP...]   Add unit to a control group\n"
-               "  unset-cgroup [NAME] [CGROUP...] Remove unit from a control group\n"
                "  load [NAME...]                  Load one or more units\n"
                "  list-dependencies [NAME]        Recursively show units which are required\n"
                "                                  or wanted by this unit or by which this\n"
@@ -4830,7 +4854,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 ARG_NO_PAGER,
                 ARG_NO_WALL,
                 ARG_ROOT,
-                ARG_FULL,
                 ARG_NO_RELOAD,
                 ARG_KILL_WHO,
                 ARG_NO_ASK_PASSWORD,
@@ -4851,7 +4874,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 { "before",    no_argument,       NULL, ARG_BEFORE    },
                 { "show-types", no_argument,      NULL, ARG_SHOW_TYPES },
                 { "failed",    no_argument,       NULL, ARG_FAILED    },
-                { "full",      no_argument,       NULL, ARG_FULL      },
+                { "full",      no_argument,       NULL, 'l'           },
                 { "fail",      no_argument,       NULL, ARG_FAIL      },
                 { "irreversible", no_argument,    NULL, ARG_IRREVERSIBLE },
                 { "ignore-dependencies", no_argument, NULL, ARG_IGNORE_DEPENDENCIES },
@@ -4884,7 +4907,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "ht:p:aqfs:H:Pn:o:i", options, NULL)) >= 0) {
+        while ((c = getopt_long(argc, argv, "ht:p:alqfs:H:Pn:o:i", options, NULL)) >= 0) {
 
                 switch (c) {
 
@@ -5032,7 +5055,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         arg_root = optarg;
                         break;
 
-                case ARG_FULL:
+                case 'l':
                         arg_full = true;
                         break;
 
@@ -5077,7 +5100,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
 
                 case 'H':
                         arg_transport = TRANSPORT_SSH;
-                        arg_host = optarg;
+                        parse_user_at_host(optarg, &arg_user, &arg_host);
                         break;
 
                 case ARG_RUNTIME:
@@ -5710,11 +5733,6 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
                 { "condreload",            MORE,  2, start_unit        }, /* For compatibility with ALTLinux */
                 { "condrestart",           MORE,  2, start_unit        }, /* For compatibility with RH */
                 { "isolate",               EQUAL, 2, start_unit        },
-                { "set-cgroup",            MORE,  3, set_cgroup        },
-                { "unset-cgroup",          MORE,  3, set_cgroup        },
-                { "get-cgroup-attr",       MORE,  3, get_cgroup_attr   },
-                { "set-cgroup-attr",       MORE,  4, set_cgroup_attr   },
-                { "unset-cgroup-attr",     MORE,  3, set_cgroup        },
                 { "kill",                  MORE,  2, kill_unit         },
                 { "is-active",             MORE,  2, check_unit_active },
                 { "check",                 MORE,  2, check_unit_active },
@@ -5755,6 +5773,7 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
                 { "set-default",           EQUAL, 2, enable_unit       },
                 { "get-default",           LESS,  1, get_default       },
                 { "set-log-level",         EQUAL, 2, set_log_level     },
+                { "set-property",          MORE,  3, set_property      },
         };
 
         int left;
@@ -6098,7 +6117,7 @@ int main(int argc, char*argv[]) {
                         bus_connect_system_polkit(&bus, &error);
                         private_bus = false;
                 } else if (arg_transport == TRANSPORT_SSH) {
-                        bus_connect_system_ssh(NULL, arg_host, &bus, &error);
+                        bus_connect_system_ssh(arg_user, arg_host, &bus, &error);
                         private_bus = false;
                 } else
                         assert_not_reached("Uh, invalid transport...");