X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fsystemctl%2Fsystemctl.c;h=8f854130e40308b4673cfffb2c0011688bd9af15;hp=01a4489f4d09e4ed82feaa761965827ecfd42d71;hb=8fcf784dffba1ca24c2790b96c18dd689d4981fb;hpb=39602c3905da5196987b4df744541295ada725ae diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 01a4489f4..8f854130e 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -58,7 +58,6 @@ #include "path-lookup.h" #include "conf-parser.h" #include "exit-status.h" -#include "bus-errors.h" #include "build.h" #include "unit-name.h" #include "pager.h" @@ -68,6 +67,7 @@ #include "logs-show.h" #include "socket-util.h" #include "fileio.h" +#include "env-util.h" #include "bus-util.h" #include "bus-message.h" #include "bus-error.h" @@ -908,6 +908,31 @@ static int output_timers_list(struct timer_info *timer_infos, unsigned n) { return 0; } +static usec_t calc_next_elapse(dual_timestamp *nw, dual_timestamp *next) { + usec_t next_elapse; + + assert(nw); + assert(next); + + if (next->monotonic != (usec_t) -1 && next->monotonic > 0) { + usec_t converted; + + if (next->monotonic > nw->monotonic) + converted = nw->realtime + (next->monotonic - nw->monotonic); + else + converted = nw->realtime - (nw->monotonic - next->monotonic); + + if (next->realtime != (usec_t) -1 && next->realtime > 0) + next_elapse = MIN(converted, next->realtime); + else + next_elapse = converted; + + } else + next_elapse = next->realtime; + + return next_elapse; +} + static int list_timers(sd_bus *bus, char **args) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; @@ -930,7 +955,7 @@ static int list_timers(sd_bus *bus, char **args) { for (u = unit_infos; u < unit_infos + n; u++) { _cleanup_strv_free_ char **triggered = NULL; - dual_timestamp next; + dual_timestamp next = {}; usec_t m; if (!endswith(u->id, ".timer")) @@ -944,26 +969,13 @@ static int list_timers(sd_bus *bus, char **args) { if (r < 0) goto cleanup; - if (next.monotonic != (usec_t) -1 && next.monotonic > 0) { - usec_t converted; - - if (next.monotonic > nw.monotonic) - converted = nw.realtime + (next.monotonic - nw.monotonic); - else - converted = nw.realtime - (nw.monotonic - next.monotonic); - - if (next.realtime != (usec_t) -1 && next.realtime > 0) - m = MIN(converted, next.realtime); - else - m = converted; - } else - m = next.realtime; - if (!GREEDY_REALLOC(timer_infos, size, c+1)) { r = log_oom(); goto cleanup; } + m = calc_next_elapse(&nw, &next); + timer_infos[c++] = (struct timer_info) { .id = u->id, .next_elapse = m, @@ -1019,7 +1031,7 @@ static bool output_show_unit_file(const UnitFileList *u, char **patterns) { } static void output_unit_file_list(const UnitFileList *units, unsigned c) { - unsigned max_id_len, id_cols, state_cols, n_shown = 0; + unsigned max_id_len, id_cols, state_cols; const UnitFileList *u; max_id_len = sizeof("UNIT FILE")-1; @@ -1050,8 +1062,6 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) { const char *on, *off; const char *id; - n_shown++; - if (u->state == UNIT_FILE_MASKED || u->state == UNIT_FILE_MASKED_RUNTIME || u->state == UNIT_FILE_DISABLED || @@ -1074,7 +1084,7 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) { } if (!arg_no_legend) - printf("\n%u unit files listed.\n", n_shown); + printf("\n%u unit files listed.\n", c); } static int list_unit_files(sd_bus *bus, char **args) { @@ -1327,7 +1337,7 @@ static int list_dependencies_one( char ***units, unsigned int branches) { - _cleanup_strv_free_ char **deps = NULL, **u; + _cleanup_strv_free_ char **deps = NULL; char **c; int r = 0; @@ -1335,8 +1345,8 @@ static int list_dependencies_one( assert(name); assert(units); - u = strv_append(*units, name); - if (!u) + r = strv_extend(units, name); + if (r < 0) return log_oom(); r = list_dependencies_get_dependencies(bus, name, &deps); @@ -1348,7 +1358,7 @@ static int list_dependencies_one( STRV_FOREACH(c, deps) { int state; - if (strv_contains(u, *c)) { + if (strv_contains(*units, *c)) { if (!arg_plain) { r = list_dependencies_print("...", level + 1, (branches << 1) | (c[1] == NULL ? 0 : 1), 1); if (r < 0) @@ -1368,17 +1378,14 @@ static int list_dependencies_one( return r; if (arg_all || unit_name_to_type(*c) == UNIT_TARGET) { - r = list_dependencies_one(bus, *c, level + 1, &u, (branches << 1) | (c[1] == NULL ? 0 : 1)); + r = list_dependencies_one(bus, *c, level + 1, units, (branches << 1) | (c[1] == NULL ? 0 : 1)); if (r < 0) return r; } } - if (arg_plain) { - strv_free(*units); - *units = u; - u = NULL; - } + if (!arg_plain) + strv_remove(*units, name); return 0; } @@ -1405,6 +1412,228 @@ static int list_dependencies(sd_bus *bus, char **args) { return list_dependencies_one(bus, u, 0, &units, 0); } +struct machine_info { + bool is_host; + char *name; + char *state; + char *control_group; + uint32_t n_failed_units; + uint32_t n_jobs; + usec_t timestamp; +}; + +static const struct bus_properties_map machine_info_property_map[] = { + { "SystemState", "s", NULL, offsetof(struct machine_info, state) }, + { "NJobs", "u", NULL, offsetof(struct machine_info, n_jobs) }, + { "NFailedUnits", "u", NULL, offsetof(struct machine_info, n_failed_units) }, + { "ControlGroup", "s", NULL, offsetof(struct machine_info, control_group) }, + { "UserspaceTimestamp", "t", NULL, offsetof(struct machine_info, timestamp) }, + {} +}; + +static void free_machines_list(struct machine_info *machine_infos, int n) { + int i; + + if (!machine_infos) + return; + + for (i = 0; i < n; i++) { + free(machine_infos[i].name); + free(machine_infos[i].state); + free(machine_infos[i].control_group); + } + + free(machine_infos); +} + +static int compare_machine_info(const void *a, const void *b) { + const struct machine_info *u = a, *v = b; + + if (u->is_host != v->is_host) + return u->is_host > v->is_host ? 1 : -1; + + return strcasecmp(u->name, v->name); +} + +static int get_machine_properties(sd_bus *bus, struct machine_info *mi) { + _cleanup_bus_unref_ sd_bus *container = NULL; + int r; + + assert(mi); + + if (!bus) { + r = sd_bus_open_system_container(&container, mi->name); + if (r < 0) + return r; + + bus = container; + } + + r = bus_map_all_properties(bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", machine_info_property_map, mi); + if (r < 0) + return r; + + return 0; +} + +static bool output_show_machine(const char *name, char **patterns) { + char **i; + + assert(name); + + if (strv_isempty(patterns)) + return true; + + STRV_FOREACH(i, patterns) + if (fnmatch(*i, name, FNM_NOESCAPE) == 0) + return true; + + return false; +} + +static int get_machine_list( + sd_bus *bus, + struct machine_info **_machine_infos, + char **patterns) { + + struct machine_info *machine_infos = NULL; + _cleanup_strv_free_ char **m = NULL; + _cleanup_free_ char *hn = NULL; + size_t sz = 0; + char **i; + int c = 0; + + hn = gethostname_malloc(); + if (!hn) + return log_oom(); + + if (output_show_machine(hn, patterns)) { + if (!GREEDY_REALLOC0(machine_infos, sz, c+1)) + return log_oom(); + + machine_infos[c].is_host = true; + machine_infos[c].name = hn; + hn = NULL; + + get_machine_properties(bus, &machine_infos[c]); + c++; + } + + sd_get_machine_names(&m); + STRV_FOREACH(i, m) { + _cleanup_free_ char *class = NULL; + + if (!output_show_machine(*i, patterns)) + continue; + + sd_machine_get_class(*i, &class); + if (!streq_ptr(class, "container")) + continue; + + if (!GREEDY_REALLOC0(machine_infos, sz, c+1)) { + free_machines_list(machine_infos, c); + return log_oom(); + } + + machine_infos[c].is_host = false; + machine_infos[c].name = strdup(*i); + if (!machine_infos[c].name) { + free_machines_list(machine_infos, c); + return log_oom(); + } + + get_machine_properties(NULL, &machine_infos[c]); + c++; + } + + *_machine_infos = machine_infos; + return c; +} + +static void output_machines_list(struct machine_info *machine_infos, unsigned n) { + struct machine_info *m; + unsigned + namelen = sizeof("NAME") - 1, + statelen = sizeof("STATE") - 1, + failedlen = sizeof("FAILED") - 1, + jobslen = sizeof("JOBS") - 1; + + assert(machine_infos || n == 0); + + for (m = machine_infos; m < machine_infos + n; m++) { + namelen = MAX(namelen, strlen(m->name) + (m->is_host ? sizeof(" (host)") - 1 : 0)); + statelen = MAX(statelen, m->state ? strlen(m->state) : 0); + failedlen = MAX(failedlen, DECIMAL_STR_WIDTH(m->n_failed_units)); + jobslen = MAX(jobslen, DECIMAL_STR_WIDTH(m->n_jobs)); + } + + if (!arg_no_legend) + printf("%-*s %-*s %-*s %-*s\n", + namelen, "NAME", + statelen, "STATE", + failedlen, "FAILED", + jobslen, "JOBS"); + + for (m = machine_infos; m < machine_infos + n; m++) { + const char *on_state, *off_state, *on_failed, *off_failed; + + if (streq_ptr(m->state, "degraded")) { + on_state = ansi_highlight_red(); + off_state = ansi_highlight_off(); + } else if (!streq_ptr(m->state, "running")) { + on_state = ansi_highlight_yellow(); + off_state = ansi_highlight_off(); + } else + on_state = off_state = ""; + + if (m->n_failed_units > 0) { + on_failed = ansi_highlight_red(); + off_failed = ansi_highlight_off(); + } else + on_failed = off_failed = ""; + + if (m->is_host) + printf("%-*s (host) %s%-*s%s %s%*u%s %*u\n", + (int) (namelen - (sizeof(" (host)")-1)), strna(m->name), + on_state, statelen, strna(m->state), off_state, + on_failed, failedlen, m->n_failed_units, off_failed, + jobslen, m->n_jobs); + else + printf("%-*s %s%-*s%s %s%*u%s %*u\n", + namelen, strna(m->name), + on_state, statelen, strna(m->state), off_state, + on_failed, failedlen, m->n_failed_units, off_failed, + jobslen, m->n_jobs); + } + + if (!arg_no_legend) + printf("\n%u machines listed.\n", n); +} + +static int list_machines(sd_bus *bus, char **args) { + struct machine_info *machine_infos = NULL; + int r; + + assert(bus); + + if (geteuid() != 0) { + log_error("Must be root."); + return -EPERM; + } + + pager_open_if_enabled(); + + r = get_machine_list(bus, &machine_infos, strv_skip_first(args)); + if (r < 0) + return r; + + qsort_safe(machine_infos, r, sizeof(struct machine_info), compare_machine_info); + output_machines_list(machine_infos, r); + free_machines_list(machine_infos, r); + + return 0; +} + static int get_default(sd_bus *bus, char **args) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; @@ -1616,16 +1845,17 @@ static void output_jobs_list(const struct job_info* jobs, unsigned n, bool skipp } static bool output_show_job(struct job_info *job, char **patterns) { - if (!strv_isempty(patterns)) { - char **pattern; + char **pattern; - STRV_FOREACH(pattern, patterns) - if (fnmatch(*pattern, job->name, FNM_NOESCAPE) == 0) - return true; - return false; - } + assert(job); - return true; + if (strv_isempty(patterns)) + return true; + + STRV_FOREACH(pattern, patterns) + if (fnmatch(*pattern, job->name, FNM_NOESCAPE) == 0) + return true; + return false; } static int list_jobs(sd_bus *bus, char **args) { @@ -2158,13 +2388,11 @@ static int expand_names(sd_bus *bus, char **names, const char* suffix, char ***r return log_oom(); if (string_is_glob(t)) - r = strv_push(&globs, t); + r = strv_consume(&globs, t); else - r = strv_push(&mangled, t); - if (r < 0) { - free(t); + r = strv_consume(&mangled, t); + if (r < 0) return log_oom(); - } } /* Query the manager only if any of the names are a glob, since @@ -2514,14 +2742,16 @@ static int start_special(sd_bus *bus, char **args) { static int check_unit_generic(sd_bus *bus, int code, const char *good_states, char **args) { _cleanup_strv_free_ char **names = NULL; char **name; - int r = code; + int r; assert(bus); assert(args); r = expand_names(bus, args, NULL, &names); - if (r < 0) + if (r < 0) { log_error("Failed to expand names: %s", strerror(-r)); + return r; + } STRV_FOREACH(name, names) { int state; @@ -2529,8 +2759,8 @@ static int check_unit_generic(sd_bus *bus, int code, const char *good_states, ch state = check_one_unit(bus, *name, good_states, arg_quiet); if (state < 0) return state; - if (state > 0) - r = 0; + if (state == 0) + r = code; } return r; @@ -2969,7 +3199,8 @@ static void print_status_info( printf(" Status: \"%s\"\n", i->status_text); if (i->control_group && - (i->main_pid > 0 || i->control_pid > 0 || cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, i->control_group, false) == 0)) { + (i->main_pid > 0 || i->control_pid > 0 || + ((arg_transport != BUS_TRANSPORT_LOCAL && arg_transport != BUS_TRANSPORT_CONTAINER) || cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, i->control_group, false) == 0))) { unsigned c; printf(" CGroup: %s\n", i->control_group); @@ -2977,7 +3208,7 @@ static void print_status_info( if (arg_transport == BUS_TRANSPORT_LOCAL || arg_transport == BUS_TRANSPORT_CONTAINER) { unsigned k = 0; pid_t extra[2]; - char prefix[] = " "; + static const char prefix[] = " "; c = columns(); if (c > sizeof(prefix) - 1) @@ -2991,8 +3222,7 @@ static void print_status_info( if (i->control_pid > 0) extra[k++] = i->control_pid; - show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, i->control_group, prefix, - c, false, extra, k, flags); + show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, i->control_group, prefix, c, false, extra, k, flags); } } @@ -3407,6 +3637,48 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte if (arg_all || !isempty(a) || !isempty(b)) printf("%s=%s \"%s\"\n", name, strempty(a), strempty(b)); + return 0; + } else if (streq_ptr(name, "SystemCallFilter")) { + _cleanup_strv_free_ char **l = NULL; + int whitelist; + + r = sd_bus_message_enter_container(m, 'r', "bas"); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_read(m, "b", &whitelist); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_read_strv(m, &l); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(m); + if (r < 0) + return bus_log_parse_error(r); + + if (arg_all || whitelist || !strv_isempty(l)) { + bool first = true; + char **i; + + fputs(name, stdout); + fputc('=', stdout); + + if (!whitelist) + fputc('~', stdout); + + STRV_FOREACH(i, l) { + if (first) + first = false; + else + fputc(' ', stdout); + + fputs(*i, stdout); + } + fputc('\n', stdout); + } + return 0; } @@ -3705,8 +3977,8 @@ static int show_one( streq(verb, "status")) { /* According to LSB: "program not running" */ /* 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 + * 1: program is dead and /run PID file exists + * 2: program is dead and /run/lock lock file exists * 3: program is not running * 4: program or service status is unknown */ @@ -3892,6 +4164,71 @@ static int cat(sd_bus *bus, char **args) { return r < 0 ? r : 0; } +static int show_system_status(sd_bus *bus) { + char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], since2[FORMAT_TIMESTAMP_MAX]; + _cleanup_free_ char *hn = NULL; + struct machine_info mi = {}; + const char *on, *off; + int r; + + hn = gethostname_malloc(); + if (!hn) + return log_oom(); + + r = bus_map_all_properties(bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", machine_info_property_map, &mi); + if (r < 0) { + log_error("Failed to read server status: %s", strerror(-r)); + return r; + } + + printf("%s\n", arg_host ? arg_host : hn); + + if (streq_ptr(mi.state, "degraded")) { + on = ansi_highlight_red(); + off = ansi_highlight_off(); + } else if (!streq_ptr(mi.state, "running")) { + on = ansi_highlight_yellow(); + off = ansi_highlight_off(); + } else + on = off = ""; + + printf(" State: %s%s%s\n", + on, strna(mi.state), off); + + printf(" Jobs: %u queued\n", mi.n_jobs); + printf(" Failed: %u units\n", mi.n_failed_units); + + printf(" Since: %s; %s\n", + format_timestamp(since2, sizeof(since2), mi.timestamp), + format_timestamp_relative(since1, sizeof(since1), mi.timestamp)); + + printf(" CGroup: %s\n", mi.control_group ?: "/"); + if (arg_transport == BUS_TRANSPORT_LOCAL || arg_transport == BUS_TRANSPORT_CONTAINER) { + int flags = + arg_all * OUTPUT_SHOW_ALL | + (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH | + on_tty() * OUTPUT_COLOR | + !arg_quiet * OUTPUT_WARN_CUTOFF | + arg_full * OUTPUT_FULL_WIDTH; + + static const char prefix[] = " "; + unsigned c; + + c = columns(); + if (c > sizeof(prefix) - 1) + c -= sizeof(prefix) - 1; + else + c = 0; + + show_cgroup(SYSTEMD_CGROUP_CONTROLLER, strempty(mi.control_group), prefix, c, false, flags); + } + + free(mi.state); + free(mi.control_group); + + return 0; +} + static int show(sd_bus *bus, char **args) { bool show_properties, show_status, new_line = false; bool ellipsized = false; @@ -3911,9 +4248,17 @@ static int show(sd_bus *bus, char **args) { if (show_properties && strv_length(args) <= 1) return show_one(args[0], bus, "/org/freedesktop/systemd1", show_properties, &new_line, &ellipsized); - if (show_status && strv_length(args) <= 1) - ret = show_all(args[0], bus, false, &new_line, &ellipsized); - else { + if (show_status && strv_length(args) <= 1) { + + if (arg_all) + pager_open_if_enabled(); + + show_system_status(bus); + new_line = true; + + if (arg_all) + ret = show_all(args[0], bus, false, &new_line, &ellipsized); + } else { _cleanup_free_ char **patterns = NULL; char **name; @@ -3968,163 +4313,6 @@ static int show(sd_bus *bus, char **args) { return ret; } -static int append_assignment(sd_bus_message *m, const char *assignment) { - const char *eq; - char *field; - int r; - - assert(m); - assert(assignment); - - eq = strchr(assignment, '='); - if (!eq) { - log_error("Not an assignment: %s", assignment); - return -EINVAL; - } - - field = strndupa(assignment, eq - assignment); - eq ++; - - r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); - if (r < 0) - return bus_log_create_error(r); - - if (streq(field, "CPUAccounting") || - streq(field, "MemoryAccounting") || - streq(field, "BlockIOAccounting")) { - - r = parse_boolean(eq); - if (r < 0) { - log_error("Failed to parse boolean assignment %s.", assignment); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "b", r); - - } else if (streq(field, "MemoryLimit")) { - off_t bytes; - - r = parse_bytes(eq, &bytes); - if (r < 0) { - log_error("Failed to parse bytes specification %s", assignment); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "t", (uint64_t) bytes); - - } 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; - } - - r = sd_bus_message_append(m, "v", "t", u); - - } else if (streq(field, "DevicePolicy")) - r = sd_bus_message_append(m, "v", "s", eq); - - else if (streq(field, "DeviceAllow")) { - - if (isempty(eq)) - r = sd_bus_message_append(m, "v", "a(ss)", 0); - else { - const char *path, *rwm; - char *e; - - e = strchr(eq, ' '); - if (e) { - path = strndupa(eq, e - eq); - rwm = e+1; - } else { - path = eq; - rwm = ""; - } - - if (!path_startswith(path, "/dev")) { - log_error("%s is not a device file in /dev.", path); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "a(ss)", 1, path, rwm); - } - - } else if (streq(field, "BlockIOReadBandwidth") || streq(field, "BlockIOWriteBandwidth")) { - - if (isempty(eq)) - r = sd_bus_message_append(m, "v", "a(st)", 0); - else { - const char *path, *bandwidth; - off_t bytes; - char *e; - - e = strchr(eq, ' '); - if (e) { - path = strndupa(eq, e - eq); - bandwidth = e+1; - } else { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - if (!path_startswith(path, "/dev")) { - log_error("%s is not a device file in /dev.", path); - return -EINVAL; - } - - r = parse_bytes(bandwidth, &bytes); - if (r < 0) { - log_error("Failed to parse byte value %s.", bandwidth); - return -EINVAL; - } - - r = sd_bus_message_append(m, "v", "a(st)", 1, path, (uint64_t) bytes); - } - - } else if (streq(field, "BlockIODeviceWeight")) { - - if (isempty(eq)) - r = sd_bus_message_append(m, "v", "a(st)", 0); - else { - const char *path, *weight; - uint64_t u; - char *e; - - e = strchr(eq, ' '); - if (e) { - path = strndupa(eq, e - eq); - weight = e+1; - } else { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } - - if (!path_startswith(path, "/dev")) { - log_error("%s is not a device file in /dev.", path); - return -EINVAL; - } - - r = safe_atou64(weight, &u); - if (r < 0) { - log_error("Failed to parse %s value %s.", field, weight); - return -EINVAL; - } - r = sd_bus_message_append(m, "v", "a(st)", path, u); - } - - } else { - log_error("Unknown assignment %s.", assignment); - return -EINVAL; - } - - if (r < 0) - return bus_log_create_error(r); - - return 0; -} - static int set_property(sd_bus *bus, char **args) { _cleanup_bus_message_unref_ sd_bus_message *m = NULL; _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; @@ -4134,11 +4322,11 @@ static int set_property(sd_bus *bus, char **args) { r = sd_bus_message_new_method_call( bus, + &m, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", - "SetUnitProperties", - &m); + "SetUnitProperties"); if (r < 0) return bus_log_create_error(r); @@ -4159,7 +4347,7 @@ static int set_property(sd_bus *bus, char **args) { if (r < 0) return bus_log_create_error(r); - r = append_assignment(m, *i); + r = bus_append_unit_property_assignment(m, *i); if (r < 0) return r; @@ -4388,8 +4576,8 @@ static int show_environment(sd_bus *bus, char **args) { static int switch_root(sd_bus *bus, char **args) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_free_ char *init = NULL; - const char *root; + _cleanup_free_ char *cmdline_init = NULL; + const char *root, *init; unsigned l; int r; @@ -4402,20 +4590,33 @@ static int switch_root(sd_bus *bus, char **args) { root = args[1]; if (l >= 3) - init = strdup(args[2]); + init = args[2]; else { - parse_env_file("/proc/cmdline", WHITESPACE, - "init", &init, - NULL); + r = parse_env_file("/proc/cmdline", WHITESPACE, + "init", &cmdline_init, + NULL); + if (r < 0) + log_debug("Failed to parse /proc/cmdline: %s", strerror(-r)); - if (!init) - init = strdup(""); + init = cmdline_init; } - if (!init) - return log_oom(); + if (isempty(init)) + init = NULL; + + if (init) { + const char *root_systemd_path = NULL, *root_init_path = NULL; + + root_systemd_path = strappenda(root, "/" SYSTEMD_BINARY_PATH); + root_init_path = strappenda3(root, "/", init); - log_debug("switching root - root: %s; init: %s", root, init); + /* If the passed init is actually the same as the + * systemd binary, then let's suppress it. */ + if (files_same(root_init_path, root_systemd_path) > 0) + init = NULL; + } + + log_debug("Switching root - root: %s; init: %s", root, strna(init)); r = sd_bus_call_method( bus, @@ -4449,11 +4650,11 @@ static int set_environment(sd_bus *bus, char **args) { r = sd_bus_message_new_method_call( bus, + &m, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", - method, - &m); + method); if (r < 0) return bus_log_create_error(r); @@ -4470,6 +4671,69 @@ static int set_environment(sd_bus *bus, char **args) { return 0; } +static int import_environment(sd_bus *bus, char **args) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_bus_message_unref_ sd_bus_message *m = NULL; + int r; + + assert(bus); + assert(args); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "SetEnvironment"); + if (r < 0) + return bus_log_create_error(r); + + if (strv_isempty(args + 1)) + r = sd_bus_message_append_strv(m, environ); + else { + char **a, **b; + + r = sd_bus_message_open_container(m, 'a', "s"); + if (r < 0) + return bus_log_create_error(r); + + STRV_FOREACH(a, args + 1) { + + if (!env_name_is_valid(*a)) { + log_error("Not a valid environment variable name: %s", *a); + return -EINVAL; + } + + STRV_FOREACH(b, environ) { + const char *eq; + + eq = startswith(*b, *a); + if (eq && *eq == '=') { + + r = sd_bus_message_append(m, "s", *b); + if (r < 0) + return bus_log_create_error(r); + + break; + } + } + } + + r = sd_bus_message_close_container(m); + } + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_call(bus, m, 0, &error, NULL); + if (r < 0) { + log_error("Failed to import environment: %s", bus_error_message(&error, r)); + return r; + } + + return 0; +} + static int enable_sysv_units(const char *verb, char **args) { int r = 0; @@ -4679,6 +4943,11 @@ static int enable_unit(sd_bus *bus, char **args) { if (r < 0) return r; + /* If the operation was fully executed by the SysV compat, + * let's finish early */ + if (strv_isempty(names)) + return 0; + if (!bus || avoid_bus()) { if (streq(verb, "enable")) { r = unit_file_enable(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes); @@ -4740,11 +5009,11 @@ static int enable_unit(sd_bus *bus, char **args) { r = sd_bus_message_new_method_call( bus, + &m, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", - method, - &m); + method); if (r < 0) return bus_log_create_error(r); @@ -4965,6 +5234,8 @@ static int systemctl_help(void) { " the search path\n" " get-default Get the name of the default target\n" " set-default NAME Set the default target\n\n" + "Machine Commands:\n" + " list-machines [PATTERN...] List local containers and host\n\n" "Job Commands:\n" " list-jobs [PATTERN...] List jobs\n" " cancel [JOB...] Cancel all, one, or more jobs\n\n" @@ -4974,7 +5245,8 @@ static int systemctl_help(void) { "Environment Commands:\n" " show-environment Dump environment\n" " set-environment NAME=VALUE... Set one or more environment variables\n" - " unset-environment NAME... Unset one or more environment variables\n\n" + " unset-environment NAME... Unset one or more environment variables\n" + " import-environment NAME... Import all, one or more environment variables\n\n" "Manager Lifecycle Commands:\n" " daemon-reload Reload systemd manager configuration\n" " daemon-reexec Reexecute systemd manager\n\n" @@ -5224,10 +5496,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) { if (!prop) return log_oom(); - if (strv_push(&arg_properties, prop) < 0) { - free(prop); + if (strv_consume(&arg_properties, prop) < 0) return log_oom(); - } } } @@ -5396,10 +5666,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) { if (!s) return log_oom(); - if (strv_push(&arg_states, s) < 0) { - free(s); + if (strv_consume(&arg_states, s) < 0) return log_oom(); - } } break; } @@ -5769,7 +6037,6 @@ static int runlevel_parse_argv(int argc, char *argv[]) { case ARG_HELP: return runlevel_help(); - return 0; case '?': return -EINVAL; @@ -5911,6 +6178,7 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) { { "list-sockets", MORE, 1, list_sockets }, { "list-timers", MORE, 1, list_timers }, { "list-jobs", MORE, 1, list_jobs }, + { "list-machines", MORE, 1, list_machines }, { "clear-jobs", EQUAL, 1, daemon_reload }, { "cancel", MORE, 2, cancel_job }, { "start", MORE, 2, start_unit }, @@ -5940,6 +6208,7 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) { { "show-environment", EQUAL, 1, show_environment }, { "set-environment", MORE, 2, set_environment }, { "unset-environment", MORE, 2, set_environment }, + { "import-environment", MORE, 1, import_environment}, { "halt", EQUAL, 1, start_special, FORCE }, { "poweroff", EQUAL, 1, start_special, FORCE }, { "reboot", EQUAL, 1, start_special, FORCE },