systemctl: add new "clean" operation to systemctl
authorLennart Poettering <lennart@poettering.net>
Mon, 1 Apr 2019 16:57:46 +0000 (18:57 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 11 Jul 2019 10:18:51 +0000 (12:18 +0200)
This exposes the CleanUnit() bus calls in systemctl, i.e. makes all the
infrastructure we previously added useful to people.

src/systemctl/systemctl.c

index b7de010..616ac0f 100644 (file)
@@ -28,6 +28,7 @@
 #include "bus-unit-util.h"
 #include "bus-util.h"
 #include "bus-wait-for-jobs.h"
+#include "bus-wait-for-units.h"
 #include "cgroup-show.h"
 #include "cgroup-util.h"
 #include "copy.h"
@@ -162,12 +163,14 @@ static const char *arg_boot_loader_entry = NULL;
 static bool arg_now = false;
 static bool arg_jobs_before = false;
 static bool arg_jobs_after = false;
+static char **arg_clean_what = NULL;
 
 STATIC_DESTRUCTOR_REGISTER(arg_wall, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_types, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_states, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_properties, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_clean_what, strv_freep);
 
 static int daemon_reload(int argc, char *argv[], void* userdata);
 static int trivial_method(int argc, char *argv[], void *userdata);
@@ -3951,6 +3954,101 @@ static int kill_unit(int argc, char *argv[], void *userdata) {
         return r;
 }
 
+static int clean_unit(int argc, char *argv[], void *userdata) {
+        _cleanup_(bus_wait_for_units_freep) BusWaitForUnits *w = NULL;
+        _cleanup_strv_free_ char **names = NULL;
+        int r, ret = EXIT_SUCCESS;
+        char **name;
+        sd_bus *bus;
+
+        r = acquire_bus(BUS_FULL, &bus);
+        if (r < 0)
+                return r;
+
+        polkit_agent_open_maybe();
+
+        if (!arg_clean_what) {
+                arg_clean_what = strv_new("cache", "runtime");
+                if (!arg_clean_what)
+                        return log_oom();
+        }
+
+        r = expand_names(bus, strv_skip(argv, 1), NULL, &names);
+        if (r < 0)
+                return log_error_errno(r, "Failed to expand names: %m");
+
+        if (!arg_no_block) {
+                r = bus_wait_for_units_new(bus, &w);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to allocate unit waiter: %m");
+        }
+
+        STRV_FOREACH(name, names) {
+                _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+                _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+
+                if (w) {
+                        /* If we shall wait for the cleaning to complete, let's add a ref on the unit first */
+                        r = sd_bus_call_method(
+                                        bus,
+                                        "org.freedesktop.systemd1",
+                                        "/org/freedesktop/systemd1",
+                                        "org.freedesktop.systemd1.Manager",
+                                        "RefUnit",
+                                        &error,
+                                        NULL,
+                                        "s", *name);
+                        if (r < 0) {
+                                log_error_errno(r, "Failed to add reference to unit %s: %s", *name, bus_error_message(&error, r));
+                                if (ret == EXIT_SUCCESS)
+                                        ret = r;
+                                continue;
+                        }
+                }
+
+                r = sd_bus_message_new_method_call(
+                                bus,
+                                &m,
+                                "org.freedesktop.systemd1",
+                                "/org/freedesktop/systemd1",
+                                "org.freedesktop.systemd1.Manager",
+                                "CleanUnit");
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_append(m, "s", *name);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_append_strv(m, arg_clean_what);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_call(bus, m, 0, &error, NULL);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to clean unit %s: %s", *name, bus_error_message(&error, r));
+                        if (ret == EXIT_SUCCESS) {
+                                ret = r;
+                                continue;
+                        }
+                }
+
+                if (w) {
+                        r = bus_wait_for_units_add_unit(w, *name, BUS_WAIT_REFFED|BUS_WAIT_FOR_MAINTENANCE_END, NULL, NULL);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to watch unit %s: %m", *name);
+                }
+        }
+
+        r = bus_wait_for_units_run(w);
+        if (r < 0)
+                return log_error_errno(r, "Failed to wait for units: %m");
+        if (r == BUS_WAIT_FAILURE)
+                ret = EXIT_FAILURE;
+
+        return ret;
+}
+
 typedef struct ExecStatusInfo {
         char *name;
 
@@ -7712,6 +7810,7 @@ static int systemctl_help(void) {
                "                      When shutting down or sleeping, ignore inhibitors\n"
                "     --kill-who=WHO   Who to send signal to\n"
                "  -s --signal=SIGNAL  Which signal to send\n"
+               "     --what=RESOURCES Which types of resources to remove\n"
                "     --now            Start or stop unit in addition to enabling or disabling it\n"
                "     --dry-run        Only print what would be done\n"
                "  -q --quiet          Suppress output\n"
@@ -7760,6 +7859,8 @@ static int systemctl_help(void) {
                "                                      if supported, otherwise restart\n"
                "  isolate UNIT                        Start one unit and stop all others\n"
                "  kill UNIT...                        Send signal to processes of a unit\n"
+               "  clean UNIT...                       Clean runtime, cache, state, logs or\n"
+               "                                      or configuration of unit\n"
                "  is-active PATTERN...                Check whether units are active\n"
                "  is-failed PATTERN...                Check whether units are failed\n"
                "  status [PATTERN...|PID...]          Show runtime status of one or more units\n"
@@ -8063,6 +8164,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 ARG_NOW,
                 ARG_MESSAGE,
                 ARG_WAIT,
+                ARG_WHAT,
         };
 
         static const struct option options[] = {
@@ -8114,6 +8216,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 { "now",                 no_argument,       NULL, ARG_NOW                 },
                 { "message",             required_argument, NULL, ARG_MESSAGE             },
                 { "show-transaction",    no_argument,       NULL, 'T'                     },
+                { "what",                required_argument, NULL, ARG_WHAT                },
                 {}
         };
 
@@ -8466,6 +8569,38 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         arg_show_transaction = true;
                         break;
 
+                case ARG_WHAT: {
+                        const char *p;
+
+                        if (isempty(optarg))
+                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--what= requires arguments.");
+
+                        for (p = optarg;;) {
+                                _cleanup_free_ char *k = NULL;
+
+                                r = extract_first_word(&p, &k, ",", 0);
+                                if (r < 0)
+                                        return log_error_errno(r, "Failed to parse directory type: %s", optarg);
+                                if (r == 0)
+                                        break;
+
+                                if (streq(k, "help")) {
+                                        puts("runtime\n"
+                                             "state\n"
+                                             "cache\n"
+                                             "logs\n"
+                                             "configuration");
+                                        return 0;
+                                }
+
+                                r = strv_consume(&arg_clean_what, TAKE_PTR(k));
+                                if (r < 0)
+                                        return log_oom();
+                        }
+
+                        break;
+                }
+
                 case '.':
                         /* Output an error mimicking getopt, and print a hint afterwards */
                         log_error("%s: invalid option -- '.'", program_invocation_name);
@@ -8912,6 +9047,7 @@ static int systemctl_main(int argc, char *argv[]) {
                 { "condrestart",           2,        VERB_ANY, VERB_ONLINE_ONLY, start_unit           }, /* For compatibility with RH */
                 { "isolate",               2,        2,        VERB_ONLINE_ONLY, start_unit           },
                 { "kill",                  2,        VERB_ANY, VERB_ONLINE_ONLY, kill_unit            },
+                { "clean",                 2,        VERB_ANY, VERB_ONLINE_ONLY, clean_unit           },
                 { "is-active",             2,        VERB_ANY, VERB_ONLINE_ONLY, check_unit_active    },
                 { "check",                 2,        VERB_ANY, VERB_ONLINE_ONLY, check_unit_active    }, /* deprecated alias of is-active */
                 { "is-failed",             2,        VERB_ANY, VERB_ONLINE_ONLY, check_unit_failed    },