diff options
Diffstat (limited to 'src/shared')
| -rw-r--r-- | src/shared/bus-unit-util.c | 1287 | ||||
| -rw-r--r-- | src/shared/bus-unit-util.h | 57 | ||||
| -rw-r--r-- | src/shared/bus-util.c | 859 | ||||
| -rw-r--r-- | src/shared/bus-util.h | 33 | ||||
| -rw-r--r-- | src/shared/cgroup-show.c | 92 | ||||
| -rw-r--r-- | src/shared/cgroup-show.h | 8 | ||||
| -rw-r--r-- | src/shared/dropin.c | 2 | ||||
| -rw-r--r-- | src/shared/install.c | 59 | ||||
| -rw-r--r-- | src/shared/logs-show.c | 49 | ||||
| -rw-r--r-- | src/shared/logs-show.h | 3 | ||||
| -rw-r--r-- | src/shared/output-mode.c | 37 | ||||
| -rw-r--r-- | src/shared/output-mode.h | 11 | ||||
| -rw-r--r-- | src/shared/path-lookup.c | 11 | 
13 files changed, 1524 insertions, 984 deletions
| diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c new file mode 100644 index 0000000000..2b755cea28 --- /dev/null +++ b/src/shared/bus-unit-util.c @@ -0,0 +1,1287 @@ +/*** +  This file is part of systemd. + +  Copyright 2016 Lennart Poettering + +  systemd is free software; you can redistribute it and/or modify it +  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 +  Lesser General Public License for more details. + +  You should have received a copy of the GNU Lesser General Public License +  along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "alloc-util.h" +#include "bus-internal.h" +#include "bus-unit-util.h" +#include "bus-util.h" +#include "cgroup-util.h" +#include "env-util.h" +#include "escape.h" +#include "hashmap.h" +#include "list.h" +#include "locale-util.h" +#include "parse-util.h" +#include "path-util.h" +#include "process-util.h" +#include "rlimit-util.h" +#include "signal-util.h" +#include "string-util.h" +#include "syslog-util.h" +#include "terminal-util.h" +#include "utf8.h" +#include "util.h" + +int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) { +        assert(message); +        assert(u); + +        u->machine = NULL; + +        return sd_bus_message_read( +                        message, +                        "(ssssssouso)", +                        &u->id, +                        &u->description, +                        &u->load_state, +                        &u->active_state, +                        &u->sub_state, +                        &u->following, +                        &u->unit_path, +                        &u->job_id, +                        &u->job_type, +                        &u->job_path); +} + +int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) { +        const char *eq, *field; +        int r, rl; + +        assert(m); +        assert(assignment); + +        eq = strchr(assignment, '='); +        if (!eq) { +                log_error("Not an assignment: %s", assignment); +                return -EINVAL; +        } + +        r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); +        if (r < 0) +                return bus_log_create_error(r); + +        field = strndupa(assignment, eq - assignment); +        eq++; + +        if (streq(field, "CPUQuota")) { + +                if (isempty(eq)) +                        r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY); +                else if (endswith(eq, "%")) { +                        double percent; + +                        if (sscanf(eq, "%lf%%", &percent) != 1 || percent <= 0) { +                                log_error("CPU quota '%s' invalid.", eq); +                                return -EINVAL; +                        } + +                        r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", (usec_t) percent * USEC_PER_SEC / 100); +                } else { +                        log_error("CPU quota needs to be in percent."); +                        return -EINVAL; +                } + +                goto finish; + +        } else if (streq(field, "EnvironmentFile")) { + +                r = sd_bus_message_append(m, "sv", "EnvironmentFiles", "a(sb)", 1, +                                          eq[0] == '-' ? eq + 1 : eq, +                                          eq[0] == '-'); +                goto finish; + +        } else if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec", "RuntimeMaxSec")) { +                char *n; +                usec_t t; +                size_t l; +                r = parse_sec(eq, &t); +                if (r < 0) +                        return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq); + +                l = strlen(field); +                n = newa(char, l + 2); +                if (!n) +                        return log_oom(); + +                /* Change suffix Sec → USec */ +                strcpy(mempcpy(n, field, l - 3), "USec"); +                r = sd_bus_message_append(m, "sv", n, "t", t); +                goto finish; +        } + +        r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); +        if (r < 0) +                return bus_log_create_error(r); + +        rl = rlimit_from_string(field); +        if (rl >= 0) { +                const char *sn; +                struct rlimit l; + +                r = rlimit_parse(rl, eq, &l); +                if (r < 0) +                        return log_error_errno(r, "Failed to parse resource limit: %s", eq); + +                r = sd_bus_message_append(m, "v", "t", l.rlim_max); +                if (r < 0) +                        return bus_log_create_error(r); + +                r = sd_bus_message_close_container(m); +                if (r < 0) +                        return bus_log_create_error(r); + +                r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); +                if (r < 0) +                        return bus_log_create_error(r); + +                sn = strjoina(field, "Soft"); +                r = sd_bus_message_append(m, "sv", sn, "t", l.rlim_cur); + +        } else if (STR_IN_SET(field, +                       "CPUAccounting", "MemoryAccounting", "BlockIOAccounting", "TasksAccounting", +                       "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies", +                       "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit", +                       "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges", +                       "SyslogLevelPrefix", "Delegate", "RemainAfterElapse")) { + +                r = parse_boolean(eq); +                if (r < 0) +                        return log_error_errno(r, "Failed to parse boolean assignment %s.", assignment); + +                r = sd_bus_message_append(m, "v", "b", r); + +        } else if (streq(field, "MemoryLimit")) { +                uint64_t bytes; + +                if (isempty(eq) || streq(eq, "infinity")) +                        bytes = (uint64_t) -1; +                else { +                        r = parse_size(eq, 1024, &bytes); +                        if (r < 0) { +                                log_error("Failed to parse bytes specification %s", assignment); +                                return -EINVAL; +                        } +                } + +                r = sd_bus_message_append(m, "v", "t", bytes); + +        } else if (streq(field, "TasksMax")) { +                uint64_t n; + +                if (isempty(eq) || streq(eq, "infinity")) +                        n = (uint64_t) -1; +                else { +                        r = safe_atou64(eq, &n); +                        if (r < 0) { +                                log_error("Failed to parse maximum tasks specification %s", assignment); +                                return -EINVAL; +                        } +                } + +                r = sd_bus_message_append(m, "v", "t", n); + +        } else if (STR_IN_SET(field, "CPUShares", "StartupCPUShares")) { +                uint64_t u; + +                r = cg_cpu_shares_parse(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 (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight")) { +                uint64_t u; + +                r = cg_cpu_shares_parse(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 (STR_IN_SET(field, +                              "User", "Group", "DevicePolicy", "KillMode", +                              "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath", +                              "StandardInput", "StandardOutput", "StandardError", +                              "Description", "Slice", "Type", "WorkingDirectory", +                              "RootDirectory", "SyslogIdentifier", "ProtectSystem", +                              "ProtectHome")) +                r = sd_bus_message_append(m, "v", "s", eq); + +        else if (streq(field, "SyslogLevel")) { +                int level; + +                level = log_level_from_string(eq); +                if (level < 0) { +                        log_error("Failed to parse %s value %s.", field, eq); +                        return -EINVAL; +                } + +                r = sd_bus_message_append(m, "v", "i", level); + +        } else if (streq(field, "SyslogFacility")) { +                int facility; + +                facility = log_facility_unshifted_from_string(eq); +                if (facility < 0) { +                        log_error("Failed to parse %s value %s.", field, eq); +                        return -EINVAL; +                } + +                r = sd_bus_message_append(m, "v", "i", facility); + +        } else if (streq(field, "DeviceAllow")) { + +                if (isempty(eq)) +                        r = sd_bus_message_append(m, "v", "a(ss)", 0); +                else { +                        const char *path, *rwm, *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 (STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) { + +                if (isempty(eq)) +                        r = sd_bus_message_append(m, "v", "a(st)", 0); +                else { +                        const char *path, *bandwidth, *e; +                        uint64_t bytes; + +                        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_size(bandwidth, 1000, &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, bytes); +                } + +        } else if (streq(field, "BlockIODeviceWeight")) { + +                if (isempty(eq)) +                        r = sd_bus_message_append(m, "v", "a(st)", 0); +                else { +                        const char *path, *weight, *e; +                        uint64_t u; + +                        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 if (streq(field, "Nice")) { +                int32_t i; + +                r = safe_atoi32(eq, &i); +                if (r < 0) { +                        log_error("Failed to parse %s value %s.", field, eq); +                        return -EINVAL; +                } + +                r = sd_bus_message_append(m, "v", "i", i); + +        } else if (STR_IN_SET(field, "Environment", "PassEnvironment")) { +                const char *p; + +                r = sd_bus_message_open_container(m, 'v', "as"); +                if (r < 0) +                        return bus_log_create_error(r); + +                r = sd_bus_message_open_container(m, 'a', "s"); +                if (r < 0) +                        return bus_log_create_error(r); + +                p = eq; + +                for (;;) { +                        _cleanup_free_ char *word = NULL; + +                        r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE); +                        if (r < 0) { +                                log_error("Failed to parse Environment value %s", eq); +                                return -EINVAL; +                        } +                        if (r == 0) +                                break; + +                        if (streq(field, "Environment")) { +                                if (!env_assignment_is_valid(word)) { +                                        log_error("Invalid environment assignment: %s", word); +                                        return -EINVAL; +                                } +                        } else {  /* PassEnvironment */ +                                if (!env_name_is_valid(word)) { +                                        log_error("Invalid environment variable name: %s", word); +                                        return -EINVAL; +                                } +                        } + +                        r = sd_bus_message_append_basic(m, 's', word); +                        if (r < 0) +                                return bus_log_create_error(r); +                } + +                r = sd_bus_message_close_container(m); +                if (r < 0) +                        return bus_log_create_error(r); + +                r = sd_bus_message_close_container(m); + +        } else if (streq(field, "KillSignal")) { +                int sig; + +                sig = signal_from_string_try_harder(eq); +                if (sig < 0) { +                        log_error("Failed to parse %s value %s.", field, eq); +                        return -EINVAL; +                } + +                r = sd_bus_message_append(m, "v", "i", sig); + +        } else if (streq(field, "TimerSlackNSec")) { +                nsec_t n; + +                r = parse_nsec(eq, &n); +                if (r < 0) { +                        log_error("Failed to parse %s value %s", field, eq); +                        return -EINVAL; +                } + +                r = sd_bus_message_append(m, "v", "t", n); +        } else if (streq(field, "OOMScoreAdjust")) { +                int oa; + +                r = safe_atoi(eq, &oa); +                if (r < 0) { +                        log_error("Failed to parse %s value %s", field, eq); +                        return -EINVAL; +                } + +                if (!oom_score_adjust_is_valid(oa)) { +                        log_error("OOM score adjust value out of range"); +                        return -EINVAL; +                } + +                r = sd_bus_message_append(m, "v", "i", oa); +        } else if (STR_IN_SET(field, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories")) { +                const char *p; + +                r = sd_bus_message_open_container(m, 'v', "as"); +                if (r < 0) +                        return bus_log_create_error(r); + +                r = sd_bus_message_open_container(m, 'a', "s"); +                if (r < 0) +                        return bus_log_create_error(r); + +                p = eq; + +                for (;;) { +                        _cleanup_free_ char *word = NULL; +                        int offset; + +                        r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); +                        if (r < 0) { +                                log_error("Failed to parse %s value %s", field, eq); +                                return -EINVAL; +                        } +                        if (r == 0) +                                break; + +                        if (!utf8_is_valid(word)) { +                                log_error("Failed to parse %s value %s", field, eq); +                                return -EINVAL; +                        } + +                        offset = word[0] == '-'; +                        if (!path_is_absolute(word + offset)) { +                                log_error("Failed to parse %s value %s", field, eq); +                                return -EINVAL; +                        } + +                        path_kill_slashes(word + offset); + +                        r = sd_bus_message_append_basic(m, 's', word); +                        if (r < 0) +                                return bus_log_create_error(r); +                } + +                r = sd_bus_message_close_container(m); +                if (r < 0) +                        return bus_log_create_error(r); + +                r = sd_bus_message_close_container(m); + +        } else if (streq(field, "RuntimeDirectory")) { +                const char *p; + +                r = sd_bus_message_open_container(m, 'v', "as"); +                if (r < 0) +                        return bus_log_create_error(r); + +                r = sd_bus_message_open_container(m, 'a', "s"); +                if (r < 0) +                        return bus_log_create_error(r); + +                p = eq; + +                for (;;) { +                        _cleanup_free_ char *word = NULL; + +                        r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); +                        if (r < 0) +                                return log_error_errno(r, "Failed to parse %s value %s", field, eq); + +                        if (r == 0) +                                break; + +                        r = sd_bus_message_append_basic(m, 's', word); +                        if (r < 0) +                                return bus_log_create_error(r); +                } + +                r = sd_bus_message_close_container(m); +                if (r < 0) +                        return bus_log_create_error(r); + +                r = sd_bus_message_close_container(m); + +        } else { +                log_error("Unknown assignment %s.", assignment); +                return -EINVAL; +        } + +finish: +        if (r < 0) +                return bus_log_create_error(r); + +        r = sd_bus_message_close_container(m); +        if (r < 0) +                return bus_log_create_error(r); + +        return 0; +} + +typedef struct BusWaitForJobs { +        sd_bus *bus; +        Set *jobs; + +        char *name; +        char *result; + +        sd_bus_slot *slot_job_removed; +        sd_bus_slot *slot_disconnected; +} BusWaitForJobs; + +static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) { +        assert(m); + +        log_error("Warning! D-Bus connection terminated."); +        sd_bus_close(sd_bus_message_get_bus(m)); + +        return 0; +} + +static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { +        const char *path, *unit, *result; +        BusWaitForJobs *d = userdata; +        uint32_t id; +        char *found; +        int r; + +        assert(m); +        assert(d); + +        r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result); +        if (r < 0) { +                bus_log_parse_error(r); +                return 0; +        } + +        found = set_remove(d->jobs, (char*) path); +        if (!found) +                return 0; + +        free(found); + +        if (!isempty(result)) +                d->result = strdup(result); + +        if (!isempty(unit)) +                d->name = strdup(unit); + +        return 0; +} + +void bus_wait_for_jobs_free(BusWaitForJobs *d) { +        if (!d) +                return; + +        set_free_free(d->jobs); + +        sd_bus_slot_unref(d->slot_disconnected); +        sd_bus_slot_unref(d->slot_job_removed); + +        sd_bus_unref(d->bus); + +        free(d->name); +        free(d->result); + +        free(d); +} + +int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) { +        _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL; +        int r; + +        assert(bus); +        assert(ret); + +        d = new0(BusWaitForJobs, 1); +        if (!d) +                return -ENOMEM; + +        d->bus = sd_bus_ref(bus); + +        /* When we are a bus client we match by sender. Direct +         * connections OTOH have no initialized sender field, and +         * hence we ignore the sender then */ +        r = sd_bus_add_match( +                        bus, +                        &d->slot_job_removed, +                        bus->bus_client ? +                        "type='signal'," +                        "sender='org.freedesktop.systemd1'," +                        "interface='org.freedesktop.systemd1.Manager'," +                        "member='JobRemoved'," +                        "path='/org/freedesktop/systemd1'" : +                        "type='signal'," +                        "interface='org.freedesktop.systemd1.Manager'," +                        "member='JobRemoved'," +                        "path='/org/freedesktop/systemd1'", +                        match_job_removed, d); +        if (r < 0) +                return r; + +        r = sd_bus_add_match( +                        bus, +                        &d->slot_disconnected, +                        "type='signal'," +                        "sender='org.freedesktop.DBus.Local'," +                        "interface='org.freedesktop.DBus.Local'," +                        "member='Disconnected'", +                        match_disconnected, d); +        if (r < 0) +                return r; + +        *ret = d; +        d = NULL; + +        return 0; +} + +static int bus_process_wait(sd_bus *bus) { +        int r; + +        for (;;) { +                r = sd_bus_process(bus, NULL); +                if (r < 0) +                        return r; +                if (r > 0) +                        return 0; + +                r = sd_bus_wait(bus, (uint64_t) -1); +                if (r < 0) +                        return r; +        } +} + +static int bus_job_get_service_result(BusWaitForJobs *d, char **result) { +        _cleanup_free_ char *dbus_path = NULL; + +        assert(d); +        assert(d->name); +        assert(result); + +        dbus_path = unit_dbus_path_from_name(d->name); +        if (!dbus_path) +                return -ENOMEM; + +        return sd_bus_get_property_string(d->bus, +                                          "org.freedesktop.systemd1", +                                          dbus_path, +                                          "org.freedesktop.systemd1.Service", +                                          "Result", +                                          NULL, +                                          result); +} + +static const struct { +        const char *result, *explanation; +} explanations [] = { +        { "resources",   "of unavailable resources or another system error" }, +        { "timeout",     "a timeout was exceeded" }, +        { "exit-code",   "the control process exited with error code" }, +        { "signal",      "a fatal signal was delivered to the control process" }, +        { "core-dump",   "a fatal signal was delivered causing the control process to dump core" }, +        { "watchdog",    "the service failed to send watchdog ping" }, +        { "start-limit", "start of the service was attempted too often" } +}; + +static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) { +        _cleanup_free_ char *service_shell_quoted = NULL; +        const char *systemctl = "systemctl", *journalctl = "journalctl"; + +        assert(service); + +        service_shell_quoted = shell_maybe_quote(service); + +        if (extra_args && extra_args[1]) { +                _cleanup_free_ char *t; + +                t = strv_join((char**) extra_args, " "); +                systemctl = strjoina("systemctl ", t ? : "<args>"); +                journalctl = strjoina("journalctl ", t ? : "<args>"); +        } + +        if (!isempty(result)) { +                unsigned i; + +                for (i = 0; i < ELEMENTSOF(explanations); ++i) +                        if (streq(result, explanations[i].result)) +                                break; + +                if (i < ELEMENTSOF(explanations)) { +                        log_error("Job for %s failed because %s.\n" +                                  "See \"%s status %s\" and \"%s -xe\" for details.\n", +                                  service, +                                  explanations[i].explanation, +                                  systemctl, +                                  service_shell_quoted ?: "<service>", +                                  journalctl); +                        goto finish; +                } +        } + +        log_error("Job for %s failed.\n" +                  "See \"%s status %s\" and \"%s -xe\" for details.\n", +                  service, +                  systemctl, +                  service_shell_quoted ?: "<service>", +                  journalctl); + +finish: +        /* For some results maybe additional explanation is required */ +        if (streq_ptr(result, "start-limit")) +                log_info("To force a start use \"%1$s reset-failed %2$s\"\n" +                         "followed by \"%1$s start %2$s\" again.", +                         systemctl, +                         service_shell_quoted ?: "<service>"); +} + +static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) { +        int r = 0; + +        assert(d->result); + +        if (!quiet) { +                if (streq(d->result, "canceled")) +                        log_error("Job for %s canceled.", strna(d->name)); +                else if (streq(d->result, "timeout")) +                        log_error("Job for %s timed out.", strna(d->name)); +                else if (streq(d->result, "dependency")) +                        log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name)); +                else if (streq(d->result, "invalid")) +                        log_error("%s is not active, cannot reload.", strna(d->name)); +                else if (streq(d->result, "assert")) +                        log_error("Assertion failed on job for %s.", strna(d->name)); +                else if (streq(d->result, "unsupported")) +                        log_error("Operation on or unit type of %s not supported on this system.", strna(d->name)); +                else if (!streq(d->result, "done") && !streq(d->result, "skipped")) { +                        if (d->name) { +                                int q; +                                _cleanup_free_ char *result = NULL; + +                                q = bus_job_get_service_result(d, &result); +                                if (q < 0) +                                        log_debug_errno(q, "Failed to get Result property of service %s: %m", d->name); + +                                log_job_error_with_service_result(d->name, result, extra_args); +                        } else +                                log_error("Job failed. See \"journalctl -xe\" for details."); +                } +        } + +        if (streq(d->result, "canceled")) +                r = -ECANCELED; +        else if (streq(d->result, "timeout")) +                r = -ETIME; +        else if (streq(d->result, "dependency")) +                r = -EIO; +        else if (streq(d->result, "invalid")) +                r = -ENOEXEC; +        else if (streq(d->result, "assert")) +                r = -EPROTO; +        else if (streq(d->result, "unsupported")) +                r = -EOPNOTSUPP; +        else if (!streq(d->result, "done") && !streq(d->result, "skipped")) +                r = -EIO; + +        return r; +} + +int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) { +        int r = 0; + +        assert(d); + +        while (!set_isempty(d->jobs)) { +                int q; + +                q = bus_process_wait(d->bus); +                if (q < 0) +                        return log_error_errno(q, "Failed to wait for response: %m"); + +                if (d->result) { +                        q = check_wait_response(d, quiet, extra_args); +                        /* Return the first error as it is most likely to be +                         * meaningful. */ +                        if (q < 0 && r == 0) +                                r = q; + +                        log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name)); +                } + +                d->name = mfree(d->name); +                d->result = mfree(d->result); +        } + +        return r; +} + +int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) { +        int r; + +        assert(d); + +        r = set_ensure_allocated(&d->jobs, &string_hash_ops); +        if (r < 0) +                return r; + +        return set_put_strdup(d->jobs, path); +} + +int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) { +        int r; + +        r = bus_wait_for_jobs_add(d, path); +        if (r < 0) +                return log_oom(); + +        return bus_wait_for_jobs(d, quiet, NULL); +} + +int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) { +        const char *type, *path, *source; +        int r; + +        r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)"); +        if (r < 0) +                return bus_log_parse_error(r); + +        while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) { +                /* We expect only "success" changes to be sent over the bus. +                   Hence, reject anything negative. */ +                UnitFileChangeType ch = unit_file_change_type_from_string(type); + +                if (ch < 0) { +                        log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type, path); +                        continue; +                } + +                r = unit_file_changes_add(changes, n_changes, ch, path, source); +                if (r < 0) +                        return r; +        } +        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); + +        unit_file_dump_changes(0, NULL, *changes, *n_changes, false); +        return 0; +} + +struct CGroupInfo { +        char *cgroup_path; +        bool is_const; /* If false, cgroup_path should be free()'d */ + +        Hashmap *pids; /* PID → process name */ +        bool done; + +        struct CGroupInfo *parent; +        LIST_FIELDS(struct CGroupInfo, siblings); +        LIST_HEAD(struct CGroupInfo, children); +        size_t n_children; +}; + +static bool IS_ROOT(const char *p) { +        return isempty(p) || streq(p, "/"); +} + +static int add_cgroup(Hashmap *cgroups, const char *path, bool is_const, struct CGroupInfo **ret) { +        struct CGroupInfo *parent = NULL, *cg; +        int r; + +        assert(cgroups); +        assert(ret); + +        if (IS_ROOT(path)) +                path = "/"; + +        cg = hashmap_get(cgroups, path); +        if (cg) { +                *ret = cg; +                return 0; +        } + +        if (!IS_ROOT(path)) { +                const char *e, *pp; + +                e = strrchr(path, '/'); +                if (!e) +                        return -EINVAL; + +                pp = strndupa(path, e - path); +                if (!pp) +                        return -ENOMEM; + +                r = add_cgroup(cgroups, pp, false, &parent); +                if (r < 0) +                        return r; +        } + +        cg = new0(struct CGroupInfo, 1); +        if (!cg) +                return -ENOMEM; + +        if (is_const) +                cg->cgroup_path = (char*) path; +        else { +                cg->cgroup_path = strdup(path); +                if (!cg->cgroup_path) { +                        free(cg); +                        return -ENOMEM; +                } +        } + +        cg->is_const = is_const; +        cg->parent = parent; + +        r = hashmap_put(cgroups, cg->cgroup_path, cg); +        if (r < 0) { +                if (!is_const) +                        free(cg->cgroup_path); +                free(cg); +                return r; +        } + +        if (parent) { +                LIST_PREPEND(siblings, parent->children, cg); +                parent->n_children++; +        } + +        *ret = cg; +        return 1; +} + +static int add_process( +                Hashmap *cgroups, +                const char *path, +                pid_t pid, +                const char *name) { + +        struct CGroupInfo *cg; +        int r; + +        assert(cgroups); +        assert(name); +        assert(pid > 0); + +        r = add_cgroup(cgroups, path, true, &cg); +        if (r < 0) +                return r; + +        r = hashmap_ensure_allocated(&cg->pids, &trivial_hash_ops); +        if (r < 0) +                return r; + +        return hashmap_put(cg->pids, PID_TO_PTR(pid), (void*) name); +} + +static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) { +        assert(cgroups); +        assert(cg); + +        while (cg->children) +                remove_cgroup(cgroups, cg->children); + +        hashmap_remove(cgroups, cg->cgroup_path); + +        if (!cg->is_const) +                free(cg->cgroup_path); + +        hashmap_free(cg->pids); + +        if (cg->parent) +                LIST_REMOVE(siblings, cg->parent->children, cg); + +        free(cg); +} + +static int cgroup_info_compare_func(const void *a, const void *b) { +        const struct CGroupInfo *x = *(const struct CGroupInfo* const*) a, *y = *(const struct CGroupInfo* const*) b; + +        assert(x); +        assert(y); + +        return strcmp(x->cgroup_path, y->cgroup_path); +} + +static int dump_processes( +                Hashmap *cgroups, +                const char *cgroup_path, +                const char *prefix, +                unsigned n_columns, +                OutputFlags flags) { + +        struct CGroupInfo *cg; +        int r; + +        assert(prefix); + +        if (IS_ROOT(cgroup_path)) +                cgroup_path = "/"; + +        cg = hashmap_get(cgroups, cgroup_path); +        if (!cg) +                return 0; + +        if (!hashmap_isempty(cg->pids)) { +                const char *name; +                size_t n = 0, i; +                pid_t *pids; +                void *pidp; +                Iterator j; +                int width; + +                /* Order processes by their PID */ +                pids = newa(pid_t, hashmap_size(cg->pids)); + +                HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) +                        pids[n++] = PTR_TO_PID(pidp); + +                assert(n == hashmap_size(cg->pids)); +                qsort_safe(pids, n, sizeof(pid_t), pid_compare_func); + +                width = DECIMAL_STR_WIDTH(pids[n-1]); + +                for (i = 0; i < n; i++) { +                        _cleanup_free_ char *e = NULL; +                        const char *special; +                        bool more; + +                        name = hashmap_get(cg->pids, PID_TO_PTR(pids[i])); +                        assert(name); + +                        if (n_columns != 0) { +                                unsigned k; + +                                k = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U); + +                                e = ellipsize(name, k, 100); +                                if (e) +                                        name = e; +                        } + +                        more = i+1 < n || cg->children; +                        special = draw_special_char(more ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT); + +                        fprintf(stdout, "%s%s%*"PID_PRI" %s\n", +                                prefix, +                                special, +                                width, pids[i], +                                name); +                } +        } + +        if (cg->children) { +                struct CGroupInfo **children, *child; +                size_t n = 0, i; + +                /* Order subcgroups by their name */ +                children = newa(struct CGroupInfo*, cg->n_children); +                LIST_FOREACH(siblings, child, cg->children) +                        children[n++] = child; +                assert(n == cg->n_children); +                qsort_safe(children, n, sizeof(struct CGroupInfo*), cgroup_info_compare_func); + +                n_columns = MAX(LESS_BY(n_columns, 2U), 20U); + +                for (i = 0; i < n; i++) { +                        _cleanup_free_ char *pp = NULL; +                        const char *name, *special; +                        bool more; + +                        child = children[i]; + +                        name = strrchr(child->cgroup_path, '/'); +                        if (!name) +                                return -EINVAL; +                        name++; + +                        more = i+1 < n; +                        special = draw_special_char(more ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT); + +                        fputs(prefix, stdout); +                        fputs(special, stdout); +                        fputs(name, stdout); +                        fputc('\n', stdout); + +                        special = draw_special_char(more ? DRAW_TREE_VERTICAL : DRAW_TREE_SPACE); + +                        pp = strappend(prefix, special); +                        if (!pp) +                                return -ENOMEM; + +                        r = dump_processes(cgroups, child->cgroup_path, pp, n_columns, flags); +                        if (r < 0) +                                return r; +                } +        } + +        cg->done = true; +        return 0; +} + +static int dump_extra_processes( +                Hashmap *cgroups, +                const char *prefix, +                unsigned n_columns, +                OutputFlags flags) { + +        _cleanup_free_ pid_t *pids = NULL; +        _cleanup_hashmap_free_ Hashmap *names = NULL; +        struct CGroupInfo *cg; +        size_t n_allocated = 0, n = 0, k; +        Iterator i; +        int width, r; + +        /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as +         * combined, sorted, linear list. */ + +        HASHMAP_FOREACH(cg, cgroups, i) { +                const char *name; +                void *pidp; +                Iterator j; + +                if (cg->done) +                        continue; + +                if (hashmap_isempty(cg->pids)) +                        continue; + +                r = hashmap_ensure_allocated(&names, &trivial_hash_ops); +                if (r < 0) +                        return r; + +                if (!GREEDY_REALLOC(pids, n_allocated, n + hashmap_size(cg->pids))) +                        return -ENOMEM; + +                HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) { +                        pids[n++] = PTR_TO_PID(pidp); + +                        r = hashmap_put(names, pidp, (void*) name); +                        if (r < 0) +                                return r; +                } +        } + +        if (n == 0) +                return 0; + +        qsort_safe(pids, n, sizeof(pid_t), pid_compare_func); +        width = DECIMAL_STR_WIDTH(pids[n-1]); + +        for (k = 0; k < n; k++) { +                _cleanup_free_ char *e = NULL; +                const char *name; + +                name = hashmap_get(names, PID_TO_PTR(pids[k])); +                assert(name); + +                if (n_columns != 0) { +                        unsigned z; + +                        z = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U); + +                        e = ellipsize(name, z, 100); +                        if (e) +                                name = e; +                } + +                fprintf(stdout, "%s%s %*" PID_PRI " %s\n", +                        prefix, +                        draw_special_char(DRAW_TRIANGULAR_BULLET), +                        width, pids[k], +                        name); +        } + +        return 0; +} + +int unit_show_processes( +                sd_bus *bus, +                const char *unit, +                const char *cgroup_path, +                const char *prefix, +                unsigned n_columns, +                OutputFlags flags, +                sd_bus_error *error) { + +        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; +        Hashmap *cgroups = NULL; +        struct CGroupInfo *cg; +        int r; + +        assert(bus); +        assert(unit); + +        if (flags & OUTPUT_FULL_WIDTH) +                n_columns = 0; +        else if (n_columns <= 0) +                n_columns = columns(); + +        prefix = strempty(prefix); + +        r = sd_bus_call_method( +                        bus, +                        "org.freedesktop.systemd1", +                        "/org/freedesktop/systemd1", +                        "org.freedesktop.systemd1.Manager", +                        "GetUnitProcesses", +                        error, +                        &reply, +                        "s", +                        unit); +        if (r < 0) +                return r; + +        cgroups = hashmap_new(&string_hash_ops); +        if (!cgroups) +                return -ENOMEM; + +        r = sd_bus_message_enter_container(reply, 'a', "(sus)"); +        if (r < 0) +                goto finish; + +        for (;;) { +                const char *path = NULL, *name = NULL; +                uint32_t pid; + +                r = sd_bus_message_read(reply, "(sus)", &path, &pid, &name); +                if (r < 0) +                        goto finish; +                if (r == 0) +                        break; + +                r = add_process(cgroups, path, pid, name); +                if (r < 0) +                        goto finish; +        } + +        r = sd_bus_message_exit_container(reply); +        if (r < 0) +                goto finish; + +        r = dump_processes(cgroups, cgroup_path, prefix, n_columns, flags); +        if (r < 0) +                goto finish; + +        r = dump_extra_processes(cgroups, prefix, n_columns, flags); + +finish: +        while ((cg = hashmap_first(cgroups))) +               remove_cgroup(cgroups, cg); + +        hashmap_free(cgroups); + +        return r; +} diff --git a/src/shared/bus-unit-util.h b/src/shared/bus-unit-util.h new file mode 100644 index 0000000000..c0c172f336 --- /dev/null +++ b/src/shared/bus-unit-util.h @@ -0,0 +1,57 @@ +#pragma once + +/*** +  This file is part of systemd. + +  Copyright 2016 Lennart Poettering + +  systemd is free software; you can redistribute it and/or modify it +  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 +  Lesser General Public License for more details. + +  You should have received a copy of the GNU Lesser General Public License +  along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "sd-bus.h" + +#include "output-mode.h" +#include "install.h" + +typedef struct UnitInfo { +        const char *machine; +        const char *id; +        const char *description; +        const char *load_state; +        const char *active_state; +        const char *sub_state; +        const char *following; +        const char *unit_path; +        uint32_t job_id; +        const char *job_type; +        const char *job_path; +} UnitInfo; + +int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u); + +int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment); + +typedef struct BusWaitForJobs BusWaitForJobs; + +int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret); +void bus_wait_for_jobs_free(BusWaitForJobs *d); +int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path); +int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args); +int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet); + +DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForJobs*, bus_wait_for_jobs_free); + +int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes); + +int unit_show_processes(sd_bus *bus, const char *unit, const char *cgroup_path, const char *prefix, unsigned n_columns, OutputFlags flags, sd_bus_error *error); diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 6a1877d8aa..4efbf3710f 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -39,34 +39,16 @@  #include "bus-label.h"  #include "bus-message.h"  #include "bus-util.h" -#include "cgroup-util.h"  #include "def.h" -#include "env-util.h"  #include "escape.h" -#include "extract-word.h"  #include "fd-util.h" -#include "hashmap.h" -#include "install.h" -#include "kdbus.h" -#include "log.h" -#include "macro.h"  #include "missing.h"  #include "parse-util.h" -#include "path-util.h"  #include "proc-cmdline.h" -#include "process-util.h"  #include "rlimit-util.h" -#include "set.h" -#include "signal-util.h"  #include "stdio-util.h" -#include "string-util.h"  #include "strv.h" -#include "syslog-util.h" -#include "time-util.h" -#include "unit-name.h"  #include "user-util.h" -#include "utf8.h" -#include "util.h"  static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {          sd_event *e = userdata; @@ -1383,847 +1365,6 @@ int bus_log_create_error(int r) {          return log_error_errno(r, "Failed to create bus message: %m");  } -int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) { -        assert(message); -        assert(u); - -        u->machine = NULL; - -        return sd_bus_message_read( -                        message, -                        "(ssssssouso)", -                        &u->id, -                        &u->description, -                        &u->load_state, -                        &u->active_state, -                        &u->sub_state, -                        &u->following, -                        &u->unit_path, -                        &u->job_id, -                        &u->job_type, -                        &u->job_path); -} - -int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment) { -        const char *eq, *field; -        int r, rl; - -        assert(m); -        assert(assignment); - -        eq = strchr(assignment, '='); -        if (!eq) { -                log_error("Not an assignment: %s", assignment); -                return -EINVAL; -        } - -        r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); -        if (r < 0) -                return bus_log_create_error(r); - -        field = strndupa(assignment, eq - assignment); -        eq++; - -        if (streq(field, "CPUQuota")) { - -                if (isempty(eq)) -                        r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY); -                else if (endswith(eq, "%")) { -                        double percent; - -                        if (sscanf(eq, "%lf%%", &percent) != 1 || percent <= 0) { -                                log_error("CPU quota '%s' invalid.", eq); -                                return -EINVAL; -                        } - -                        r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", (usec_t) percent * USEC_PER_SEC / 100); -                } else { -                        log_error("CPU quota needs to be in percent."); -                        return -EINVAL; -                } - -                goto finish; - -        } else if (streq(field, "EnvironmentFile")) { - -                r = sd_bus_message_append(m, "sv", "EnvironmentFiles", "a(sb)", 1, -                                          eq[0] == '-' ? eq + 1 : eq, -                                          eq[0] == '-'); -                goto finish; - -        } else if (STR_IN_SET(field, "AccuracySec", "RandomizedDelaySec", "RuntimeMaxSec")) { -                char *n; -                usec_t t; -                size_t l; -                r = parse_sec(eq, &t); -                if (r < 0) -                        return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq); - -                l = strlen(field); -                n = newa(char, l + 2); -                if (!n) -                        return log_oom(); - -                /* Change suffix Sec → USec */ -                strcpy(mempcpy(n, field, l - 3), "USec"); -                r = sd_bus_message_append(m, "sv", n, "t", t); -                goto finish; -        } - -        r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); -        if (r < 0) -                return bus_log_create_error(r); - -        rl = rlimit_from_string(field); -        if (rl >= 0) { -                const char *sn; -                struct rlimit l; - -                r = rlimit_parse(rl, eq, &l); -                if (r < 0) -                        return log_error_errno(r, "Failed to parse resource limit: %s", eq); - -                r = sd_bus_message_append(m, "v", "t", l.rlim_max); -                if (r < 0) -                        return bus_log_create_error(r); - -                r = sd_bus_message_close_container(m); -                if (r < 0) -                        return bus_log_create_error(r); - -                r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); -                if (r < 0) -                        return bus_log_create_error(r); - -                sn = strjoina(field, "Soft"); -                r = sd_bus_message_append(m, "sv", sn, "t", l.rlim_cur); - -        } else if (STR_IN_SET(field, -                       "CPUAccounting", "MemoryAccounting", "BlockIOAccounting", "TasksAccounting", -                       "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies", -                       "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit", -                       "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges", -                       "SyslogLevelPrefix", "Delegate", "RemainAfterElapse")) { - -                r = parse_boolean(eq); -                if (r < 0) -                        return log_error_errno(r, "Failed to parse boolean assignment %s.", assignment); - -                r = sd_bus_message_append(m, "v", "b", r); - -        } else if (streq(field, "MemoryLimit")) { -                uint64_t bytes; - -                if (isempty(eq) || streq(eq, "infinity")) -                        bytes = (uint64_t) -1; -                else { -                        r = parse_size(eq, 1024, &bytes); -                        if (r < 0) { -                                log_error("Failed to parse bytes specification %s", assignment); -                                return -EINVAL; -                        } -                } - -                r = sd_bus_message_append(m, "v", "t", bytes); - -        } else if (streq(field, "TasksMax")) { -                uint64_t n; - -                if (isempty(eq) || streq(eq, "infinity")) -                        n = (uint64_t) -1; -                else { -                        r = safe_atou64(eq, &n); -                        if (r < 0) { -                                log_error("Failed to parse maximum tasks specification %s", assignment); -                                return -EINVAL; -                        } -                } - -                r = sd_bus_message_append(m, "v", "t", n); - -        } else if (STR_IN_SET(field, "CPUShares", "StartupCPUShares")) { -                uint64_t u; - -                r = cg_cpu_shares_parse(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 (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight")) { -                uint64_t u; - -                r = cg_cpu_shares_parse(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 (STR_IN_SET(field, -                              "User", "Group", "DevicePolicy", "KillMode", -                              "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath", -                              "StandardInput", "StandardOutput", "StandardError", -                              "Description", "Slice", "Type", "WorkingDirectory", -                              "RootDirectory", "SyslogIdentifier", "ProtectSystem", -                              "ProtectHome")) -                r = sd_bus_message_append(m, "v", "s", eq); - -        else if (streq(field, "SyslogLevel")) { -                int level; - -                level = log_level_from_string(eq); -                if (level < 0) { -                        log_error("Failed to parse %s value %s.", field, eq); -                        return -EINVAL; -                } - -                r = sd_bus_message_append(m, "v", "i", level); - -        } else if (streq(field, "SyslogFacility")) { -                int facility; - -                facility = log_facility_unshifted_from_string(eq); -                if (facility < 0) { -                        log_error("Failed to parse %s value %s.", field, eq); -                        return -EINVAL; -                } - -                r = sd_bus_message_append(m, "v", "i", facility); - -        } else if (streq(field, "DeviceAllow")) { - -                if (isempty(eq)) -                        r = sd_bus_message_append(m, "v", "a(ss)", 0); -                else { -                        const char *path, *rwm, *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 (STR_IN_SET(field, "BlockIOReadBandwidth", "BlockIOWriteBandwidth")) { - -                if (isempty(eq)) -                        r = sd_bus_message_append(m, "v", "a(st)", 0); -                else { -                        const char *path, *bandwidth, *e; -                        uint64_t bytes; - -                        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_size(bandwidth, 1000, &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, bytes); -                } - -        } else if (streq(field, "BlockIODeviceWeight")) { - -                if (isempty(eq)) -                        r = sd_bus_message_append(m, "v", "a(st)", 0); -                else { -                        const char *path, *weight, *e; -                        uint64_t u; - -                        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 if (streq(field, "Nice")) { -                int32_t i; - -                r = safe_atoi32(eq, &i); -                if (r < 0) { -                        log_error("Failed to parse %s value %s.", field, eq); -                        return -EINVAL; -                } - -                r = sd_bus_message_append(m, "v", "i", i); - -        } else if (STR_IN_SET(field, "Environment", "PassEnvironment")) { -                const char *p; - -                r = sd_bus_message_open_container(m, 'v', "as"); -                if (r < 0) -                        return bus_log_create_error(r); - -                r = sd_bus_message_open_container(m, 'a', "s"); -                if (r < 0) -                        return bus_log_create_error(r); - -                p = eq; - -                for (;;) { -                        _cleanup_free_ char *word = NULL; - -                        r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE); -                        if (r < 0) { -                                log_error("Failed to parse Environment value %s", eq); -                                return -EINVAL; -                        } -                        if (r == 0) -                                break; - -                        if (streq(field, "Environment")) { -                                if (!env_assignment_is_valid(word)) { -                                        log_error("Invalid environment assignment: %s", word); -                                        return -EINVAL; -                                } -                        } else {  /* PassEnvironment */ -                                if (!env_name_is_valid(word)) { -                                        log_error("Invalid environment variable name: %s", word); -                                        return -EINVAL; -                                } -                        } - -                        r = sd_bus_message_append_basic(m, 's', word); -                        if (r < 0) -                                return bus_log_create_error(r); -                } - -                r = sd_bus_message_close_container(m); -                if (r < 0) -                        return bus_log_create_error(r); - -                r = sd_bus_message_close_container(m); - -        } else if (streq(field, "KillSignal")) { -                int sig; - -                sig = signal_from_string_try_harder(eq); -                if (sig < 0) { -                        log_error("Failed to parse %s value %s.", field, eq); -                        return -EINVAL; -                } - -                r = sd_bus_message_append(m, "v", "i", sig); - -        } else if (streq(field, "TimerSlackNSec")) { -                nsec_t n; - -                r = parse_nsec(eq, &n); -                if (r < 0) { -                        log_error("Failed to parse %s value %s", field, eq); -                        return -EINVAL; -                } - -                r = sd_bus_message_append(m, "v", "t", n); -        } else if (streq(field, "OOMScoreAdjust")) { -                int oa; - -                r = safe_atoi(eq, &oa); -                if (r < 0) { -                        log_error("Failed to parse %s value %s", field, eq); -                        return -EINVAL; -                } - -                if (!oom_score_adjust_is_valid(oa)) { -                        log_error("OOM score adjust value out of range"); -                        return -EINVAL; -                } - -                r = sd_bus_message_append(m, "v", "i", oa); -        } else if (STR_IN_SET(field, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories")) { -                const char *p; - -                r = sd_bus_message_open_container(m, 'v', "as"); -                if (r < 0) -                        return bus_log_create_error(r); - -                r = sd_bus_message_open_container(m, 'a', "s"); -                if (r < 0) -                        return bus_log_create_error(r); - -                p = eq; - -                for (;;) { -                        _cleanup_free_ char *word = NULL; -                        int offset; - -                        r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); -                        if (r < 0) { -                                log_error("Failed to parse %s value %s", field, eq); -                                return -EINVAL; -                        } -                        if (r == 0) -                                break; - -                        if (!utf8_is_valid(word)) { -                                log_error("Failed to parse %s value %s", field, eq); -                                return -EINVAL; -                        } - -                        offset = word[0] == '-'; -                        if (!path_is_absolute(word + offset)) { -                                log_error("Failed to parse %s value %s", field, eq); -                                return -EINVAL; -                        } - -                        path_kill_slashes(word + offset); - -                        r = sd_bus_message_append_basic(m, 's', word); -                        if (r < 0) -                                return bus_log_create_error(r); -                } - -                r = sd_bus_message_close_container(m); -                if (r < 0) -                        return bus_log_create_error(r); - -                r = sd_bus_message_close_container(m); - -        } else if (streq(field, "RuntimeDirectory")) { -                const char *p; - -                r = sd_bus_message_open_container(m, 'v', "as"); -                if (r < 0) -                        return bus_log_create_error(r); - -                r = sd_bus_message_open_container(m, 'a', "s"); -                if (r < 0) -                        return bus_log_create_error(r); - -                p = eq; - -                for (;;) { -                        _cleanup_free_ char *word = NULL; - -                        r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); -                        if (r < 0) -                                return log_error_errno(r, "Failed to parse %s value %s", field, eq); - -                        if (r == 0) -                                break; - -                        r = sd_bus_message_append_basic(m, 's', word); -                        if (r < 0) -                                return bus_log_create_error(r); -                } - -                r = sd_bus_message_close_container(m); -                if (r < 0) -                        return bus_log_create_error(r); - -                r = sd_bus_message_close_container(m); - -        } else { -                log_error("Unknown assignment %s.", assignment); -                return -EINVAL; -        } - -finish: -        if (r < 0) -                return bus_log_create_error(r); - -        r = sd_bus_message_close_container(m); -        if (r < 0) -                return bus_log_create_error(r); - -        return 0; -} - -typedef struct BusWaitForJobs { -        sd_bus *bus; -        Set *jobs; - -        char *name; -        char *result; - -        sd_bus_slot *slot_job_removed; -        sd_bus_slot *slot_disconnected; -} BusWaitForJobs; - -static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) { -        assert(m); - -        log_error("Warning! D-Bus connection terminated."); -        sd_bus_close(sd_bus_message_get_bus(m)); - -        return 0; -} - -static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { -        const char *path, *unit, *result; -        BusWaitForJobs *d = userdata; -        uint32_t id; -        char *found; -        int r; - -        assert(m); -        assert(d); - -        r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result); -        if (r < 0) { -                bus_log_parse_error(r); -                return 0; -        } - -        found = set_remove(d->jobs, (char*) path); -        if (!found) -                return 0; - -        free(found); - -        if (!isempty(result)) -                d->result = strdup(result); - -        if (!isempty(unit)) -                d->name = strdup(unit); - -        return 0; -} - -void bus_wait_for_jobs_free(BusWaitForJobs *d) { -        if (!d) -                return; - -        set_free_free(d->jobs); - -        sd_bus_slot_unref(d->slot_disconnected); -        sd_bus_slot_unref(d->slot_job_removed); - -        sd_bus_unref(d->bus); - -        free(d->name); -        free(d->result); - -        free(d); -} - -int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) { -        _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL; -        int r; - -        assert(bus); -        assert(ret); - -        d = new0(BusWaitForJobs, 1); -        if (!d) -                return -ENOMEM; - -        d->bus = sd_bus_ref(bus); - -        /* When we are a bus client we match by sender. Direct -         * connections OTOH have no initialized sender field, and -         * hence we ignore the sender then */ -        r = sd_bus_add_match( -                        bus, -                        &d->slot_job_removed, -                        bus->bus_client ? -                        "type='signal'," -                        "sender='org.freedesktop.systemd1'," -                        "interface='org.freedesktop.systemd1.Manager'," -                        "member='JobRemoved'," -                        "path='/org/freedesktop/systemd1'" : -                        "type='signal'," -                        "interface='org.freedesktop.systemd1.Manager'," -                        "member='JobRemoved'," -                        "path='/org/freedesktop/systemd1'", -                        match_job_removed, d); -        if (r < 0) -                return r; - -        r = sd_bus_add_match( -                        bus, -                        &d->slot_disconnected, -                        "type='signal'," -                        "sender='org.freedesktop.DBus.Local'," -                        "interface='org.freedesktop.DBus.Local'," -                        "member='Disconnected'", -                        match_disconnected, d); -        if (r < 0) -                return r; - -        *ret = d; -        d = NULL; - -        return 0; -} - -static int bus_process_wait(sd_bus *bus) { -        int r; - -        for (;;) { -                r = sd_bus_process(bus, NULL); -                if (r < 0) -                        return r; -                if (r > 0) -                        return 0; - -                r = sd_bus_wait(bus, (uint64_t) -1); -                if (r < 0) -                        return r; -        } -} - -static int bus_job_get_service_result(BusWaitForJobs *d, char **result) { -        _cleanup_free_ char *dbus_path = NULL; - -        assert(d); -        assert(d->name); -        assert(result); - -        dbus_path = unit_dbus_path_from_name(d->name); -        if (!dbus_path) -                return -ENOMEM; - -        return sd_bus_get_property_string(d->bus, -                                          "org.freedesktop.systemd1", -                                          dbus_path, -                                          "org.freedesktop.systemd1.Service", -                                          "Result", -                                          NULL, -                                          result); -} - -static const struct { -        const char *result, *explanation; -} explanations [] = { -        { "resources",   "a configured resource limit was exceeded" }, -        { "timeout",     "a timeout was exceeded" }, -        { "exit-code",   "the control process exited with error code" }, -        { "signal",      "a fatal signal was delivered to the control process" }, -        { "core-dump",   "a fatal signal was delivered causing the control process to dump core" }, -        { "watchdog",    "the service failed to send watchdog ping" }, -        { "start-limit", "start of the service was attempted too often" } -}; - -static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) { -        _cleanup_free_ char *service_shell_quoted = NULL; -        const char *systemctl = "systemctl", *journalctl = "journalctl"; - -        assert(service); - -        service_shell_quoted = shell_maybe_quote(service); - -        if (extra_args && extra_args[1]) { -                _cleanup_free_ char *t; - -                t = strv_join((char**) extra_args, " "); -                systemctl = strjoina("systemctl ", t ? : "<args>"); -                journalctl = strjoina("journalctl ", t ? : "<args>"); -        } - -        if (!isempty(result)) { -                unsigned i; - -                for (i = 0; i < ELEMENTSOF(explanations); ++i) -                        if (streq(result, explanations[i].result)) -                                break; - -                if (i < ELEMENTSOF(explanations)) { -                        log_error("Job for %s failed because %s.\n" -                                  "See \"%s status %s\" and \"%s -xe\" for details.\n", -                                  service, -                                  explanations[i].explanation, -                                  systemctl, -                                  service_shell_quoted ?: "<service>", -                                  journalctl); -                        goto finish; -                } -        } - -        log_error("Job for %s failed.\n" -                  "See \"%s status %s\" and \"%s -xe\" for details.\n", -                  service, -                  systemctl, -                  service_shell_quoted ?: "<service>", -                  journalctl); - -finish: -        /* For some results maybe additional explanation is required */ -        if (streq_ptr(result, "start-limit")) -                log_info("To force a start use \"%1$s reset-failed %2$s\"\n" -                         "followed by \"%1$s start %2$s\" again.", -                         systemctl, -                         service_shell_quoted ?: "<service>"); -} - -static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) { -        int r = 0; - -        assert(d->result); - -        if (!quiet) { -                if (streq(d->result, "canceled")) -                        log_error("Job for %s canceled.", strna(d->name)); -                else if (streq(d->result, "timeout")) -                        log_error("Job for %s timed out.", strna(d->name)); -                else if (streq(d->result, "dependency")) -                        log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name)); -                else if (streq(d->result, "invalid")) -                        log_error("%s is not active, cannot reload.", strna(d->name)); -                else if (streq(d->result, "assert")) -                        log_error("Assertion failed on job for %s.", strna(d->name)); -                else if (streq(d->result, "unsupported")) -                        log_error("Operation on or unit type of %s not supported on this system.", strna(d->name)); -                else if (!streq(d->result, "done") && !streq(d->result, "skipped")) { -                        if (d->name) { -                                int q; -                                _cleanup_free_ char *result = NULL; - -                                q = bus_job_get_service_result(d, &result); -                                if (q < 0) -                                        log_debug_errno(q, "Failed to get Result property of service %s: %m", d->name); - -                                log_job_error_with_service_result(d->name, result, extra_args); -                        } else -                                log_error("Job failed. See \"journalctl -xe\" for details."); -                } -        } - -        if (streq(d->result, "canceled")) -                r = -ECANCELED; -        else if (streq(d->result, "timeout")) -                r = -ETIME; -        else if (streq(d->result, "dependency")) -                r = -EIO; -        else if (streq(d->result, "invalid")) -                r = -ENOEXEC; -        else if (streq(d->result, "assert")) -                r = -EPROTO; -        else if (streq(d->result, "unsupported")) -                r = -EOPNOTSUPP; -        else if (!streq(d->result, "done") && !streq(d->result, "skipped")) -                r = -EIO; - -        return r; -} - -int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) { -        int r = 0; - -        assert(d); - -        while (!set_isempty(d->jobs)) { -                int q; - -                q = bus_process_wait(d->bus); -                if (q < 0) -                        return log_error_errno(q, "Failed to wait for response: %m"); - -                if (d->result) { -                        q = check_wait_response(d, quiet, extra_args); -                        /* Return the first error as it is most likely to be -                         * meaningful. */ -                        if (q < 0 && r == 0) -                                r = q; - -                        log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name)); -                } - -                d->name = mfree(d->name); -                d->result = mfree(d->result); -        } - -        return r; -} - -int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) { -        int r; - -        assert(d); - -        r = set_ensure_allocated(&d->jobs, &string_hash_ops); -        if (r < 0) -                return r; - -        return set_put_strdup(d->jobs, path); -} - -int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) { -        int r; - -        r = bus_wait_for_jobs_add(d, path); -        if (r < 0) -                return log_oom(); - -        return bus_wait_for_jobs(d, quiet, NULL); -} - -int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes) { -        const char *type, *path, *source; -        int r; - -        r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)"); -        if (r < 0) -                return bus_log_parse_error(r); - -        while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) { -                /* We expect only "success" changes to be sent over the bus. -                   Hence, reject anything negative. */ -                UnitFileChangeType ch = unit_file_change_type_from_string(type); - -                if (ch < 0) { -                        log_notice("Manager reported unknown change type \"%s\" for path \"%s\", ignoring.", type, path); -                        continue; -                } - -                r = unit_file_changes_add(changes, n_changes, ch, path, source); -                if (r < 0) -                        return r; -        } -        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); - -        unit_file_dump_changes(0, NULL, *changes, *n_changes, false); -        return 0; -} -  /**   * bus_path_encode_unique() - encode unique object path   * @b: bus connection or NULL diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h index 1a0841ce81..d792258ecd 100644 --- a/src/shared/bus-util.h +++ b/src/shared/bus-util.h @@ -24,15 +24,12 @@  #include <stdint.h>  #include <sys/types.h> -#include "sd-bus-vtable.h"  #include "sd-bus.h"  #include "sd-event.h"  #include "hashmap.h" -#include "install.h"  #include "macro.h"  #include "string-util.h" -#include "time-util.h"  typedef enum BusTransport {          BUS_TRANSPORT_LOCAL, @@ -126,22 +123,6 @@ assert_cc(sizeof(mode_t) == sizeof(uint32_t));  int bus_log_parse_error(int r);  int bus_log_create_error(int r); -typedef struct UnitInfo { -        const char *machine; -        const char *id; -        const char *description; -        const char *load_state; -        const char *active_state; -        const char *sub_state; -        const char *following; -        const char *unit_path; -        uint32_t job_id; -        const char *job_type; -        const char *job_path; -} UnitInfo; - -int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u); -  #define BUS_DEFINE_PROPERTY_GET_ENUM(function, name, type)              \          int function(sd_bus *bus,                                       \                       const char *path,                                  \ @@ -173,20 +154,6 @@ int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u);          SD_BUS_PROPERTY(name, "t", bus_property_get_usec, (offset) + offsetof(struct dual_timestamp, realtime), (flags)), \          SD_BUS_PROPERTY(name "Monotonic", "t", bus_property_get_usec, (offset) + offsetof(struct dual_timestamp, monotonic), (flags)) -int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignment); - -typedef struct BusWaitForJobs BusWaitForJobs; - -int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret); -void bus_wait_for_jobs_free(BusWaitForJobs *d); -int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path); -int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args); -int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet); - -DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForJobs*, bus_wait_for_jobs_free); - -int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, unsigned *n_changes); -  int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, const char *external_id, char **ret_path);  int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external); diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c index f3039b23f7..7539891bf2 100644 --- a/src/shared/cgroup-show.c +++ b/src/shared/cgroup-show.c @@ -37,23 +37,21 @@  #include "string-util.h"  #include "terminal-util.h" -static int compare(const void *a, const void *b) { -        const pid_t *p = a, *q = b; +static void show_pid_array( +                pid_t pids[], +                unsigned n_pids, +                const char *prefix, +                unsigned n_columns, +                bool extra, +                bool more, +                OutputFlags flags) { -        if (*p < *q) -                return -1; -        if (*p > *q) -                return 1; -        return 0; -} - -static void show_pid_array(pid_t pids[], unsigned n_pids, const char *prefix, unsigned n_columns, bool extra, bool more, bool kernel_threads, OutputFlags flags) {          unsigned i, j, pid_width;          if (n_pids == 0)                  return; -        qsort(pids, n_pids, sizeof(pid_t), compare); +        qsort(pids, n_pids, sizeof(pid_t), pid_compare_func);          /* Filter duplicates */          for (j = 0, i = 1; i < n_pids; i++) { @@ -86,8 +84,13 @@ static void show_pid_array(pid_t pids[], unsigned n_pids, const char *prefix, un          }  } +static int show_cgroup_one_by_path( +                const char *path, +                const char *prefix, +                unsigned n_columns, +                bool more, +                OutputFlags flags) { -static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigned n_columns, bool more, bool kernel_threads, OutputFlags flags) {          char *fn;          _cleanup_fclose_ FILE *f = NULL;          size_t n = 0, n_allocated = 0; @@ -107,7 +110,7 @@ static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigne          while ((r = cg_read_pid(f, &pid)) > 0) { -                if (!kernel_threads && is_kernel_thread(pid) > 0) +                if (!(flags & OUTPUT_KERNEL_THREADS) && is_kernel_thread(pid) > 0)                          continue;                  if (!GREEDY_REALLOC(pids, n_allocated, n + 1)) @@ -120,12 +123,17 @@ static int show_cgroup_one_by_path(const char *path, const char *prefix, unsigne          if (r < 0)                  return r; -        show_pid_array(pids, n, prefix, n_columns, false, more, kernel_threads, flags); +        show_pid_array(pids, n, prefix, n_columns, false, more, flags);          return 0;  } -int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, OutputFlags flags) { +int show_cgroup_by_path( +                const char *path, +                const char *prefix, +                unsigned n_columns, +                OutputFlags flags) { +          _cleanup_free_ char *fn = NULL, *p1 = NULL, *last = NULL, *p2 = NULL;          _cleanup_closedir_ DIR *d = NULL;          char *gn = NULL; @@ -137,8 +145,7 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns          if (n_columns <= 0)                  n_columns = columns(); -        if (!prefix) -                prefix = ""; +        prefix = strempty(prefix);          r = cg_mangle_path(path, &fn);          if (r < 0) @@ -160,7 +167,7 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns                          continue;                  if (!shown_pids) { -                        show_cgroup_one_by_path(path, prefix, n_columns, true, kernel_threads, flags); +                        show_cgroup_one_by_path(path, prefix, n_columns, true, flags);                          shown_pids = true;                  } @@ -173,7 +180,7 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns                                          return -ENOMEM;                          } -                        show_cgroup_by_path(last, p1, n_columns-2, kernel_threads, flags); +                        show_cgroup_by_path(last, p1, n_columns-2, flags);                          free(last);                  } @@ -185,7 +192,7 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns                  return r;          if (!shown_pids) -                show_cgroup_one_by_path(path, prefix, n_columns, !!last, kernel_threads, flags); +                show_cgroup_one_by_path(path, prefix, n_columns, !!last, flags);          if (last) {                  printf("%s%s%s\n", prefix, draw_special_char(DRAW_TREE_RIGHT), cg_unescape(basename(last))); @@ -196,13 +203,17 @@ int show_cgroup_by_path(const char *path, const char *prefix, unsigned n_columns                                  return -ENOMEM;                  } -                show_cgroup_by_path(last, p2, n_columns-2, kernel_threads, flags); +                show_cgroup_by_path(last, p2, n_columns-2, flags);          }          return 0;  } -int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, OutputFlags flags) { +int show_cgroup(const char *controller, +                const char *path, +                const char *prefix, +                unsigned n_columns, +                OutputFlags flags) {          _cleanup_free_ char *p = NULL;          int r; @@ -212,10 +223,18 @@ int show_cgroup(const char *controller, const char *path, const char *prefix, un          if (r < 0)                  return r; -        return show_cgroup_by_path(p, prefix, n_columns, kernel_threads, flags); +        return show_cgroup_by_path(p, prefix, n_columns, flags);  } -static int show_extra_pids(const char *controller, const char *path, const char *prefix, unsigned n_columns, const pid_t pids[], unsigned n_pids, OutputFlags flags) { +static int show_extra_pids( +                const char *controller, +                const char *path, +                const char *prefix, +                unsigned n_columns, +                const pid_t pids[], +                unsigned n_pids, +                OutputFlags flags) { +          _cleanup_free_ pid_t *copy = NULL;          unsigned i, j;          int r; @@ -247,24 +266,39 @@ static int show_extra_pids(const char *controller, const char *path, const char                  copy[j++] = pids[i];          } -        show_pid_array(copy, j, prefix, n_columns, true, false, false, flags); +        show_pid_array(copy, j, prefix, n_columns, true, false, flags);          return 0;  } -int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags) { +int show_cgroup_and_extra( +                const char *controller, +                const char *path, +                const char *prefix, +                unsigned n_columns, +                const pid_t extra_pids[], +                unsigned n_extra_pids, +                OutputFlags flags) { +          int r;          assert(path); -        r = show_cgroup(controller, path, prefix, n_columns, kernel_threads, flags); +        r = show_cgroup(controller, path, prefix, n_columns, flags);          if (r < 0)                  return r;          return show_extra_pids(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags);  } -int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags) { +int show_cgroup_and_extra_by_spec( +                const char *spec, +                const char *prefix, +                unsigned n_columns, +                const pid_t extra_pids[], +                unsigned n_extra_pids, +                OutputFlags flags) { +          _cleanup_free_ char *controller = NULL, *path = NULL;          int r; @@ -274,5 +308,5 @@ int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned          if (r < 0)                  return r; -        return show_cgroup_and_extra(controller, path, prefix, n_columns, kernel_threads, extra_pids, n_extra_pids, flags); +        return show_cgroup_and_extra(controller, path, prefix, n_columns, extra_pids, n_extra_pids, flags);  } diff --git a/src/shared/cgroup-show.h b/src/shared/cgroup-show.h index 3ab7dfb33c..5c1d6e6d98 100644 --- a/src/shared/cgroup-show.h +++ b/src/shared/cgroup-show.h @@ -25,8 +25,8 @@  #include "logs-show.h"  #include "output-mode.h" -int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns, bool kernel_threads, OutputFlags flags); -int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned columns, bool kernel_threads, OutputFlags flags); +int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns, OutputFlags flags); +int show_cgroup(const char *controller, const char *path, const char *prefix, unsigned columns, OutputFlags flags); -int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags); -int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, bool kernel_threads, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags); +int show_cgroup_and_extra_by_spec(const char *spec, const char *prefix, unsigned n_columns, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags); +int show_cgroup_and_extra(const char *controller, const char *path, const char *prefix, unsigned n_columns, const pid_t extra_pids[], unsigned n_extra_pids, OutputFlags flags); diff --git a/src/shared/dropin.c b/src/shared/dropin.c index cc1acd6f23..b9cd952ac8 100644 --- a/src/shared/dropin.c +++ b/src/shared/dropin.c @@ -160,7 +160,7 @@ static int iterate_dir(                  if (!de)                          break; -                if (hidden_file(de->d_name)) +                if (hidden_or_backup_file(de->d_name))                          continue;                  f = strjoin(path, "/", de->d_name, NULL); diff --git a/src/shared/install.c b/src/shared/install.c index 71012eafb4..b74ff6de22 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -222,8 +222,8 @@ int unit_file_changes_add(                  const char *path,                  const char *source) { +        _cleanup_free_ char *p = NULL, *s = NULL;          UnitFileChange *c; -        unsigned i;          assert(path);          assert(!changes == !n_changes); @@ -234,29 +234,22 @@ int unit_file_changes_add(          c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange));          if (!c)                  return -ENOMEM; -          *changes = c; -        i = *n_changes; -        c[i].type = type; -        c[i].path = strdup(path); -        if (!c[i].path) -                return -ENOMEM; +        p = strdup(path); +        if (source) +                s = strdup(source); -        path_kill_slashes(c[i].path); - -        if (source) { -                c[i].source = strdup(source); -                if (!c[i].source) { -                        free(c[i].path); -                        return -ENOMEM; -                } +        if (!p || (source && !s)) +                return -ENOMEM; -                path_kill_slashes(c[i].path); -        } else -                c[i].source = NULL; +        path_kill_slashes(p); +        if (s) +                path_kill_slashes(s); -        *n_changes = i+1; +        c[*n_changes] = (UnitFileChange) { type, p, s }; +        p = s = NULL; +        (*n_changes) ++;          return 0;  } @@ -265,9 +258,6 @@ void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) {          assert(changes || n_changes == 0); -        if (!changes) -                return; -          for (i = 0; i < n_changes; i++) {                  free(changes[i].path);                  free(changes[i].source); @@ -370,8 +360,14 @@ static int create_symlink(          }          r = readlink_malloc(new_path, &dest); -        if (r < 0) +        if (r < 0) { +                /* translate EINVAL (non-symlink exists) to EEXIST */ +                if (r == -EINVAL) +                        r = -EEXIST; + +                unit_file_changes_add(changes, n_changes, r, new_path, NULL);                  return r; +        }          if (path_equal(dest, old_path))                  return 0; @@ -382,8 +378,10 @@ static int create_symlink(          }          r = symlink_atomic(old_path, new_path); -        if (r < 0) +        if (r < 0) { +                unit_file_changes_add(changes, n_changes, r, new_path, NULL);                  return r; +        }          unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL);          unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path); @@ -521,8 +519,8 @@ static int remove_marked_symlinks_fd(                          unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, p, NULL); -                        /* Now, remember the full path (but with the root prefix removed) of the symlink we just -                         * removed, and remove any symlinks to it, too */ +                        /* Now, remember the full path (but with the root prefix removed) of +                         * the symlink we just removed, and remove any symlinks to it, too. */                          rp = skip_root(lp, p);                          q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: p); @@ -1392,7 +1390,6 @@ static int install_info_symlink_wants(                  const char *config_path,                  char **list,                  const char *suffix, -                bool force,                  UnitFileChange **changes,                  unsigned *n_changes) { @@ -1440,7 +1437,7 @@ static int install_info_symlink_wants(                  rp = skip_root(paths, i->path); -                q = create_symlink(rp ?: i->path, path, force, changes, n_changes); +                q = create_symlink(rp ?: i->path, path, true, changes, n_changes);                  if (r == 0)                          r = q;          } @@ -1499,11 +1496,11 @@ static int install_info_apply(          r = install_info_symlink_alias(i, paths, config_path, force, changes, n_changes); -        q = install_info_symlink_wants(i, paths, config_path, i->wanted_by, ".wants/", force, changes, n_changes); +        q = install_info_symlink_wants(i, paths, config_path, i->wanted_by, ".wants/", changes, n_changes);          if (r == 0)                  r = q; -        q = install_info_symlink_wants(i, paths, config_path, i->required_by, ".requires/", force, changes, n_changes); +        q = install_info_symlink_wants(i, paths, config_path, i->required_by, ".requires/", changes, n_changes);          if (r == 0)                  r = q; @@ -1864,7 +1861,7 @@ int unit_file_revert(           * c) if there's a vendor unit file (i.e. one in /usr) we remove any configured overriding unit files (i.e. in           *    "config", but not in "transient" or "control" or even "generated").           * -         * We remove all that in both the runtime and the persistant directories, if that applies. +         * We remove all that in both the runtime and the persistent directories, if that applies.           */          r = lookup_paths_init(&paths, scope, 0, root_dir); diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index e2d2931c51..9351b85eed 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -287,7 +287,10 @@ static int output_short(                  if (r < 0)                          return r;          } - +        if (r == -EBADMSG) { +                log_debug_errno(r, "Skipping message we can't read: %m"); +                return 0; +        }          if (r < 0)                  return log_error_errno(r, "Failed to get journal fields: %m"); @@ -344,16 +347,22 @@ static int output_short(                  t = (time_t) (x / USEC_PER_SEC); -                switch(mode) { +                switch (mode) { + +                case OUTPUT_SHORT_UNIX: +                        r = snprintf(buf, sizeof(buf), "%10llu.%06llu", (unsigned long long) t, (unsigned long long) (x % USEC_PER_SEC)); +                        break; +                  case OUTPUT_SHORT_ISO:                          r = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", gettime_r(&t, &tm));                          break; +                  case OUTPUT_SHORT_PRECISE:                          r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm));                          if (r > 0) -                                snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), -                                         ".%06llu", (unsigned long long) (x % USEC_PER_SEC)); +                                snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ".%06llu", (unsigned long long) (x % USEC_PER_SEC));                          break; +                  default:                          r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm));                  } @@ -367,6 +376,12 @@ static int output_short(                  n += strlen(buf);          } +        if (hostname && (flags & OUTPUT_NO_HOSTNAME)) { +                /* Suppress display of the hostname if this is requested. */ +                hostname = NULL; +                hostname_len = 0; +        } +          if (hostname && shall_print(hostname, hostname_len, flags)) {                  fprintf(f, " %.*s", (int) hostname_len, hostname);                  n += hostname_len + 1; @@ -894,6 +909,7 @@ static int (*output_funcs[_OUTPUT_MODE_MAX])(          [OUTPUT_SHORT_ISO] = output_short,          [OUTPUT_SHORT_PRECISE] = output_short,          [OUTPUT_SHORT_MONOTONIC] = output_short, +        [OUTPUT_SHORT_UNIX] = output_short,          [OUTPUT_VERBOSE] = output_verbose,          [OUTPUT_EXPORT] = output_export,          [OUTPUT_JSON] = output_json, @@ -1040,8 +1056,8 @@ static int show_journal(FILE *f,  }  int add_matches_for_unit(sd_journal *j, const char *unit) { +        const char *m1, *m2, *m3, *m4;          int r; -        char *m1, *m2, *m3, *m4;          assert(j);          assert(unit); @@ -1073,7 +1089,9 @@ int add_matches_for_unit(sd_journal *j, const char *unit) {          );          if (r == 0 && endswith(unit, ".slice")) { -                char *m5 = strappend("_SYSTEMD_SLICE=", unit); +                const char *m5; + +                m5 = strjoina("_SYSTEMD_SLICE=", unit);                  /* Show all messages belonging to a slice */                  (void)( @@ -1123,7 +1141,9 @@ int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) {          );          if (r == 0 && endswith(unit, ".slice")) { -                char *m5 = strappend("_SYSTEMD_SLICE=", unit); +                const char *m5; + +                m5 = strjoina("_SYSTEMD_SLICE=", unit);                  /* Show all messages belonging to a slice */                  (void)( @@ -1288,18 +1308,3 @@ int show_journal_by_unit(          return show_journal(f, j, mode, n_columns, not_before, how_many, flags, ellipsized);  } - -static const char *const output_mode_table[_OUTPUT_MODE_MAX] = { -        [OUTPUT_SHORT] = "short", -        [OUTPUT_SHORT_ISO] = "short-iso", -        [OUTPUT_SHORT_PRECISE] = "short-precise", -        [OUTPUT_SHORT_MONOTONIC] = "short-monotonic", -        [OUTPUT_VERBOSE] = "verbose", -        [OUTPUT_EXPORT] = "export", -        [OUTPUT_JSON] = "json", -        [OUTPUT_JSON_PRETTY] = "json-pretty", -        [OUTPUT_JSON_SSE] = "json-sse", -        [OUTPUT_CAT] = "cat" -}; - -DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode); diff --git a/src/shared/logs-show.h b/src/shared/logs-show.h index 9765a24ff2..6643440881 100644 --- a/src/shared/logs-show.h +++ b/src/shared/logs-show.h @@ -68,6 +68,3 @@ void json_escape(                  const char* p,                  size_t l,                  OutputFlags flags); - -const char* output_mode_to_string(OutputMode m) _const_; -OutputMode output_mode_from_string(const char *s) _pure_; diff --git a/src/shared/output-mode.c b/src/shared/output-mode.c new file mode 100644 index 0000000000..bec53ee0ae --- /dev/null +++ b/src/shared/output-mode.c @@ -0,0 +1,37 @@ +/*** +  This file is part of systemd. + +  Copyright 2012 Lennart Poettering + +  systemd is free software; you can redistribute it and/or modify it +  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 +  Lesser General Public License for more details. + +  You should have received a copy of the GNU Lesser General Public License +  along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "output-mode.h" +#include "string-table.h" + +static const char *const output_mode_table[_OUTPUT_MODE_MAX] = { +        [OUTPUT_SHORT] = "short", +        [OUTPUT_SHORT_ISO] = "short-iso", +        [OUTPUT_SHORT_PRECISE] = "short-precise", +        [OUTPUT_SHORT_MONOTONIC] = "short-monotonic", +        [OUTPUT_SHORT_UNIX] = "short-unix", +        [OUTPUT_VERBOSE] = "verbose", +        [OUTPUT_EXPORT] = "export", +        [OUTPUT_JSON] = "json", +        [OUTPUT_JSON_PRETTY] = "json-pretty", +        [OUTPUT_JSON_SSE] = "json-sse", +        [OUTPUT_CAT] = "cat" +}; + +DEFINE_STRING_TABLE_LOOKUP(output_mode, OutputMode); diff --git a/src/shared/output-mode.h b/src/shared/output-mode.h index c5470e7c1b..f37189e57f 100644 --- a/src/shared/output-mode.h +++ b/src/shared/output-mode.h @@ -19,11 +19,14 @@    along with systemd; If not, see <http://www.gnu.org/licenses/>.  ***/ +#include "macro.h" +  typedef enum OutputMode {          OUTPUT_SHORT,          OUTPUT_SHORT_ISO,          OUTPUT_SHORT_PRECISE,          OUTPUT_SHORT_MONOTONIC, +        OUTPUT_SHORT_UNIX,          OUTPUT_VERBOSE,          OUTPUT_EXPORT,          OUTPUT_JSON, @@ -34,6 +37,9 @@ typedef enum OutputMode {          _OUTPUT_MODE_INVALID = -1  } OutputMode; +/* The output flags definitions are shared by the logs and process tree output. Some apply to both, some only to the + * logs output, others only to the process tree output. */ +  typedef enum OutputFlags {          OUTPUT_SHOW_ALL       = 1 << 0,          OUTPUT_FOLLOW         = 1 << 1, @@ -43,4 +49,9 @@ typedef enum OutputFlags {          OUTPUT_CATALOG        = 1 << 5,          OUTPUT_BEGIN_NEWLINE  = 1 << 6,          OUTPUT_UTC            = 1 << 7, +        OUTPUT_KERNEL_THREADS = 1 << 8, +        OUTPUT_NO_HOSTNAME    = 1 << 9,  } OutputFlags; + +const char* output_mode_to_string(OutputMode m) _const_; +OutputMode output_mode_from_string(const char *s) _pure_; diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c index 80a2ea7940..ca593b6963 100644 --- a/src/shared/path-lookup.c +++ b/src/shared/path-lookup.c @@ -586,9 +586,16 @@ int lookup_paths_init(                  if (!add)                          return -ENOMEM; -                r = strv_extend_strv(&paths, add, true); -                if (r < 0) +                if (paths) { +                        r = strv_extend_strv(&paths, add, true); +                        if (r < 0)                                  return r; +                } else { +                        /* Small optimization: if paths is NULL (and it usually is), we can simply assign 'add' to it, +                         * and don't have to copy anything */ +                        paths = add; +                        add = NULL; +                }          }          r = patch_root_prefix(&persistent_config, root); | 
