diff options
author | Martin Pitt <martin.pitt@ubuntu.com> | 2014-12-12 19:54:51 +0200 |
---|---|---|
committer | Martin Pitt <martin.pitt@ubuntu.com> | 2014-12-12 19:54:51 +0200 |
commit | f47781d88ca6bf69d6b1dd0703b2b283482e5c09 (patch) | |
tree | 6a8f1f74e8fba0a2c20d710848d00b0338f6e341 /src | |
parent | 5eef597e931b0428bb984dc2bf0736d032a9198c (diff) | |
download | systemd-f47781d88ca6bf69d6b1dd0703b2b283482e5c09.tar.gz |
Imported Upstream version 218
Diffstat (limited to 'src')
475 files changed, 22865 insertions, 11328 deletions
diff --git a/src/ac-power/ac-power.c b/src/ac-power/ac-power.c index bd1b6ecc72..2f25734612 100644 --- a/src/ac-power/ac-power.c +++ b/src/ac-power/ac-power.c @@ -29,7 +29,7 @@ int main(int argc, char *argv[]) { r = on_ac_power(); if (r < 0) { - log_error("Failed to read AC status: %s", strerror(-r)); + log_error_errno(r, "Failed to read AC status: %m"); return EXIT_FAILURE; } diff --git a/src/activate/activate.c b/src/activate/activate.c index 0a1df37f35..2689934c40 100644 --- a/src/activate/activate.c +++ b/src/activate/activate.c @@ -51,10 +51,8 @@ static int add_epoll(int epoll_fd, int fd) { ev.data.fd = fd; r = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev); - if (r < 0) { - log_error("Failed to add event on epoll fd:%d for fd:%d: %m", epoll_fd, fd); - return -errno; - } + if (r < 0) + return log_error_errno(errno, "Failed to add event on epoll fd:%d for fd:%d: %m", epoll_fd, fd); return 0; } @@ -65,11 +63,8 @@ static int open_sockets(int *epoll_fd, bool accept) { int count = 0; n = sd_listen_fds(true); - if (n < 0) { - log_error("Failed to read listening file descriptors from environment: %s", - strerror(-n)); - return n; - } + if (n < 0) + return log_error_errno(n, "Failed to read listening file descriptors from environment: %m"); if (n > 0) { log_info("Received %i descriptors via the environment.", n); @@ -103,8 +98,7 @@ static int open_sockets(int *epoll_fd, bool accept) { fd = make_socket_fd(LOG_DEBUG, *address, SOCK_STREAM | (arg_accept*SOCK_CLOEXEC)); if (fd < 0) { log_open(); - log_error("Failed to open '%s': %s", *address, strerror(-fd)); - return fd; + return log_error_errno(fd, "Failed to open '%s': %m", *address); } assert(fd == SD_LISTEN_FDS_START + count); @@ -115,10 +109,8 @@ static int open_sockets(int *epoll_fd, bool accept) { log_open(); *epoll_fd = epoll_create1(EPOLL_CLOEXEC); - if (*epoll_fd < 0) { - log_error("Failed to create epoll object: %m"); - return -errno; - } + if (*epoll_fd < 0) + return log_error_errno(errno, "Failed to create epoll object: %m"); for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) { _cleanup_free_ char *name = NULL; @@ -179,7 +171,7 @@ static int launch(char* name, char **argv, char **env, int fds) { log_info("Execing %s (%s)", name, tmp); execvpe(name, argv, envp); - log_error("Failed to execp %s (%s): %m", name, tmp); + log_error_errno(errno, "Failed to execp %s (%s): %m", name, tmp); return -errno; } @@ -196,28 +188,26 @@ static int launch1(const char* child, char** argv, char **env, int fd) { parent_pid = getpid(); child_pid = fork(); - if (child_pid < 0) { - log_error("Failed to fork: %m"); - return -errno; - } + if (child_pid < 0) + return log_error_errno(errno, "Failed to fork: %m"); /* In the child */ if (child_pid == 0) { r = dup2(fd, STDIN_FILENO); if (r < 0) { - log_error("Failed to dup connection to stdin: %m"); + log_error_errno(errno, "Failed to dup connection to stdin: %m"); _exit(EXIT_FAILURE); } r = dup2(fd, STDOUT_FILENO); if (r < 0) { - log_error("Failed to dup connection to stdout: %m"); + log_error_errno(errno, "Failed to dup connection to stdout: %m"); _exit(EXIT_FAILURE); } r = close(fd); if (r < 0) { - log_error("Failed to close dupped connection: %m"); + log_error_errno(errno, "Failed to close dupped connection: %m"); _exit(EXIT_FAILURE); } @@ -231,7 +221,7 @@ static int launch1(const char* child, char** argv, char **env, int fd) { _exit(EXIT_SUCCESS); execvp(child, argv); - log_error("Failed to exec child %s: %m", child); + log_error_errno(errno, "Failed to exec child %s: %m", child); _exit(EXIT_FAILURE); } @@ -246,7 +236,7 @@ static int do_accept(const char* name, char **argv, char **envp, int fd) { fd2 = accept(fd, NULL, NULL); if (fd2 < 0) { - log_error("Failed to accept connection on fd:%d: %m", fd); + log_error_errno(errno, "Failed to accept connection on fd:%d: %m", fd); return fd2; } @@ -275,7 +265,7 @@ static int install_chld_handler(void) { r = sigaction(SIGCHLD, &act, 0); if (r < 0) - log_error("Failed to install SIGCHLD handler: %m"); + log_error_errno(errno, "Failed to install SIGCHLD handler: %m"); return r; } @@ -393,7 +383,7 @@ int main(int argc, char **argv, char **envp) { if (errno == EINTR) continue; - log_error("epoll_wait() failed: %m"); + log_error_errno(errno, "epoll_wait() failed: %m"); return EXIT_FAILURE; } diff --git a/src/analyze/analyze-verify.c b/src/analyze/analyze-verify.c index 5b16b6c3d9..f569109268 100644 --- a/src/analyze/analyze-verify.c +++ b/src/analyze/analyze-verify.c @@ -74,7 +74,7 @@ static int verify_socket(Unit *u) { /* This makes sure instance is created if necessary. */ r = socket_instantiate_service(SOCKET(u)); if (r < 0) { - log_error_unit(u->id, "Socket %s cannot be started, failed to create instance.", + log_unit_error(u->id, "Socket %s cannot be started, failed to create instance.", u->id); return r; } @@ -84,10 +84,10 @@ static int verify_socket(Unit *u) { Service *service; service = SERVICE(UNIT_DEREF(SOCKET(u)->service)); - log_debug_unit(u->id, "%s uses %s", u->id, UNIT(service)->id); + log_unit_debug(u->id, "%s uses %s", u->id, UNIT(service)->id); if (UNIT(service)->load_state != UNIT_LOADED) { - log_error_unit(u->id, "Service %s not loaded, %s cannot be started.", + log_unit_error(u->id, "Service %s not loaded, %s cannot be started.", UNIT(service)->id, u->id); return -ENOENT; } @@ -101,7 +101,7 @@ static int verify_executable(Unit *u, ExecCommand *exec) { return 0; if (access(exec->path, X_OK) < 0) { - log_error_unit(u->id, "%s: command %s is not executable: %m", + log_unit_error(u->id, "%s: command %s is not executable: %m", u->id, exec->path); return -errno; } @@ -145,15 +145,15 @@ static int verify_documentation(Unit *u, bool check_man) { int r = 0, k; STRV_FOREACH(p, u->documentation) { - log_debug_unit(u->id, "%s: found documentation item %s.", u->id, *p); + log_unit_debug(u->id, "%s: found documentation item %s.", u->id, *p); if (check_man && startswith(*p, "man:")) { k = show_man_page(*p + 4, true); if (k != 0) { if (k < 0) - log_error_unit(u->id, "%s: can't show %s: %s", + log_unit_error(u->id, "%s: can't show %s: %s", u->id, *p, strerror(-r)); else { - log_error_unit(u->id, "%s: man %s command failed with code %d", + log_unit_error(u->id, "%s: man %s command failed with code %d", u->id, *p + 4, k); k = -ENOEXEC; } @@ -178,13 +178,13 @@ static int verify_unit(Unit *u, bool check_man) { if (log_get_max_level() >= LOG_DEBUG) unit_dump(u, stdout, "\t"); - log_debug_unit(u->id, "Creating %s/start job", u->id); + log_unit_debug(u->id, "Creating %s/start job", u->id); r = manager_add_job(u->manager, JOB_START, u, JOB_REPLACE, false, &err, &j); if (sd_bus_error_is_set(&err)) - log_error_unit(u->id, "Error: %s: %s", + log_unit_error(u->id, "Error: %s: %s", err.name, err.message); if (r < 0) - log_error_unit(u->id, "Failed to create %s/start: %s", + log_unit_error(u->id, "Failed to create %s/start: %s", u->id, strerror(-r)); k = verify_socket(u); @@ -221,24 +221,20 @@ int verify_units(char **filenames, SystemdRunningAs running_as, bool check_man) /* set the path */ r = generate_path(&var, filenames); - if (r < 0) { - log_error("Failed to generate unit load path: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to generate unit load path: %m"); assert_se(set_unit_path(var) >= 0); r = manager_new(running_as, true, &m); - if (r < 0) { - log_error("Failed to initalize manager: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to initalize manager: %m"); log_debug("Starting manager..."); r = manager_startup(m, serial, fdset); if (r < 0) { - log_error("Failed to start manager: %s", strerror(-r)); + log_error_errno(r, "Failed to start manager: %m"); goto finish; } @@ -260,7 +256,7 @@ int verify_units(char **filenames, SystemdRunningAs running_as, bool check_man) } else k = manager_load_unit(m, NULL, *filename, &err, &units[count]); if (k < 0) { - log_error("Failed to load %s: %s", *filename, strerror(-k)); + log_error_errno(k, "Failed to load %s: %m", *filename); if (r == 0) r = k; } else diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index 5e55988063..9a5fd741c7 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -1362,7 +1362,7 @@ int main(int argc, char *argv[]) { r = bus_open_transport_systemd(arg_transport, arg_host, arg_user, &bus); if (r < 0) { - log_error("Failed to create bus connection: %s", strerror(-r)); + log_error_errno(r, "Failed to create bus connection: %m"); goto finish; } diff --git a/src/backlight/backlight.c b/src/backlight/backlight.c index 0a2bac6f16..1271a66983 100644 --- a/src/backlight/backlight.c +++ b/src/backlight/backlight.c @@ -211,7 +211,7 @@ static unsigned get_max_brightness(struct udev_device *device) { r = safe_atou(max_brightness_str, &max_brightness); if (r < 0) { - log_warning("Failed to parse 'max_brightness' \"%s\": %s", max_brightness_str, strerror(-r)); + log_warning_errno(r, "Failed to parse 'max_brightness' \"%s\": %m", max_brightness_str); return 0; } @@ -235,7 +235,7 @@ static void clamp_brightness(struct udev_device *device, char **value, unsigned r = safe_atou(*value, &brightness); if (r < 0) { - log_warning("Failed to parse brightness \"%s\": %s", *value, strerror(-r)); + log_warning_errno(r, "Failed to parse brightness \"%s\": %m", *value); return; } @@ -285,8 +285,7 @@ int main(int argc, char *argv[]) { r = mkdir_p("/var/lib/systemd/backlight", 0755); if (r < 0) { - log_error("Failed to create backlight directory /var/lib/systemd/backlight: %s", - strerror(-r)); + log_error_errno(r, "Failed to create backlight directory /var/lib/systemd/backlight: %m"); return EXIT_FAILURE; } @@ -319,7 +318,7 @@ int main(int argc, char *argv[]) { device = udev_device_new_from_subsystem_sysname(udev, ss, sysname); if (!device) { if (errno != 0) - log_error("Failed to get backlight or LED device '%s:%s': %m", ss, sysname); + log_error_errno(errno, "Failed to get backlight or LED device '%s:%s': %m", ss, sysname); else log_oom(); @@ -387,7 +386,7 @@ int main(int argc, char *argv[]) { if (r == -ENOENT) return EXIT_SUCCESS; - log_error("Failed to read %s: %s", saved, strerror(-r)); + log_error_errno(r, "Failed to read %s: %m", saved); return EXIT_FAILURE; } @@ -395,8 +394,7 @@ int main(int argc, char *argv[]) { r = udev_device_set_sysattr_value(device, "brightness", value); if (r < 0) { - log_error("Failed to write system 'brightness' attribute: %s", - strerror(-r)); + log_error_errno(r, "Failed to write system 'brightness' attribute: %m"); return EXIT_FAILURE; } @@ -416,7 +414,7 @@ int main(int argc, char *argv[]) { r = write_string_file(saved, value); if (r < 0) { - log_error("Failed to write %s: %s", saved, strerror(-r)); + log_error_errno(r, "Failed to write %s: %m", saved); return EXIT_FAILURE; } diff --git a/src/binfmt/binfmt.c b/src/binfmt/binfmt.c index c1c152239b..089b7754a4 100644 --- a/src/binfmt/binfmt.c +++ b/src/binfmt/binfmt.c @@ -36,15 +36,7 @@ #include "fileio.h" #include "build.h" -static const char conf_file_dirs[] = - "/etc/binfmt.d\0" - "/run/binfmt.d\0" - "/usr/local/lib/binfmt.d\0" - "/usr/lib/binfmt.d\0" -#ifdef HAVE_SPLIT_USR - "/lib/binfmt.d\0" -#endif - ; +static const char conf_file_dirs[] = CONF_DIRS_NULSTR("binfmt"); static int delete_rule(const char *rule) { _cleanup_free_ char *x = NULL, *fn = NULL; @@ -72,10 +64,8 @@ static int apply_rule(const char *rule) { delete_rule(rule); r = write_string_file("/proc/sys/fs/binfmt_misc/register", rule); - if (r < 0) { - log_error("Failed to add binary format: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add binary format: %m"); return 0; } @@ -91,8 +81,7 @@ static int apply_file(const char *path, bool ignore_enoent) { if (ignore_enoent && r == -ENOENT) return 0; - log_error("Failed to open file '%s', ignoring: %s", path, strerror(-r)); - return r; + return log_error_errno(r, "Failed to open file '%s', ignoring: %m", path); } log_debug("apply: %s", path); @@ -104,7 +93,7 @@ static int apply_file(const char *path, bool ignore_enoent) { if (feof(f)) break; - log_error("Failed to read file '%s', ignoring: %m", path); + log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path); return -errno; } @@ -199,7 +188,7 @@ int main(int argc, char *argv[]) { r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); if (r < 0) { - log_error("Failed to enumerate binfmt.d files: %s", strerror(-r)); + log_error_errno(r, "Failed to enumerate binfmt.d files: %m"); goto finish; } diff --git a/src/bootchart/bootchart.c b/src/bootchart/bootchart.c index 813e38deeb..0808ba44ea 100644 --- a/src/bootchart/bootchart.c +++ b/src/bootchart/bootchart.c @@ -125,10 +125,9 @@ static void parse_conf(void) { { NULL, NULL, NULL, 0, NULL } }; - config_parse(NULL, BOOTCHART_CONF, NULL, - NULL, - config_item_table_lookup, items, - true, false, true, NULL); + config_parse_many(BOOTCHART_CONF, + CONF_DIRS_NULSTR("systemd/bootchart.conf"), + NULL, config_item_table_lookup, items, true, NULL); if (init != NULL) strscpy(arg_init_path, sizeof(arg_init_path), init); @@ -194,8 +193,8 @@ static int parse_argv(int argc, char *argv[]) { case 'f': r = safe_atod(optarg, &arg_hz); if (r < 0) - log_warning("failed to parse --freq/-f argument '%s': %s", - optarg, strerror(-r)); + log_warning_errno(r, "failed to parse --freq/-f argument '%s': %m", + optarg); break; case 'F': arg_filter = false; @@ -209,8 +208,8 @@ static int parse_argv(int argc, char *argv[]) { case 'n': r = safe_atoi(optarg, &arg_samples_len); if (r < 0) - log_warning("failed to parse --samples/-n argument '%s': %s", - optarg, strerror(-r)); + log_warning_errno(r, "failed to parse --samples/-n argument '%s': %m", + optarg); break; case 'o': path_kill_slashes(optarg); @@ -226,14 +225,14 @@ static int parse_argv(int argc, char *argv[]) { case 'x': r = safe_atod(optarg, &arg_scale_x); if (r < 0) - log_warning("failed to parse --scale-x/-x argument '%s': %s", - optarg, strerror(-r)); + log_warning_errno(r, "failed to parse --scale-x/-x argument '%s': %m", + optarg); break; case 'y': r = safe_atod(optarg, &arg_scale_y); if (r < 0) - log_warning("failed to parse --scale-y/-y argument '%s': %s", - optarg, strerror(-r)); + log_warning_errno(r, "failed to parse --scale-y/-y argument '%s': %m", + optarg); break; case 'e': arg_entropy = true; @@ -285,12 +284,12 @@ static void do_journal_append(char *file) { f = open(file, O_RDONLY|O_CLOEXEC); if (f < 0) { - log_error("Failed to read bootchart data: %m"); + log_error_errno(errno, "Failed to read bootchart data: %m"); return; } n = loop_read(f, p + 10, BOOTCHART_MAX, false); if (n < 0) { - log_error("Failed to read bootchart data: %s", strerror(-n)); + log_error_errno(n, "Failed to read bootchart data: %m"); close(f); return; } @@ -302,7 +301,7 @@ static void do_journal_append(char *file) { r = sd_journal_sendv(iovec, j); if (r < 0) - log_error("Failed to send bootchart: %s", strerror(-r)); + log_error_errno(r, "Failed to send bootchart: %m"); } int main(int argc, char *argv[]) { @@ -434,7 +433,7 @@ int main(int argc, char *argv[]) { /* caught signal, probably HUP! */ break; } - log_error("nanosleep() failed: %m"); + log_error_errno(errno, "nanosleep() failed: %m"); exit(EXIT_FAILURE); } } else { diff --git a/src/bootchart/bootchart.conf b/src/bootchart/bootchart.conf index d7e0dabe09..c73328fde2 100644 --- a/src/bootchart/bootchart.conf +++ b/src/bootchart/bootchart.conf @@ -5,6 +5,9 @@ # the Free Software Foundation; either version 2.1 of the License, or # (at your option) any later version. # +# You can override the directives in this file by creating files in +# /etc/systemd/bootchart.conf.d/*.conf. +# # See bootchart.conf(5) for details [Bootchart] diff --git a/src/bootchart/store.c b/src/bootchart/store.c index 9ea1b27de4..a66387c304 100644 --- a/src/bootchart/store.c +++ b/src/bootchart/store.c @@ -146,7 +146,7 @@ void log_sample(int sample, struct list_sample_data **ptr) { /* block stuff */ vmstat = openat(procfd, "vmstat", O_RDONLY); if (vmstat == -1) { - log_error("Failed to open /proc/vmstat: %m"); + log_error_errno(errno, "Failed to open /proc/vmstat: %m"); exit(EXIT_FAILURE); } } @@ -178,7 +178,7 @@ vmstat_next: /* overall CPU utilization */ schedstat = openat(procfd, "schedstat", O_RDONLY); if (schedstat == -1) { - log_error("Failed to open /proc/schedstat: %m"); + log_error_errno(errno, "Failed to open /proc/schedstat: %m"); exit(EXIT_FAILURE); } } diff --git a/src/bootchart/svg.c b/src/bootchart/svg.c index faf377e506..e5569e1622 100644 --- a/src/bootchart/svg.c +++ b/src/bootchart/svg.c @@ -39,6 +39,7 @@ #include "svg.h" #include "bootchart.h" #include "list.h" +#include "utf8.h" #define time_to_graph(t) ((t) * arg_scale_x) #define ps_to_graph(n) ((n) * arg_scale_y) @@ -1006,12 +1007,15 @@ static void svg_ps_bars(void) { /* pass 2 - ps boxes */ ps = ps_first; while ((ps = get_next_ps(ps))) { - _cleanup_free_ char *enc_name = NULL; + _cleanup_free_ char *enc_name = NULL, *escaped = NULL; double endtime; double starttime; int t; - enc_name = xml_comment_encode(ps->name); + if (!utf8_is_printable(ps->name, strlen(ps->name))) + escaped = utf8_escape_non_printable(ps->name); + + enc_name = xml_comment_encode(escaped ? escaped : ps->name); if (!enc_name) continue; @@ -1100,7 +1104,7 @@ static void svg_ps_bars(void) { svg(" <text x=\"%.03f\" y=\"%.03f\"><![CDATA[%s]]> [%i]<tspan class=\"run\">%.03fs</tspan> %s</text>\n", time_to_graph(w - graph_start) + 5.0, ps_to_graph(j) + 14.0, - ps->name, + escaped ? escaped : ps->name, ps->pid, (ps->last->runtime - ps->first->runtime) / 1000000000.0, arg_show_cgroup ? ps->cgroup : ""); diff --git a/src/bus-proxyd/bus-policy.c b/src/bus-proxyd/bus-policy.c index 625f5ddaee..59cc1d788b 100644 --- a/src/bus-proxyd/bus-policy.c +++ b/src/bus-proxyd/bus-policy.c @@ -95,8 +95,7 @@ static int file_load(Policy *p, const char *path) { if (r == -EISDIR) return r; - log_error("Failed to load %s: %s", path, strerror(-r)); - return r; + return log_error_errno(r, "Failed to load %s: %m", path); } q = c; @@ -105,10 +104,8 @@ static int file_load(Policy *p, const char *path) { int t; t = xml_tokenize(&q, &name, &xml_state, &line); - if (t < 0) { - log_error("XML parse failure in %s: %s", path, strerror(-t)); - return t; - } + if (t < 0) + return log_error_errno(t, "XML parse failure in %s: %m", path); switch (state) { @@ -358,7 +355,7 @@ static int file_load(Policy *p, const char *path) { r = get_user_creds(&u, &i->uid, NULL, NULL, NULL); if (r < 0) { - log_error("Failed to resolve user %s, ignoring policy: %s", u, strerror(-r)); + log_error_errno(r, "Failed to resolve user %s, ignoring policy: %m", u); free(i); } else { PolicyItem *first; @@ -390,7 +387,7 @@ static int file_load(Policy *p, const char *path) { r = get_group_creds(&g, &i->gid); if (r < 0) { - log_error("Failed to resolve group %s, ignoring policy: %s", g, strerror(-r)); + log_error_errno(r, "Failed to resolve group %s, ignoring policy: %m", g); free(i); } else { PolicyItem *first; @@ -533,7 +530,7 @@ static int file_load(Policy *p, const char *path) { r = get_user_creds(&u, &i->uid, NULL, NULL, NULL); if (r < 0) - log_error("Failed to resolve user %s: %s", name, strerror(-r)); + log_error_errno(r, "Failed to resolve user %s: %m", name); else i->uid_valid = true; } @@ -544,7 +541,7 @@ static int file_load(Policy *p, const char *path) { r = get_group_creds(&g, &i->gid); if (r < 0) - log_error("Failed to resolve group %s: %s", name, strerror(-r)); + log_error_errno(r, "Failed to resolve group %s: %m", name); else i->gid_valid = true; } @@ -593,14 +590,29 @@ static int file_load(Policy *p, const char *path) { } enum { + DENY, ALLOW, DUNNO, - DENY, }; +static const char *verdict_to_string(int v) { + switch (v) { + + case DENY: + return "DENY"; + case ALLOW: + return "ALLOW"; + case DUNNO: + return "DUNNO"; + } + + return NULL; +} + struct policy_check_filter { - int class; - const struct ucred *ucred; + PolicyItemClass class; + uid_t uid; + gid_t gid; int message_type; const char *name; const char *interface; @@ -627,7 +639,7 @@ static int check_policy_item(PolicyItem *i, const struct policy_check_filter *fi if (i->name && !streq_ptr(i->name, filter->name)) break; - if ((i->message_type != _POLICY_ITEM_CLASS_UNSET) && (i->message_type != filter->message_type)) + if ((i->message_type != 0) && (i->message_type != filter->message_type)) break; if (i->path && !streq_ptr(i->path, filter->path)) @@ -651,22 +663,20 @@ static int check_policy_item(PolicyItem *i, const struct policy_check_filter *fi case POLICY_ITEM_OWN_PREFIX: assert(filter->name); - if (streq(i->name, "*") || startswith(i->name, filter->name)) + if (streq(i->name, "*") || service_name_startswith(filter->name, i->name)) return is_permissive(i); break; case POLICY_ITEM_USER: - assert(filter->ucred); - - if ((streq_ptr(i->name, "*") || (i->uid_valid && i->uid == filter->ucred->uid))) - return is_permissive(i); + if (filter->uid != UID_INVALID) + if ((streq_ptr(i->name, "*") || (i->uid_valid && i->uid == filter->uid))) + return is_permissive(i); break; case POLICY_ITEM_GROUP: - assert(filter->ucred); - - if ((streq_ptr(i->name, "*") || (i->gid_valid && i->gid == filter->ucred->gid))) - return is_permissive(i); + if (filter->gid != GID_INVALID) + if ((streq_ptr(i->name, "*") || (i->gid_valid && i->gid == filter->gid))) + return is_permissive(i); break; case POLICY_ITEM_IGNORE: @@ -680,97 +690,130 @@ static int check_policy_item(PolicyItem *i, const struct policy_check_filter *fi static int check_policy_items(PolicyItem *items, const struct policy_check_filter *filter) { PolicyItem *i; - int r, ret = DUNNO; + int verdict = DUNNO; assert(filter); /* Check all policies in a set - a broader one might be followed by a more specific one, * and the order of rules in policy definitions matters */ LIST_FOREACH(items, i, items) { - if (i->class != filter->class) + int v; + + if (i->class != filter->class && + !(i->class == POLICY_ITEM_OWN_PREFIX && filter->class == POLICY_ITEM_OWN)) continue; - r = check_policy_item(i, filter); - if (r != DUNNO) - ret = r; + v = check_policy_item(i, filter); + if (v != DUNNO) + verdict = v; } - return ret; + return verdict; } static int policy_check(Policy *p, const struct policy_check_filter *filter) { PolicyItem *items; - int r; + int verdict, v; assert(p); assert(filter); + assert(IN_SET(filter->class, POLICY_ITEM_SEND, POLICY_ITEM_RECV, POLICY_ITEM_OWN, POLICY_ITEM_USER, POLICY_ITEM_GROUP)); + /* * The policy check is implemented by the following logic: * - * 1. Check mandatory items. If the message matches any of these, it is decisive. - * 2. See if the passed ucred match against the user/group hashmaps. A matching entry is also decisive. - * 3. Consult the defaults if non of the above matched with a more specific rule. - * 4. If the message isn't caught be the defaults either, reject it. + * 1. Check default items + * 2. Check group items + * 3. Check user items + * 4. Check mandatory items + * + * Later rules override earlier rules. */ - r = check_policy_items(p->mandatory_items, filter); - if (r != DUNNO) - return r; + verdict = check_policy_items(p->default_items, filter); - if (filter->ucred) { - items = hashmap_get(p->user_items, UINT32_TO_PTR(filter->ucred->uid)); + if (filter->gid != GID_INVALID) { + items = hashmap_get(p->group_items, UINT32_TO_PTR(filter->gid)); if (items) { - r = check_policy_items(items, filter); - if (r != DUNNO) - return r; + v = check_policy_items(items, filter); + if (v != DUNNO) + verdict = v; } + } - items = hashmap_get(p->group_items, UINT32_TO_PTR(filter->ucred->gid)); + if (filter->uid != UID_INVALID) { + items = hashmap_get(p->user_items, UINT32_TO_PTR(filter->uid)); if (items) { - r = check_policy_items(items, filter); - if (r != DUNNO) - return r; + v = check_policy_items(items, filter); + if (v != DUNNO) + verdict = v; } } - return check_policy_items(p->default_items, filter); + v = check_policy_items(p->mandatory_items, filter); + if (v != DUNNO) + verdict = v; + + return verdict; } -bool policy_check_own(Policy *p, const struct ucred *ucred, const char *name) { +bool policy_check_own(Policy *p, uid_t uid, gid_t gid, const char *name) { struct policy_check_filter filter = { .class = POLICY_ITEM_OWN, - .ucred = ucred, + .uid = uid, + .gid = gid, .name = name, }; - return policy_check(p, &filter) == ALLOW; + int verdict; + + assert(p); + assert(name); + + verdict = policy_check(p, &filter); + + log_full(LOG_AUTH | (verdict != ALLOW ? LOG_WARNING : LOG_DEBUG), + "Ownership permission check for uid=" UID_FMT " gid=" GID_FMT" name=%s: %s", + uid, gid, strna(name), strna(verdict_to_string(verdict))); + + return verdict == ALLOW; } -bool policy_check_hello(Policy *p, const struct ucred *ucred) { +bool policy_check_hello(Policy *p, uid_t uid, gid_t gid) { struct policy_check_filter filter = { - .ucred = ucred, + .uid = uid, + .gid = gid, }; - int user, group; + int verdict; + + assert(p); filter.class = POLICY_ITEM_USER; - user = policy_check(p, &filter); - if (user == DENY) - return false; + verdict = policy_check(p, &filter); - filter.class = POLICY_ITEM_GROUP; - group = policy_check(p, &filter); - if (group == DENY) - return false; + if (verdict != DENY) { + int v; - return !(user == DUNNO && group == DUNNO); + filter.class = POLICY_ITEM_GROUP; + v = policy_check(p, &filter); + if (v != DUNNO) + verdict = v; + } + + log_full(LOG_AUTH | (verdict != ALLOW ? LOG_WARNING : LOG_DEBUG), + "Hello permission check for uid=" UID_FMT " gid=" GID_FMT": %s", + uid, gid, strna(verdict_to_string(verdict))); + + return verdict == ALLOW; } bool policy_check_recv(Policy *p, - const struct ucred *ucred, + uid_t uid, + gid_t gid, int message_type, const char *name, const char *path, @@ -779,7 +822,8 @@ bool policy_check_recv(Policy *p, struct policy_check_filter filter = { .class = POLICY_ITEM_RECV, - .ucred = ucred, + .uid = uid, + .gid = gid, .message_type = message_type, .name = name, .interface = interface, @@ -787,11 +831,22 @@ bool policy_check_recv(Policy *p, .member = member, }; - return policy_check(p, &filter) == ALLOW; + int verdict; + + assert(p); + + verdict = policy_check(p, &filter); + + log_full(LOG_AUTH | (verdict != ALLOW ? LOG_WARNING : LOG_DEBUG), + "Recieve permission check for uid=" UID_FMT " gid=" GID_FMT" message=%s name=%s interface=%s path=%s member=%s: %s", + uid, gid, bus_message_type_to_string(message_type), strna(name), strna(path), strna(interface), strna(member), strna(verdict_to_string(verdict))); + + return verdict == ALLOW; } bool policy_check_send(Policy *p, - const struct ucred *ucred, + uid_t uid, + gid_t gid, int message_type, const char *name, const char *path, @@ -800,7 +855,8 @@ bool policy_check_send(Policy *p, struct policy_check_filter filter = { .class = POLICY_ITEM_SEND, - .ucred = ucred, + .uid = uid, + .gid = gid, .message_type = message_type, .name = name, .interface = interface, @@ -808,7 +864,17 @@ bool policy_check_send(Policy *p, .member = member, }; - return policy_check(p, &filter) == ALLOW; + int verdict; + + assert(p); + + verdict = policy_check(p, &filter); + + log_full(LOG_AUTH | (verdict != ALLOW ? LOG_WARNING : LOG_DEBUG), + "Send permission check for uid=" UID_FMT " gid=" GID_FMT" message=%s name=%s interface=%s path=%s member=%s: %s", + uid, gid, bus_message_type_to_string(message_type), strna(name), strna(path), strna(interface), strna(member), strna(verdict_to_string(verdict))); + + return verdict == ALLOW; } int policy_load(Policy *p, char **files) { @@ -825,10 +891,8 @@ int policy_load(Policy *p, char **files) { char **j; r = conf_files_list(&l, ".conf", NULL, *i, NULL); - if (r < 0) { - log_error("Failed to get configuration file list: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get configuration file list: %m"); STRV_FOREACH(j, l) file_load(p, *j); @@ -936,6 +1000,7 @@ static void dump_items(PolicyItem *items, const char *prefix) { printf("%sGroup: %s (%d)\n", prefix, strna(group), i->gid); } + printf("%s-\n", prefix); } } diff --git a/src/bus-proxyd/bus-policy.h b/src/bus-proxyd/bus-policy.h index 64fe1ffac5..933a53ceb5 100644 --- a/src/bus-proxyd/bus-policy.h +++ b/src/bus-proxyd/bus-policy.h @@ -76,17 +76,19 @@ typedef struct Policy { int policy_load(Policy *p, char **files); void policy_free(Policy *p); -bool policy_check_own(Policy *p, const struct ucred *ucred, const char *name); -bool policy_check_hello(Policy *p, const struct ucred *ucred); +bool policy_check_own(Policy *p, uid_t uid, gid_t gid, const char *name); +bool policy_check_hello(Policy *p, uid_t uid, gid_t gid); bool policy_check_recv(Policy *p, - const struct ucred *ucred, + uid_t uid, + gid_t gid, int message_type, const char *name, const char *path, const char *interface, const char *member); bool policy_check_send(Policy *p, - const struct ucred *ucred, + uid_t uid, + gid_t gid, int message_type, const char *name, const char *path, diff --git a/src/bus-proxyd/bus-proxyd.c b/src/bus-proxyd/bus-proxyd.c index 8c1b6a5ad6..5d304538fd 100644 --- a/src/bus-proxyd/bus-proxyd.c +++ b/src/bus-proxyd/bus-proxyd.c @@ -45,6 +45,8 @@ #include "def.h" #include "capability.h" #include "bus-policy.h" +#include "bus-control.h" +#include "smack-util.h" static char *arg_address = NULL; static char *arg_command_line_buffer = NULL; @@ -61,7 +63,7 @@ static int help(void) { " --configuration=PATH Configuration file or directory\n" " --machine=MACHINE Connect to specified machine\n" " --address=ADDRESS Connect to the bus specified by ADDRESS\n" - " (default: " DEFAULT_SYSTEM_BUS_PATH ")\n", + " (default: " DEFAULT_SYSTEM_BUS_ADDRESS ")\n", program_invocation_short_name); return 0; @@ -166,7 +168,7 @@ static int parse_argv(int argc, char *argv[]) { } if (!arg_address) { - arg_address = strdup(DEFAULT_SYSTEM_BUS_PATH); + arg_address = strdup(DEFAULT_SYSTEM_BUS_ADDRESS); if (!arg_address) return log_oom(); } @@ -309,48 +311,6 @@ static int synthesize_name_acquired(sd_bus *a, sd_bus *b, sd_bus_message *m) { return sd_bus_send(b, n, NULL); } -static int process_policy(sd_bus *a, sd_bus *b, sd_bus_message *m) { - _cleanup_bus_message_unref_ sd_bus_message *n = NULL; - int r; - - assert(a); - assert(b); - assert(m); - - if (!a->is_kernel) - return 0; - - if (!sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "GetAll")) - return 0; - - if (!streq_ptr(m->path, "/org/gnome/DisplayManager/Slave")) - return 0; - - r = sd_bus_message_new_method_errorf(m, &n, SD_BUS_ERROR_ACCESS_DENIED, "gdm, you are stupid"); - if (r < 0) - return r; - - r = bus_message_append_sender(n, "org.freedesktop.DBus"); - if (r < 0) { - log_error("Failed to append sender to gdm reply: %s", strerror(-r)); - return r; - } - - r = bus_seal_synthetic_message(b, n); - if (r < 0) { - log_error("Failed to seal gdm reply: %s", strerror(-r)); - return r; - } - - r = sd_bus_send(b, n, NULL); - if (r < 0) { - log_error("Failed to send gdm reply: %s", strerror(-r)); - return r; - } - - return 1; -} - static int synthetic_driver_send(sd_bus *b, sd_bus_message *m) { int r; @@ -452,8 +412,6 @@ static int get_creds_by_name(sd_bus *bus, const char *name, uint64_t mask, sd_bu assert(name); assert(_creds); - assert_return(service_name_is_valid(name), -EINVAL); - r = sd_bus_get_name_creds(bus, name, mask, &c); if (r == -ESRCH || r == -ENXIO) return sd_bus_error_setf(error, SD_BUS_ERROR_NAME_HAS_NO_OWNER, "Name %s is currently not owned by anyone.", name); @@ -484,30 +442,7 @@ static int get_creds_by_message(sd_bus *bus, sd_bus_message *m, uint64_t mask, s return get_creds_by_name(bus, name, mask, _creds, error); } -static int peer_is_privileged(sd_bus *bus, sd_bus_message *m) { - _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL; - uid_t uid; - int r; - - r = get_creds_by_message(bus, m, SD_BUS_CREDS_UID, &creds, NULL); - if (r < 0) - return r; - - r = sd_bus_creds_get_uid(creds, &uid); - if (r < 0) - return r; - - r = sd_bus_creds_has_effective_cap(creds, CAP_SYS_ADMIN); - if (r > 0) - return true; - - if (uid == getuid()) - return true; - - return false; -} - -static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m) { +static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m, Policy *policy, const struct ucred *ucred, Set *owned_names) { int r; assert(a); @@ -520,14 +455,12 @@ static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m) { if (!streq_ptr(sd_bus_message_get_destination(m), "org.freedesktop.DBus")) return 0; - if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) { - if (0 && !isempty(sd_bus_message_get_signature(m, true))) { - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + /* The "Hello()" call is is handled in process_hello() */ - r = sd_bus_error_setf(&error, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters"); + if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) { - return synthetic_reply_method_errno(m, r, &error); - } + if (!sd_bus_message_has_signature(m, "")) + return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); return synthetic_reply_method_return(m, "s", "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" " @@ -617,6 +550,9 @@ static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m) { } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "AddMatch")) { const char *match; + if (!sd_bus_message_has_signature(m, "s")) + return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); + r = sd_bus_message_read(m, "s", &match); if (r < 0) return synthetic_reply_method_errno(m, r, NULL); @@ -630,6 +566,9 @@ static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m) { } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "RemoveMatch")) { const char *match; + if (!sd_bus_message_has_signature(m, "s")) + return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); + r = sd_bus_message_read(m, "s", &match); if (r < 0) return synthetic_reply_method_errno(m, r, NULL); @@ -646,6 +585,9 @@ static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m) { _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL; _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + if (!sd_bus_message_has_signature(m, "s")) + return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); + r = get_creds_by_message(a, m, SD_BUS_CREDS_SELINUX_CONTEXT, &creds, &error); if (r < 0) return synthetic_reply_method_errno(m, r, &error); @@ -656,6 +598,9 @@ static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m) { _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL; _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + if (!sd_bus_message_has_signature(m, "s")) + return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); + r = get_creds_by_message(a, m, SD_BUS_CREDS_PID, &creds, &error); if (r < 0) return synthetic_reply_method_errno(m, r, &error); @@ -666,6 +611,9 @@ static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m) { _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL; _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + if (!sd_bus_message_has_signature(m, "s")) + return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); + r = get_creds_by_message(a, m, SD_BUS_CREDS_UID, &creds, &error); if (r < 0) return synthetic_reply_method_errno(m, r, &error); @@ -676,7 +624,10 @@ static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m) { sd_id128_t server_id; char buf[SD_ID128_STRING_MAX]; - r = sd_bus_get_server_id(a, &server_id); + if (!sd_bus_message_has_signature(m, "")) + return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); + + r = sd_bus_get_bus_id(a, &server_id); if (r < 0) return synthetic_reply_method_errno(m, r, NULL); @@ -687,6 +638,9 @@ static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m) { _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL; _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + if (!sd_bus_message_has_signature(m, "s")) + return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); + r = sd_bus_message_read(m, "s", &name); if (r < 0) return synthetic_reply_method_errno(m, r, NULL); @@ -700,11 +654,12 @@ static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m) { return synthetic_reply_method_return(m, "s", creds->unique_name); - /* "Hello" is handled in process_hello() */ - } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "ListActivatableNames")) { _cleanup_strv_free_ char **names = NULL; + if (!sd_bus_message_has_signature(m, "")) + return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); + r = sd_bus_list_names(a, NULL, &names); if (r < 0) return synthetic_reply_method_errno(m, r, NULL); @@ -717,6 +672,9 @@ static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m) { } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "ListNames")) { _cleanup_strv_free_ char **names = NULL; + if (!sd_bus_message_has_signature(m, "")) + return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); + r = sd_bus_list_names(a, &names, NULL); if (r < 0) return synthetic_reply_method_errno(m, r, NULL); @@ -733,20 +691,19 @@ static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m) { } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "ListQueuedOwners")) { struct kdbus_cmd_name_list cmd = {}; struct kdbus_name_list *name_list; - struct kdbus_cmd_free cmd_free; struct kdbus_name_info *name; _cleanup_strv_free_ char **owners = NULL; _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; char *arg0; int err = 0; + if (!sd_bus_message_has_signature(m, "s")) + return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); + r = sd_bus_message_read(m, "s", &arg0); if (r < 0) return synthetic_reply_method_errno(m, r, NULL); - if (!service_name_is_valid(arg0)) - return synthetic_reply_method_errno(m, -EINVAL, NULL); - r = sd_bus_get_name_creds(a, arg0, 0, NULL); if (r == -ESRCH || r == -ENXIO) { sd_bus_error_setf(&error, SD_BUS_ERROR_NAME_HAS_NO_OWNER, "Could not get owners of name '%s': no such name.", arg0); @@ -768,8 +725,8 @@ static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m) { char *n; KDBUS_ITEM_FOREACH(item, name, items) - if (item->type == KDBUS_ITEM_NAME) - entry_name = item->str; + if (item->type == KDBUS_ITEM_OWNED_NAME) + entry_name = item->name.name; if (!streq_ptr(entry_name, arg0)) continue; @@ -786,10 +743,7 @@ static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m) { } } - cmd_free.flags = 0; - cmd_free.offset = cmd.offset; - - r = ioctl(a->input_fd, KDBUS_CMD_FREE, &cmd_free); + r = bus_kernel_cmd_free(a, cmd.offset); if (r < 0) return synthetic_reply_method_errno(m, r, NULL); @@ -801,13 +755,13 @@ static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m) { } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "NameHasOwner")) { const char *name; + if (!sd_bus_message_has_signature(m, "s")) + return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); + r = sd_bus_message_read(m, "s", &name); if (r < 0) return synthetic_reply_method_errno(m, r, NULL); - if (!service_name_is_valid(name)) - return synthetic_reply_method_errno(m, -EINVAL, NULL); - if (streq(name, "org.freedesktop.DBus")) return synthetic_reply_method_return(m, "b", true); @@ -820,13 +774,13 @@ static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m) { } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "ReleaseName")) { const char *name; + if (!sd_bus_message_has_signature(m, "s")) + return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); + r = sd_bus_message_read(m, "s", &name); if (r < 0) return synthetic_reply_method_errno(m, r, NULL); - if (!service_name_is_valid(name)) - return synthetic_reply_method_errno(m, -EINVAL, NULL); - r = sd_bus_release_name(a, name); if (r < 0) { if (r == -ESRCH) @@ -837,11 +791,16 @@ static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m) { return synthetic_reply_method_errno(m, r, NULL); } + set_remove(owned_names, (char*) name); + return synthetic_reply_method_return(m, "u", BUS_NAME_RELEASED); } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "ReloadConfig")) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + if (!sd_bus_message_has_signature(m, "")) + return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); + r = sd_bus_error_setf(&error, SD_BUS_ERROR_NOT_SUPPORTED, "%s() is not supported", sd_bus_message_get_member(m)); return synthetic_reply_method_errno(m, r, &error); @@ -849,13 +808,18 @@ static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m) { } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "RequestName")) { const char *name; uint32_t flags, param; + bool in_queue; + + if (!sd_bus_message_has_signature(m, "su")) + return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); r = sd_bus_message_read(m, "su", &name, &flags); if (r < 0) return synthetic_reply_method_errno(m, r, NULL); - if (!service_name_is_valid(name)) - return synthetic_reply_method_errno(m, -EINVAL, NULL); + if (policy && !policy_check_own(policy, ucred->uid, ucred->gid, name)) + return synthetic_reply_method_errno(m, -EPERM, NULL); + if ((flags & ~(BUS_NAME_ALLOW_REPLACEMENT|BUS_NAME_REPLACE_EXISTING|BUS_NAME_DO_NOT_QUEUE)) != 0) return synthetic_reply_method_errno(m, -EINVAL, NULL); @@ -867,16 +831,25 @@ static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m) { if (!(flags & BUS_NAME_DO_NOT_QUEUE)) param |= SD_BUS_NAME_QUEUE; + r = set_put_strdup(owned_names, name); + if (r < 0) + return synthetic_reply_method_errno(m, r, NULL); + r = sd_bus_request_name(a, name, param); if (r < 0) { - if (r == -EEXIST) - return synthetic_reply_method_return(m, "u", BUS_NAME_EXISTS); if (r == -EALREADY) return synthetic_reply_method_return(m, "u", BUS_NAME_ALREADY_OWNER); + + set_remove(owned_names, (char*) name); + + if (r == -EEXIST) + return synthetic_reply_method_return(m, "u", BUS_NAME_EXISTS); return synthetic_reply_method_errno(m, r, NULL); } - if (r == 0) + in_queue = (r == 0); + + if (in_queue) return synthetic_reply_method_return(m, "u", BUS_NAME_IN_QUEUE); return synthetic_reply_method_return(m, "u", BUS_NAME_PRIMARY_OWNER); @@ -886,12 +859,13 @@ static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m) { const char *name; uint32_t flags; + if (!sd_bus_message_has_signature(m, "su")) + return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); + r = sd_bus_message_read(m, "su", &name, &flags); if (r < 0) return synthetic_reply_method_errno(m, r, NULL); - if (!service_name_is_valid(name)) - return synthetic_reply_method_errno(m, -EINVAL, NULL); if (flags != 0) return synthetic_reply_method_errno(m, -EINVAL, NULL); @@ -921,8 +895,8 @@ static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m) { _cleanup_bus_message_unref_ sd_bus_message *msg = NULL; _cleanup_strv_free_ char **args = NULL; - if (!peer_is_privileged(a, m)) - return synthetic_reply_method_errno(m, -EPERM, NULL); + if (!sd_bus_message_has_signature(m, "a{ss}")) + return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters")); r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{ss}"); if (r < 0) @@ -986,6 +960,166 @@ static int process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m) { } } +static int process_policy(sd_bus *from, sd_bus *to, sd_bus_message *m, Policy *policy, const struct ucred *our_ucred, Set *owned_names) { + int r; + + assert(from); + assert(to); + assert(m); + + if (!policy) + return 0; + + if (from->is_kernel) { + uid_t sender_uid = UID_INVALID; + gid_t sender_gid = GID_INVALID; + char **sender_names = NULL; + bool granted = false; + + /* Driver messages are always OK */ + if (streq_ptr(m->sender, "org.freedesktop.DBus")) + return 0; + + /* The message came from the kernel, and is sent to our legacy client. */ + r = sd_bus_creds_get_well_known_names(&m->creds, &sender_names); + if (r < 0) + return r; + + (void) sd_bus_creds_get_uid(&m->creds, &sender_uid); + (void) sd_bus_creds_get_gid(&m->creds, &sender_gid); + + /* First check whether the sender can send the message to our name */ + if (set_isempty(owned_names)) { + if (policy_check_send(policy, sender_uid, sender_gid, m->header->type, NULL, m->path, m->interface, m->member)) + granted = true; + } else { + Iterator i; + char *n; + + SET_FOREACH(n, owned_names, i) + if (policy_check_send(policy, sender_uid, sender_gid, m->header->type, n, m->path, m->interface, m->member)) { + granted = true; + break; + } + } + + if (granted) { + /* Then check whether us (the recipient) can recieve from the sender's name */ + if (strv_isempty(sender_names)) { + if (policy_check_recv(policy, our_ucred->uid, our_ucred->gid, m->header->type, NULL, m->path, m->interface, m->member)) + return 0; + } else { + char **n; + + STRV_FOREACH(n, sender_names) { + if (policy_check_recv(policy, our_ucred->uid, our_ucred->gid, m->header->type, *n, m->path, m->interface, m->member)) + return 0; + } + } + } + + /* Return an error back to the caller */ + if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL) + return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_ACCESS_DENIED, "Access prohibited by XML receiver policy."); + + /* Return 1, indicating that the message shall not be processed any further */ + return 1; + } + + if (to->is_kernel) { + _cleanup_bus_creds_unref_ sd_bus_creds *destination_creds = NULL; + uid_t destination_uid = UID_INVALID; + gid_t destination_gid = GID_INVALID; + const char *destination_unique = NULL; + char **destination_names = NULL; + bool granted = false; + + /* Driver messages are always OK */ + if (streq_ptr(m->destination, "org.freedesktop.DBus")) + return 0; + + /* The message came from the legacy client, and is sent to kdbus. */ + if (m->destination) { + r = bus_get_name_creds_kdbus(to, m->destination, + SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_UNIQUE_NAME| + SD_BUS_CREDS_UID|SD_BUS_CREDS_GID|SD_BUS_CREDS_PID, + true, &destination_creds); + if (r < 0) + return r; + + r = sd_bus_creds_get_well_known_names(destination_creds, &destination_names); + if (r < 0) + return r; + + r = sd_bus_creds_get_unique_name(destination_creds, &destination_unique); + if (r < 0) + return r; + + (void) sd_bus_creds_get_uid(destination_creds, &destination_uid); + (void) sd_bus_creds_get_gid(destination_creds, &destination_gid); + } + + /* First check if we (the sender) can send to this name */ + if (strv_isempty(destination_names)) { + if (policy_check_send(policy, our_ucred->uid, our_ucred->gid, m->header->type, NULL, m->path, m->interface, m->member)) + granted = true; + } else { + char **n; + + STRV_FOREACH(n, destination_names) { + if (policy_check_send(policy, our_ucred->uid, our_ucred->gid, m->header->type, *n, m->path, m->interface, m->member)) { + + /* If we made a receiver decision, + then remember which name's policy + we used, and to which unique ID it + mapped when we made the + decision. Then, let's pass this to + the kernel when sending the + message, so that it refuses the + operation should the name and + unique ID not map to each other + anymore. */ + + r = free_and_strdup(&m->destination_ptr, *n); + if (r < 0) + return r; + + r = bus_kernel_parse_unique_name(destination_unique, &m->verify_destination_id); + if (r < 0) + break; + + granted = true; + break; + } + } + } + + /* Then check if the recipient can receive from our name */ + if (granted) { + if (set_isempty(owned_names)) { + if (policy_check_recv(policy, destination_uid, destination_gid, m->header->type, NULL, m->path, m->interface, m->member)) + return 0; + } else { + Iterator i; + char *n; + + SET_FOREACH(n, owned_names, i) + if (policy_check_recv(policy, destination_uid, destination_gid, m->header->type, n, m->path, m->interface, m->member)) + return 0; + } + } + + /* Return an error back to the caller */ + if (m->header->type == SD_BUS_MESSAGE_METHOD_CALL) + return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_ACCESS_DENIED, "Access prohibited by XML sender policy."); + + /* Return 1, indicating that the message shall not be processed any further */ + return 1; + } + + return 0; +} + static int process_hello(sd_bus *a, sd_bus *b, sd_bus_message *m, bool *got_hello) { _cleanup_bus_message_unref_ sd_bus_message *n = NULL; bool is_hello; @@ -1024,34 +1158,24 @@ static int process_hello(sd_bus *a, sd_bus *b, sd_bus_message *m, bool *got_hell return 0; r = sd_bus_message_new_method_return(m, &n); - if (r < 0) { - log_error("Failed to generate HELLO reply: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to generate HELLO reply: %m"); r = sd_bus_message_append(n, "s", a->unique_name); - if (r < 0) { - log_error("Failed to append unique name to HELLO reply: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to append unique name to HELLO reply: %m"); r = bus_message_append_sender(n, "org.freedesktop.DBus"); - if (r < 0) { - log_error("Failed to append sender to HELLO reply: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to append sender to HELLO reply: %m"); r = bus_seal_synthetic_message(b, n); - if (r < 0) { - log_error("Failed to seal HELLO reply: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to seal HELLO reply: %m"); r = sd_bus_send(b, n, NULL); - if (r < 0) { - log_error("Failed to send HELLO reply: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to send HELLO reply: %m"); n = sd_bus_message_unref(n); r = sd_bus_message_new_signal( @@ -1060,34 +1184,24 @@ static int process_hello(sd_bus *a, sd_bus *b, sd_bus_message *m, bool *got_hell "/org/freedesktop/DBus", "org.freedesktop.DBus", "NameAcquired"); - if (r < 0) { - log_error("Failed to allocate initial NameAcquired message: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to allocate initial NameAcquired message: %m"); r = sd_bus_message_append(n, "s", a->unique_name); - if (r < 0) { - log_error("Failed to append unique name to NameAcquired message: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to append unique name to NameAcquired message: %m"); r = bus_message_append_sender(n, "org.freedesktop.DBus"); - if (r < 0) { - log_error("Failed to append sender to NameAcquired message: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to append sender to NameAcquired message: %m"); r = bus_seal_synthetic_message(b, n); - if (r < 0) { - log_error("Failed to seal NameAcquired message: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to seal NameAcquired message: %m"); r = sd_bus_send(b, n, NULL); - if (r < 0) { - log_error("Failed to send NameAcquired message: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to send NameAcquired message: %m"); return 1; } @@ -1122,6 +1236,23 @@ static int patch_sender(sd_bus *a, sd_bus_message *m) { return 0; } +static int mac_smack_apply_label_and_drop_cap_mac_admin(pid_t its_pid, const char *new_label) { +#ifdef HAVE_SMACK + int r = 0, k; + + if (!mac_smack_use()) + return 0; + + if (new_label && its_pid > 0) + r = mac_smack_apply_pid(its_pid, new_label); + + k = drop_capability(CAP_MAC_ADMIN); + return r < 0 ? r : k; +#else + return 0; +#endif +} + int main(int argc, char *argv[]) { _cleanup_bus_close_unref_ sd_bus *a = NULL, *b = NULL; @@ -1131,7 +1262,8 @@ int main(int argc, char *argv[]) { bool is_unix; struct ucred ucred = {}; _cleanup_free_ char *peersec = NULL; - Policy policy = {}; + Policy policy_buffer = {}, *policy = NULL; + _cleanup_set_free_free_ Set *owned_names = NULL; log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); log_parse_environment(); @@ -1141,14 +1273,6 @@ int main(int argc, char *argv[]) { if (r <= 0) goto finish; - r = policy_load(&policy, arg_configuration); - if (r < 0) { - log_error("Failed to load policy: %s", strerror(-r)); - goto finish; - } - - /* policy_dump(&policy); */ - r = sd_listen_fds(0); if (r == 0) { in_fd = STDIN_FILENO; @@ -1168,6 +1292,10 @@ int main(int argc, char *argv[]) { if (is_unix) { (void) getpeercred(in_fd, &ucred); (void) getpeersec(in_fd, &peersec); + + r = mac_smack_apply_label_and_drop_cap_mac_admin(getpid(), peersec); + if (r < 0) + log_warning_errno(r, "Failed to set SMACK label (%s) and drop CAP_MAC_ADMIN: %m", peersec); } if (arg_drop_privileges) { @@ -1177,7 +1305,7 @@ int main(int argc, char *argv[]) { r = get_user_creds(&user, &uid, &gid, NULL, NULL); if (r < 0) { - log_error("Cannot resolve user name %s: %s", user, strerror(-r)); + log_error_errno(r, "Cannot resolve user name %s: %m", user); goto finish; } @@ -1186,34 +1314,54 @@ int main(int argc, char *argv[]) { goto finish; } + owned_names = set_new(&string_hash_ops); + if (!owned_names) { + log_oom(); + goto finish; + } + r = sd_bus_new(&a); if (r < 0) { - log_error("Failed to allocate bus: %s", strerror(-r)); + log_error_errno(r, "Failed to allocate bus: %m"); goto finish; } - r = sd_bus_set_name(a, "sd-proxy"); + r = sd_bus_set_description(a, "sd-proxy"); if (r < 0) { - log_error("Failed to set bus name: %s", strerror(-r)); + log_error_errno(r, "Failed to set bus name: %m"); goto finish; } r = sd_bus_set_address(a, arg_address); if (r < 0) { - log_error("Failed to set address to connect to: %s", strerror(-r)); + log_error_errno(r, "Failed to set address to connect to: %m"); goto finish; } r = sd_bus_negotiate_fds(a, is_unix); if (r < 0) { - log_error("Failed to set FD negotiation: %s", strerror(-r)); + log_error_errno(r, "Failed to set FD negotiation: %m"); + goto finish; + } + + r = sd_bus_negotiate_creds(a, true, SD_BUS_CREDS_UID|SD_BUS_CREDS_PID|SD_BUS_CREDS_GID|SD_BUS_CREDS_SELINUX_CONTEXT); + if (r < 0) { + log_error_errno(r, "Failed to set credential negotiation: %m"); goto finish; } if (ucred.pid > 0) { - a->fake_creds.pid = ucred.pid; + a->fake_pids.pid = ucred.pid; + a->fake_pids_valid = true; + a->fake_creds.uid = ucred.uid; + a->fake_creds.euid = UID_INVALID; + a->fake_creds.suid = UID_INVALID; + a->fake_creds.fsuid = UID_INVALID; a->fake_creds.gid = ucred.gid; + a->fake_creds.egid = GID_INVALID; + a->fake_creds.sgid = GID_INVALID; + a->fake_creds.fsgid = GID_INVALID; a->fake_creds_valid = true; } @@ -1226,43 +1374,97 @@ int main(int argc, char *argv[]) { r = sd_bus_start(a); if (r < 0) { - log_error("Failed to start bus client: %s", strerror(-r)); + log_error_errno(r, "Failed to start bus client: %m"); goto finish; } - r = sd_bus_get_server_id(a, &server_id); + r = sd_bus_get_bus_id(a, &server_id); if (r < 0) { - log_error("Failed to get server ID: %s", strerror(-r)); + log_error_errno(r, "Failed to get server ID: %m"); goto finish; } + if (a->is_kernel) { + if (!arg_configuration) { + const char *scope; + + r = sd_bus_get_scope(a, &scope); + if (r < 0) { + log_error_errno(r, "Couldn't determine bus scope: %m"); + goto finish; + } + + if (streq(scope, "system")) + arg_configuration = strv_new( + "/etc/dbus-1/system.conf", + "/etc/dbus-1/system.d/", + "/etc/dbus-1/system-local.conf", + NULL); + else if (streq(scope, "user")) + arg_configuration = strv_new( + "/etc/dbus-1/session.conf", + "/etc/dbus-1/session.d/", + "/etc/dbus-1/session-local.conf", + NULL); + else { + log_error("Unknown scope %s, don't know which policy to load. Refusing.", scope); + goto finish; + } + + if (!arg_configuration) { + r = log_oom(); + goto finish; + } + } + + r = policy_load(&policy_buffer, arg_configuration); + if (r < 0) { + log_error_errno(r, "Failed to load policy: %m"); + goto finish; + } + + policy = &policy_buffer; + /* policy_dump(policy); */ + + if (!policy_check_hello(policy, ucred.uid, ucred.gid)) { + r = log_error_errno(EPERM, "Policy denied connection."); + goto finish; + } + } + r = sd_bus_new(&b); if (r < 0) { - log_error("Failed to allocate bus: %s", strerror(-r)); + log_error_errno(r, "Failed to allocate bus: %m"); goto finish; } r = sd_bus_set_fd(b, in_fd, out_fd); if (r < 0) { - log_error("Failed to set fds: %s", strerror(-r)); + log_error_errno(r, "Failed to set fds: %m"); goto finish; } r = sd_bus_set_server(b, 1, server_id); if (r < 0) { - log_error("Failed to set server mode: %s", strerror(-r)); + log_error_errno(r, "Failed to set server mode: %m"); goto finish; } r = sd_bus_negotiate_fds(b, is_unix); if (r < 0) { - log_error("Failed to set FD negotiation: %s", strerror(-r)); + log_error_errno(r, "Failed to set FD negotiation: %m"); + goto finish; + } + + r = sd_bus_negotiate_creds(b, true, SD_BUS_CREDS_UID|SD_BUS_CREDS_PID|SD_BUS_CREDS_GID|SD_BUS_CREDS_SELINUX_CONTEXT); + if (r < 0) { + log_error_errno(r, "Failed to set credential negotiation: %m"); goto finish; } r = sd_bus_set_anonymous(b, true); if (r < 0) { - log_error("Failed to set anonymous authentication: %s", strerror(-r)); + log_error_errno(r, "Failed to set anonymous authentication: %m"); goto finish; } @@ -1270,13 +1472,13 @@ int main(int argc, char *argv[]) { r = sd_bus_start(b); if (r < 0) { - log_error("Failed to start bus client: %s", strerror(-r)); + log_error_errno(r, "Failed to start bus client: %m"); goto finish; } r = rename_service(a, b); if (r < 0) - log_debug("Failed to rename process: %s", strerror(-r)); + log_debug_errno(r, "Failed to rename process: %m"); if (a->is_kernel) { _cleanup_free_ char *match = NULL; @@ -1284,7 +1486,7 @@ int main(int argc, char *argv[]) { r = sd_bus_get_unique_name(a, &unique); if (r < 0) { - log_error("Failed to get unique name: %s", strerror(-r)); + log_error_errno(r, "Failed to get unique name: %m"); goto finish; } @@ -1304,7 +1506,7 @@ int main(int argc, char *argv[]) { r = sd_bus_add_match(a, NULL, match, NULL, NULL); if (r < 0) { - log_error("Failed to add match for NameLost: %s", strerror(-r)); + log_error_errno(r, "Failed to add match for NameLost: %m"); goto finish; } @@ -1325,7 +1527,7 @@ int main(int argc, char *argv[]) { r = sd_bus_add_match(a, NULL, match, NULL, NULL); if (r < 0) { - log_error("Failed to add match for NameAcquired: %s", strerror(-r)); + log_error_errno(r, "Failed to add match for NameAcquired: %m"); goto finish; } } @@ -1339,18 +1541,22 @@ int main(int argc, char *argv[]) { int k; if (got_hello) { + /* Read messages from bus, to pass them on to our client */ + r = sd_bus_process(a, &m); if (r < 0) { /* treat 'connection reset by peer' as clean exit condition */ if (r == -ECONNRESET) r = 0; else - log_error("Failed to process bus a: %s", strerror(-r)); + log_error_errno(r, "Failed to process bus a: %m"); goto finish; } if (m) { + bool processed = false; + /* We officially got EOF, let's quit */ if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) { r = 0; @@ -1360,22 +1566,37 @@ int main(int argc, char *argv[]) { k = synthesize_name_acquired(a, b, m); if (k < 0) { r = k; - log_error("Failed to synthesize message: %s", strerror(-r)); + log_error_errno(r, "Failed to synthesize message: %m"); goto finish; } patch_sender(a, m); - k = sd_bus_send(b, m, NULL); - if (k < 0) { - if (k == -ECONNRESET) - r = 0; - else { + if (policy) { + k = process_policy(a, b, m, policy, &ucred, owned_names); + if (k < 0) { r = k; - log_error("Failed to send message: %s", strerror(-r)); + log_error_errno(r, "Failed to process policy: %m"); + goto finish; + } else if (k > 0) { + r = 1; + processed = true; } + } - goto finish; + if (!processed) { + k = sd_bus_send(b, m, NULL); + if (k < 0) { + if (k == -ECONNRESET) + r = 0; + else { + r = k; + log_error_errno(r, "Failed to send message to client: %m"); + } + + goto finish; + } else + r = 1; } } @@ -1383,18 +1604,21 @@ int main(int argc, char *argv[]) { continue; } + /* Read messages from our client, to pass them on to the bus */ r = sd_bus_process(b, &m); if (r < 0) { /* treat 'connection reset by peer' as clean exit condition */ if (r == -ECONNRESET) r = 0; else - log_error("Failed to process bus b: %s", strerror(-r)); + log_error_errno(r, "Failed to process bus b: %m"); goto finish; } if (m) { + bool processed = false; + /* We officially got EOF, let's quit */ if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) { r = 0; @@ -1404,40 +1628,57 @@ int main(int argc, char *argv[]) { k = process_hello(a, b, m, &got_hello); if (k < 0) { r = k; - log_error("Failed to process HELLO: %s", strerror(-r)); + log_error_errno(r, "Failed to process HELLO: %m"); goto finish; + } else if (k > 0) { + processed = true; + r = 1; } - if (k > 0) - r = k; - else { - k = process_policy(a, b, m); - if (k < 0) { - r = k; - log_error("Failed to process policy: %s", strerror(-r)); - goto finish; - } - - k = process_driver(a, b, m); + if (!processed) { + k = process_driver(a, b, m, policy, &ucred, owned_names); if (k < 0) { r = k; - log_error("Failed to process driver calls: %s", strerror(-r)); + log_error_errno(r, "Failed to process driver calls: %m"); goto finish; + } else if (k > 0) { + processed = true; + r = 1; } - if (k > 0) - r = k; - else { - k = sd_bus_send(a, m, NULL); - if (k < 0) { - if (k == -ECONNRESET) - r = 0; - else { - r = k; - log_error("Failed to send message: %s", strerror(-r)); + if (!processed) { + + for (;;) { + if (policy) { + k = process_policy(b, a, m, policy, &ucred, owned_names); + if (k < 0) { + r = k; + log_error_errno(r, "Failed to process policy: %m"); + goto finish; + } else if (k > 0) { + processed = true; + r = 1; + break; + } } - goto finish; + k = sd_bus_send(a, m, NULL); + if (k < 0) { + if (k == -EREMCHG) + /* The name database changed since the policy check, hence let's check again */ + continue; + else if (k == -ECONNRESET) + r = 0; + else { + r = k; + log_error_errno(r, "Failed to send message to bus: %m"); + } + + goto finish; + } else + r = 1; + + break; } } } @@ -1448,31 +1689,31 @@ int main(int argc, char *argv[]) { fd = sd_bus_get_fd(a); if (fd < 0) { - log_error("Failed to get fd: %s", strerror(-r)); + log_error_errno(r, "Failed to get fd: %m"); goto finish; } events_a = sd_bus_get_events(a); if (events_a < 0) { - log_error("Failed to get events mask: %s", strerror(-r)); + log_error_errno(r, "Failed to get events mask: %m"); goto finish; } r = sd_bus_get_timeout(a, &timeout_a); if (r < 0) { - log_error("Failed to get timeout: %s", strerror(-r)); + log_error_errno(r, "Failed to get timeout: %m"); goto finish; } events_b = sd_bus_get_events(b); if (events_b < 0) { - log_error("Failed to get events mask: %s", strerror(-r)); + log_error_errno(r, "Failed to get events mask: %m"); goto finish; } r = sd_bus_get_timeout(b, &timeout_b); if (r < 0) { - log_error("Failed to get timeout: %s", strerror(-r)); + log_error_errno(r, "Failed to get timeout: %m"); goto finish; } @@ -1502,7 +1743,7 @@ int main(int argc, char *argv[]) { r = ppoll(pollfd, 3, ts, NULL); if (r < 0) { - log_error("ppoll() failed: %m"); + log_error_errno(errno, "ppoll() failed: %m"); goto finish; } } @@ -1512,7 +1753,7 @@ finish: "STOPPING=1\n" "STATUS=Shutting down."); - policy_free(&policy); + policy_free(&policy_buffer); strv_free(arg_configuration); free(arg_address); diff --git a/src/bus-proxyd/test-bus-policy.c b/src/bus-proxyd/test-bus-policy.c index c9a027e877..91ab33da4a 100644 --- a/src/bus-proxyd/test-bus-policy.c +++ b/src/bus-proxyd/test-bus-policy.c @@ -62,41 +62,29 @@ static int test_policy_load(Policy *p, const char *name) int main(int argc, char *argv[]) { Policy p = {}; - struct ucred ucred = {}; /* Ownership tests */ assert_se(test_policy_load(&p, "ownerships.conf") == 0); - ucred.uid = 0; - assert_se(policy_check_own(&p, &ucred, "org.test.test1") == true); - ucred.uid = 1; - assert_se(policy_check_own(&p, &ucred, "org.test.test1") == true); + assert_se(policy_check_own(&p, 0, 0, "org.test.test1") == true); + assert_se(policy_check_own(&p, 1, 0, "org.test.test1") == true); - ucred.uid = 0; - assert_se(policy_check_own(&p, &ucred, "org.test.test2") == true); - ucred.uid = 1; - assert_se(policy_check_own(&p, &ucred, "org.test.test2") == false); + assert_se(policy_check_own(&p, 0, 0, "org.test.test2") == true); + assert_se(policy_check_own(&p, 1, 0, "org.test.test2") == false); - ucred.uid = 0; - assert_se(policy_check_own(&p, &ucred, "org.test.test3") == false); - ucred.uid = 1; - assert_se(policy_check_own(&p, &ucred, "org.test.test3") == false); + assert_se(policy_check_own(&p, 0, 0, "org.test.test3") == false); + assert_se(policy_check_own(&p, 1, 0, "org.test.test3") == false); - ucred.uid = 0; - assert_se(policy_check_own(&p, &ucred, "org.test.test4") == false); - ucred.uid = 1; - assert_se(policy_check_own(&p, &ucred, "org.test.test4") == true); + assert_se(policy_check_own(&p, 0, 0, "org.test.test4") == false); + assert_se(policy_check_own(&p, 1, 0, "org.test.test4") == true); policy_free(&p); /* Signaltest */ assert_se(test_policy_load(&p, "signals.conf") == 0); - ucred.uid = 0; - assert_se(policy_check_send(&p, &ucred, SD_BUS_MESSAGE_SIGNAL, "bli.bla.blubb", NULL, "/an/object/path", NULL) == true); - - ucred.uid = 1; - assert_se(policy_check_send(&p, &ucred, SD_BUS_MESSAGE_SIGNAL, "bli.bla.blubb", NULL, "/an/object/path", NULL) == false); + assert_se(policy_check_send(&p, 0, 0, SD_BUS_MESSAGE_SIGNAL, "bli.bla.blubb", NULL, "/an/object/path", NULL) == true); + assert_se(policy_check_send(&p, 1, 0, SD_BUS_MESSAGE_SIGNAL, "bli.bla.blubb", NULL, "/an/object/path", NULL) == false); policy_free(&p); @@ -104,14 +92,12 @@ int main(int argc, char *argv[]) { assert_se(test_policy_load(&p, "methods.conf") == 0); policy_dump(&p); - ucred.uid = 0; - - assert_se(policy_check_send(&p, &ucred, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "bli.bla.blubb", "Member") == false); - assert_se(policy_check_send(&p, &ucred, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "bli.bla.blubb", "Member") == false); - assert_se(policy_check_send(&p, &ucred, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "org.test.int1", "Member") == true); - assert_se(policy_check_send(&p, &ucred, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "org.test.int2", "Member") == true); + assert_se(policy_check_send(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "bli.bla.blubb", "Member") == false); + assert_se(policy_check_send(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "bli.bla.blubb", "Member") == false); + assert_se(policy_check_send(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "org.test.int1", "Member") == true); + assert_se(policy_check_send(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "org.test.int2", "Member") == true); - assert_se(policy_check_recv(&p, &ucred, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test3", "/an/object/path", "org.test.int3", "Member111") == true); + assert_se(policy_check_recv(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test3", "/an/object/path", "org.test.int3", "Member111") == true); policy_free(&p); @@ -119,15 +105,54 @@ int main(int argc, char *argv[]) { assert_se(test_policy_load(&p, "hello.conf") == 0); policy_dump(&p); - ucred.uid = 0; - assert_se(policy_check_hello(&p, &ucred) == true); + assert_se(policy_check_hello(&p, 0, 0) == true); + assert_se(policy_check_hello(&p, 1, 0) == false); + assert_se(policy_check_hello(&p, 0, 1) == false); + + policy_free(&p); + + /* dbus1 test file: ownership */ + + assert_se(test_policy_load(&p, "check-own-rules.conf") >= 0); + policy_dump(&p); + + assert_se(policy_check_own(&p, 0, 0, "org.freedesktop") == false); + assert_se(policy_check_own(&p, 0, 0, "org.freedesktop.ManySystem") == false); + assert_se(policy_check_own(&p, 0, 0, "org.freedesktop.ManySystems") == true); + assert_se(policy_check_own(&p, 0, 0, "org.freedesktop.ManySystems.foo") == true); + assert_se(policy_check_own(&p, 0, 0, "org.freedesktop.ManySystems.foo.bar") == true); + assert_se(policy_check_own(&p, 0, 0, "org.freedesktop.ManySystems2") == false); + assert_se(policy_check_own(&p, 0, 0, "org.freedesktop.ManySystems2.foo") == false); + assert_se(policy_check_own(&p, 0, 0, "org.freedesktop.ManySystems2.foo.bar") == false); - ucred.uid = 1; - assert_se(policy_check_hello(&p, &ucred) == false); + policy_free(&p); + + /* dbus1 test file: many rules */ + + assert_se(test_policy_load(&p, "many-rules.conf") >= 0); + policy_dump(&p); + policy_free(&p); + + /* dbus1 test file: generic test */ + + assert_se(test_policy_load(&p, "test.conf") >= 0); + policy_dump(&p); - ucred.uid = 0; - ucred.gid = 1; - assert_se(policy_check_hello(&p, &ucred) == false); + assert_se(policy_check_own(&p, 0, 0, "org.foo.FooService") == true); + assert_se(policy_check_own(&p, 0, 0, "org.foo.FooService2") == false); + assert_se(policy_check_send(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "org.test.int2", "Member") == false); + assert_se(policy_check_send(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "org.foo.FooBroadcastInterface", "Member") == true); + assert_se(policy_check_recv(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.foo.FooService", "/an/object/path", "org.foo.FooBroadcastInterface", "Member") == true); + assert_se(policy_check_recv(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.foo.FooService", "/an/object/path", "org.foo.FooBroadcastInterface2", "Member") == false); + assert_se(policy_check_recv(&p, 0, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.foo.FooService2", "/an/object/path", "org.foo.FooBroadcastInterface", "Member") == false); + + assert_se(policy_check_own(&p, 100, 0, "org.foo.FooService") == false); + assert_se(policy_check_own(&p, 100, 0, "org.foo.FooService2") == false); + assert_se(policy_check_send(&p, 100, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "org.test.int2", "Member") == false); + assert_se(policy_check_send(&p, 100, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.test.test1", "/an/object/path", "org.foo.FooBroadcastInterface", "Member") == false); + assert_se(policy_check_recv(&p, 100, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.foo.FooService", "/an/object/path", "org.foo.FooBroadcastInterface", "Member") == true); + assert_se(policy_check_recv(&p, 100, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.foo.FooService", "/an/object/path", "org.foo.FooBroadcastInterface2", "Member") == false); + assert_se(policy_check_recv(&p, 100, 0, SD_BUS_MESSAGE_METHOD_CALL, "org.foo.FooService2", "/an/object/path", "org.foo.FooBroadcastInterface", "Member") == false); policy_free(&p); diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c index 7070503d68..f400bccc0a 100644 --- a/src/cgls/cgls.c +++ b/src/cgls/cgls.c @@ -155,7 +155,7 @@ int main(int argc, char *argv[]) { r = bus_open_transport(BUS_TRANSPORT_LOCAL, NULL, false, &bus); if (r < 0) { - log_error("Failed to create bus connection: %s", strerror(-r)); + log_error_errno(r, "Failed to create bus connection: %m"); goto finish; } @@ -186,7 +186,7 @@ int main(int argc, char *argv[]) { p = get_current_dir_name(); if (!p) { - log_error("Cannot determine current working directory: %m"); + log_error_errno(errno, "Cannot determine current working directory: %m"); goto finish; } @@ -206,7 +206,7 @@ int main(int argc, char *argv[]) { m = strappenda("/run/systemd/machines/", arg_machine); r = parse_env_file(m, NEWLINE, "SCOPE", &scope, NULL); if (r < 0) { - log_error("Failed to get machine path: %s", strerror(-r)); + log_error_errno(r, "Failed to get machine path: %m"); goto finish; } @@ -246,8 +246,8 @@ int main(int argc, char *argv[]) { } else r = cg_get_root_path(&root); if (r < 0) { - log_error("Failed to get %s path: %s", - arg_machine ? "machine" : "root", strerror(-r)); + log_error_errno(r, "Failed to get %s path: %m", + arg_machine ? "machine" : "root"); goto finish; } @@ -257,7 +257,7 @@ int main(int argc, char *argv[]) { } if (r < 0) { - log_error("Failed to list cgroup tree %s: %s", root, strerror(-r)); + log_error_errno(r, "Failed to list cgroup tree %s: %m", root); retval = EXIT_FAILURE; } else retval = EXIT_SUCCESS; diff --git a/src/cgroups-agent/cgroups-agent.c b/src/cgroups-agent/cgroups-agent.c index e581fbc9b6..529e843030 100644 --- a/src/cgroups-agent/cgroups-agent.c +++ b/src/cgroups-agent/cgroups-agent.c @@ -48,7 +48,7 @@ int main(int argc, char *argv[]) { /* If we couldn't connect we assume this was triggered * while systemd got restarted/transitioned from * initrd to the system, so let's ignore this */ - log_debug("Failed to get D-Bus connection: %s", strerror(-r)); + log_debug_errno(r, "Failed to get D-Bus connection: %m"); return EXIT_FAILURE; } @@ -58,7 +58,7 @@ int main(int argc, char *argv[]) { "Released", "s", argv[1]); if (r < 0) { - log_debug("Failed to send signal message on private connection: %s", strerror(-r)); + log_debug_errno(r, "Failed to send signal message on private connection: %m"); return EXIT_FAILURE; } diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c index 932a7ba7c6..3c7ad40605 100644 --- a/src/cgtop/cgtop.c +++ b/src/cgtop/cgtop.c @@ -748,7 +748,7 @@ int main(int argc, char *argv[]) { if (r == -ETIMEDOUT) continue; if (r < 0) { - log_error("Couldn't read key: %s", strerror(-r)); + log_error_errno(r, "Couldn't read key: %m"); goto finish; } } @@ -842,7 +842,7 @@ finish: group_hashmap_free(b); if (r < 0) { - log_error("Exiting with failure: %s", strerror(-r)); + log_error_errno(r, "Exiting with failure: %m"); return EXIT_FAILURE; } diff --git a/src/console/consoled-manager.c b/src/console/consoled-manager.c index 1b856f2228..9dd62f04a0 100644 --- a/src/console/consoled-manager.c +++ b/src/console/consoled-manager.c @@ -141,16 +141,14 @@ static int manager_sysview_session_add(Manager *m, sysview_event *event) { int r; r = sysview_session_take_control(session); - if (r < 0) { - log_error("Cannot request session control on '%s': %s", - sysview_session_get_name(session), strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Cannot request session control on '%s': %m", + sysview_session_get_name(session)); r = session_new(&s, m, session); if (r < 0) { - log_error("Cannot create session on '%s': %s", - sysview_session_get_name(session), strerror(-r)); + log_error_errno(r, "Cannot create session on '%s': %m", + sysview_session_get_name(session)); sysview_session_release_control(session); return r; } @@ -226,8 +224,8 @@ static int manager_sysview_session_control(Manager *m, sysview_event *event) { return 0; if (error < 0) { - log_error("Cannot take session control on '%s': %s", - sysview_session_get_name(session), strerror(-error)); + log_error_errno(error, "Cannot take session control on '%s': %m", + sysview_session_get_name(session)); session_free(s); sysview_session_set_userdata(session, NULL); return -error; diff --git a/src/console/consoled-session.c b/src/console/consoled-session.c index 8bacacab35..927965e02c 100644 --- a/src/console/consoled-session.c +++ b/src/console/consoled-session.c @@ -106,8 +106,8 @@ static void session_grdev_fn(grdev_session *grdev, void *userdata, grdev_event * r = display_new(&d, s, display); if (r < 0) { - log_error("Cannot create display '%s' on '%s': %s", - grdev_display_get_name(display), sysview_session_get_name(s->sysview), strerror(-r)); + log_error_errno(r, "Cannot create display '%s' on '%s': %m", + grdev_display_get_name(display), sysview_session_get_name(s->sysview)); break; } @@ -231,7 +231,7 @@ void session_dirty(Session *s) { r = sd_event_source_set_enabled(s->redraw_src, SD_EVENT_ONESHOT); if (r < 0) - log_error("Cannot enable redraw-source: %s", strerror(-r)); + log_error_errno(r, "Cannot enable redraw-source: %m"); } void session_add_device(Session *s, sysview_device *device) { diff --git a/src/console/consoled-terminal.c b/src/console/consoled-terminal.c index d091579aa5..19d14016ad 100644 --- a/src/console/consoled-terminal.c +++ b/src/console/consoled-terminal.c @@ -52,7 +52,7 @@ static int terminal_pty_fn(Pty *pty, void *userdata, unsigned int event, const v case PTY_DATA: r = term_screen_feed_text(t->screen, ptr, size); if (r < 0) - log_error("Cannot update screen state: %s", strerror(-r)); + log_error_errno(r, "Cannot update screen state: %m"); workspace_dirty(t->workspace); break; @@ -128,12 +128,12 @@ void terminal_resize(Terminal *t) { if (t->pty) { r = pty_resize(t->pty, width, height); if (r < 0) - log_error("Cannot resize pty: %s", strerror(-r)); + log_error_errno(r, "Cannot resize pty: %m"); } r = term_screen_resize(t->screen, width, height); if (r < 0) - log_error("Cannot resize screen: %s", strerror(-r)); + log_error_errno(r, "Cannot resize screen: %m"); } void terminal_run(Terminal *t) { @@ -151,7 +151,7 @@ void terminal_run(Terminal *t) { term_screen_get_width(t->screen), term_screen_get_height(t->screen)); if (pid < 0) { - log_error("Cannot fork PTY: %s", strerror(-pid)); + log_error_errno(pid, "Cannot fork PTY: %m"); return; } else if (pid == 0) { /* child */ @@ -165,7 +165,7 @@ void terminal_run(Terminal *t) { setenv("COLORTERM", "systemd-console", 1); execve(argv[0], argv, environ); - log_error("Cannot exec %s (%d): %m", argv[0], -errno); + log_error_errno(errno, "Cannot exec %s (%d): %m", argv[0], -errno); _exit(1); } } @@ -189,8 +189,7 @@ static void terminal_feed_keyboard(Terminal *t, idev_data *data) { kdata->codepoints, kdata->mods); if (r < 0) - log_error("Cannot feed keyboard data to screen: %s", - strerror(-r)); + log_error_errno(r, "Cannot feed keyboard data to screen: %m"); } } diff --git a/src/console/consoled.c b/src/console/consoled.c index b0c9eda1ac..6f65dcdcb9 100644 --- a/src/console/consoled.c +++ b/src/console/consoled.c @@ -45,7 +45,7 @@ int main(int argc, char *argv[]) { r = manager_new(&m); if (r < 0) { - log_error("Could not create manager: %s", strerror(-r)); + log_error_errno(r, "Could not create manager: %m"); goto out; } @@ -55,7 +55,7 @@ int main(int argc, char *argv[]) { r = manager_run(m); if (r < 0) { - log_error("Cannot run manager: %s", strerror(-r)); + log_error_errno(r, "Cannot run manager: %m"); goto out; } diff --git a/src/core/audit-fd.c b/src/core/audit-fd.c index 4326d17360..5a18e263a8 100644 --- a/src/core/audit-fd.c +++ b/src/core/audit-fd.c @@ -41,7 +41,7 @@ int get_audit_fd(void) { if (audit_fd < 0) { if (errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT) - log_error("Failed to connect to audit log: %m"); + log_error_errno(errno, "Failed to connect to audit log: %m"); audit_fd = errno ? -errno : -EINVAL; } diff --git a/src/core/automount.c b/src/core/automount.c index f72aca2957..f795487131 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -75,7 +75,7 @@ static void repeat_unmount(const char *path) { continue; if (errno != EINVAL) - log_error("Failed to unmount: %m"); + log_error_errno(errno, "Failed to unmount: %m"); break; } @@ -151,7 +151,7 @@ static int automount_verify(Automount *a) { return 0; if (path_equal(a->where, "/")) { - log_error_unit(UNIT(a)->id, "Cannot have an automount unit for the root directory. Refusing."); + log_unit_error(UNIT(a)->id, "Cannot have an automount unit for the root directory. Refusing."); return -EINVAL; } @@ -162,7 +162,7 @@ static int automount_verify(Automount *a) { b = unit_has_name(UNIT(a), e); if (!b) { - log_error_unit(UNIT(a)->id, "%s's Where setting doesn't match unit name. Refusing.", UNIT(a)->id); + log_unit_error(UNIT(a)->id, "%s's Where setting doesn't match unit name. Refusing.", UNIT(a)->id); return -EINVAL; } @@ -226,7 +226,7 @@ static void automount_set_state(Automount *a, AutomountState state) { unmount_autofs(a); if (state != old_state) - log_debug_unit(UNIT(a)->id, + log_unit_debug(UNIT(a)->id, "%s changed %s -> %s", UNIT(a)->id, automount_state_to_string(old_state), @@ -300,10 +300,8 @@ static int open_dev_autofs(Manager *m) { label_fix("/dev/autofs", false, false); m->dev_autofs_fd = open("/dev/autofs", O_CLOEXEC|O_RDONLY); - if (m->dev_autofs_fd < 0) { - log_error("Failed to open /dev/autofs: %m"); - return -errno; - } + if (m->dev_autofs_fd < 0) + return log_error_errno(errno, "Failed to open /dev/autofs: %m"); init_autofs_dev_ioctl(¶m); if (ioctl(m->dev_autofs_fd, AUTOFS_DEV_IOCTL_VERSION, ¶m) < 0) { @@ -422,9 +420,9 @@ int automount_send_ready(Automount *a, int status) { return ioctl_fd; if (status) - log_debug_unit(UNIT(a)->id, "Sending failure: %s", strerror(-status)); + log_unit_debug_errno(UNIT(a)->id, status, "Sending failure: %m"); else - log_debug_unit(UNIT(a)->id, "Sending success."); + log_unit_debug(UNIT(a)->id, "Sending success."); r = 0; @@ -536,7 +534,7 @@ fail: if (mounted) repeat_unmount(a->where); - log_error_unit(UNIT(a)->id, + log_unit_error(UNIT(a)->id, "Failed to initialize automounter: %s", strerror(-r)); automount_enter_dead(a, AUTOMOUNT_FAILURE_RESOURCES); } @@ -551,7 +549,7 @@ static void automount_enter_runnning(Automount *a) { /* We don't take mount requests anymore if we are supposed to * shut down anyway */ if (unit_stop_pending(UNIT(a))) { - log_debug_unit(UNIT(a)->id, + log_unit_debug(UNIT(a)->id, "Suppressing automount request on %s since unit stop is scheduled.", UNIT(a)->id); automount_send_ready(a, -EHOSTDOWN); return; @@ -561,19 +559,19 @@ static void automount_enter_runnning(Automount *a) { /* Before we do anything, let's see if somebody is playing games with us? */ if (lstat(a->where, &st) < 0) { - log_warning_unit(UNIT(a)->id, + log_unit_warning(UNIT(a)->id, "%s failed to stat automount point: %m", UNIT(a)->id); goto fail; } if (!S_ISDIR(st.st_mode) || st.st_dev != a->dev_id) - log_info_unit(UNIT(a)->id, + log_unit_info(UNIT(a)->id, "%s's automount point already active?", UNIT(a)->id); else { r = manager_add_job(UNIT(a)->manager, JOB_START, UNIT_TRIGGER(UNIT(a)), JOB_REPLACE, true, &error, NULL); if (r < 0) { - log_warning_unit(UNIT(a)->id, + log_unit_warning(UNIT(a)->id, "%s failed to queue mount startup job: %s", UNIT(a)->id, bus_error_message(&error, r)); goto fail; @@ -594,7 +592,7 @@ static int automount_start(Unit *u) { assert(a->state == AUTOMOUNT_DEAD || a->state == AUTOMOUNT_FAILED); if (path_is_mount_point(a->where, false)) { - log_error_unit(u->id, + log_unit_error(u->id, "Path %s is already a mount point, refusing start for %s", a->where, u->id); return -EEXIST; @@ -659,7 +657,7 @@ static int automount_deserialize_item(Unit *u, const char *key, const char *valu state = automount_state_from_string(value); if (state < 0) - log_debug_unit(u->id, "Failed to parse state value %s", value); + log_unit_debug(u->id, "Failed to parse state value %s", value); else a->deserialized_state = state; } else if (streq(key, "result")) { @@ -667,7 +665,7 @@ static int automount_deserialize_item(Unit *u, const char *key, const char *valu f = automount_result_from_string(value); if (f < 0) - log_debug_unit(u->id, "Failed to parse result value %s", value); + log_unit_debug(u->id, "Failed to parse result value %s", value); else if (f != AUTOMOUNT_SUCCESS) a->result = f; @@ -675,14 +673,14 @@ static int automount_deserialize_item(Unit *u, const char *key, const char *valu unsigned d; if (safe_atou(value, &d) < 0) - log_debug_unit(u->id, "Failed to parse dev-id value %s", value); + log_unit_debug(u->id, "Failed to parse dev-id value %s", value); else a->dev_id = (unsigned) d; } else if (streq(key, "token")) { unsigned token; if (safe_atou(value, &token) < 0) - log_debug_unit(u->id, "Failed to parse token value %s", value); + log_unit_debug(u->id, "Failed to parse token value %s", value); else { if (!a->tokens) if (!(a->tokens = set_new(NULL))) @@ -696,13 +694,13 @@ static int automount_deserialize_item(Unit *u, const char *key, const char *valu int fd; if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) - log_debug_unit(u->id, "Failed to parse pipe-fd value %s", value); + log_unit_debug(u->id, "Failed to parse pipe-fd value %s", value); else { safe_close(a->pipe_fd); a->pipe_fd = fdset_remove(fds, fd); } } else - log_debug_unit(u->id, "Unknown serialization key '%s'", key); + log_unit_debug(u->id, "Unknown serialization key '%s'", key); return 0; } @@ -738,13 +736,16 @@ static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, vo assert(fd == a->pipe_fd); if (events != EPOLLIN) { - log_error_unit(UNIT(a)->id, "Got invalid poll event on pipe."); + log_unit_error(UNIT(a)->id, "Got invalid poll event on pipe."); goto fail; } l = loop_read(a->pipe_fd, &packet, sizeof(packet), true); if (l != sizeof(packet)) { - log_error_unit(UNIT(a)->id, "Invalid read from pipe: %s", l < 0 ? strerror(-l) : "short read"); + if (l < 0) + log_unit_error_errno(UNIT(a)->id, l, "Invalid read from pipe: %m"); + else + log_unit_error(UNIT(a)->id, "Invalid read from pipe: short read"); goto fail; } @@ -756,21 +757,21 @@ static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, vo _cleanup_free_ char *p = NULL; get_process_comm(packet.v5_packet.pid, &p); - log_info_unit(UNIT(a)->id, + log_unit_info(UNIT(a)->id, "Got automount request for %s, triggered by "PID_FMT" (%s)", a->where, packet.v5_packet.pid, strna(p)); } else - log_debug_unit(UNIT(a)->id, "Got direct mount request on %s", a->where); + log_unit_debug(UNIT(a)->id, "Got direct mount request on %s", a->where); r = set_ensure_allocated(&a->tokens, NULL); if (r < 0) { - log_error_unit(UNIT(a)->id, "Failed to allocate token set."); + log_unit_error(UNIT(a)->id, "Failed to allocate token set."); goto fail; } r = set_put(a->tokens, UINT_TO_PTR(packet.v5_packet.wait_queue_token)); if (r < 0) { - log_error_unit(UNIT(a)->id, "Failed to remember token: %s", strerror(-r)); + log_unit_error_errno(UNIT(a)->id, r, "Failed to remember token: %m"); goto fail; } @@ -778,7 +779,7 @@ static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, vo break; default: - log_error_unit(UNIT(a)->id, "Received unknown automount request %i", packet.hdr.type); + log_unit_error(UNIT(a)->id, "Received unknown automount request %i", packet.hdr.type); break; } diff --git a/src/core/build.h b/src/core/build.h index d5e55506cf..24873ab9d7 100644 --- a/src/core/build.h +++ b/src/core/build.h @@ -154,4 +154,4 @@ _BLKID_FEATURE_ " " \ _ELFUTILS_FEATURE_ " " \ _KMOD_FEATURE_ " " \ - _IDN_FEATURE_ " " + _IDN_FEATURE_ diff --git a/src/core/busname.c b/src/core/busname.c index 68cb6ca7b7..99641ec418 100644 --- a/src/core/busname.c +++ b/src/core/busname.c @@ -19,6 +19,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <sys/mman.h> + #include "special.h" #include "bus-kernel.h" #include "bus-internal.h" @@ -26,6 +28,7 @@ #include "service.h" #include "dbus-busname.h" #include "busname.h" +#include "kdbus.h" static const UnitActiveState state_translation_table[_BUSNAME_STATE_MAX] = { [BUSNAME_DEAD] = UNIT_INACTIVE, @@ -197,13 +200,13 @@ static int busname_verify(BusName *n) { return 0; if (!service_name_is_valid(n->name)) { - log_error_unit(UNIT(n)->id, "%s's Name= setting is not a valid service name Refusing.", UNIT(n)->id); + log_unit_error(UNIT(n)->id, "%s's Name= setting is not a valid service name Refusing.", UNIT(n)->id); return -EINVAL; } e = strappenda(n->name, ".busname"); if (!unit_has_name(UNIT(n), e)) { - log_error_unit(UNIT(n)->id, "%s's Name= setting doesn't match unit name. Refusing.", UNIT(n)->id); + log_unit_error(UNIT(n)->id, "%s's Name= setting doesn't match unit name. Refusing.", UNIT(n)->id); return -EINVAL; } @@ -265,7 +268,7 @@ static void busname_unwatch_fd(BusName *n) { r = sd_event_source_set_enabled(n->starter_event_source, SD_EVENT_OFF); if (r < 0) - log_debug_unit(UNIT(n)->id, "Failed to disable event source."); + log_unit_debug(UNIT(n)->id, "Failed to disable event source."); } static int busname_watch_fd(BusName *n) { @@ -281,7 +284,7 @@ static int busname_watch_fd(BusName *n) { else r = sd_event_add_io(UNIT(n)->manager->event, &n->starter_event_source, n->starter_fd, EPOLLIN, busname_dispatch_io, n); if (r < 0) { - log_warning_unit(UNIT(n)->id, "Failed to watch starter fd: %s", strerror(-r)); + log_unit_warning_errno(UNIT(n)->id, r, "Failed to watch starter fd: %m"); busname_unwatch_fd(n); return r; } @@ -300,11 +303,8 @@ static int busname_open_fd(BusName *n) { mode = UNIT(n)->manager->running_as == SYSTEMD_SYSTEM ? "system" : "user"; n->starter_fd = bus_kernel_open_bus_fd(mode, &path); - if (n->starter_fd < 0) { - log_warning_unit(UNIT(n)->id, "Failed to open %s: %s", - path ?: "kdbus", strerror(-n->starter_fd)); - return n->starter_fd; - } + if (n->starter_fd < 0) + return log_unit_warning_errno(UNIT(n)->id, n->starter_fd, "Failed to open %s: %m", path ?: "kdbus"); return 0; } @@ -328,7 +328,7 @@ static void busname_set_state(BusName *n, BusNameState state) { busname_close_fd(n); if (state != old_state) - log_debug_unit(UNIT(n)->id, "%s changed %s -> %s", + log_unit_debug(UNIT(n)->id, "%s changed %s -> %s", UNIT(n)->id, busname_state_to_string(old_state), busname_state_to_string(state)); unit_notify(UNIT(n), state_translation_table[old_state], state_translation_table[state], true); @@ -407,7 +407,7 @@ static int busname_make_starter(BusName *n, pid_t *_pid) { fail_child: log_open(); - log_error("Failed to create starter connection at step %s: %s", exit_status_to_string(ret, EXIT_STATUS_SYSTEMD), strerror(-r)); + log_error_errno(r, "Failed to create starter connection at step %s: %m", exit_status_to_string(ret, EXIT_STATUS_SYSTEMD)); _exit(ret); } @@ -451,14 +451,14 @@ static void busname_enter_signal(BusName *n, BusNameState state, BusNameResult f n->control_pid, false); if (r < 0) { - log_warning_unit(UNIT(n)->id, "%s failed to kill control process: %s", UNIT(n)->id, strerror(-r)); + log_unit_warning_errno(UNIT(n)->id, r, "%s failed to kill control process: %m", UNIT(n)->id); goto fail; } if (r > 0) { r = busname_arm_timer(n); if (r < 0) { - log_warning_unit(UNIT(n)->id, "%s failed to arm timer: %s", UNIT(n)->id, strerror(-r)); + log_unit_warning_errno(UNIT(n)->id, r, "%s failed to arm timer: %m", UNIT(n)->id); goto fail; } @@ -482,7 +482,7 @@ static void busname_enter_listening(BusName *n) { if (n->activating) { r = busname_watch_fd(n); if (r < 0) { - log_warning_unit(UNIT(n)->id, "%s failed to watch names: %s", UNIT(n)->id, strerror(-r)); + log_unit_warning_errno(UNIT(n)->id, r, "%s failed to watch names: %m", UNIT(n)->id); goto fail; } @@ -513,7 +513,7 @@ static void busname_enter_making(BusName *n) { r = busname_make_starter(n, &n->control_pid); if (r < 0) { - log_warning_unit(UNIT(n)->id, "%s failed to fork 'making' task: %s", UNIT(n)->id, strerror(-r)); + log_unit_warning_errno(UNIT(n)->id, r, "%s failed to fork 'making' task: %m", UNIT(n)->id); goto fail; } @@ -524,7 +524,7 @@ static void busname_enter_making(BusName *n) { r = bus_kernel_make_starter(n->starter_fd, n->name, n->activating, n->accept_fd, NULL, n->policy_world); if (r < 0) { - log_warning_unit(UNIT(n)->id, "%s failed to make starter: %s", UNIT(n)->id, strerror(-r)); + log_unit_warning_errno(UNIT(n)->id, r, "%s failed to make starter: %m", UNIT(n)->id); goto fail; } @@ -553,7 +553,7 @@ static void busname_enter_running(BusName *n) { * shut down anyway */ if (unit_stop_pending(UNIT(n))) { - log_debug_unit(UNIT(n)->id, "Suppressing activation request on %s since unit stop is scheduled.", UNIT(n)->id); + log_unit_debug(UNIT(n)->id, "Suppressing activation request on %s since unit stop is scheduled.", UNIT(n)->id); /* Flush all queued activation reqeuest by closing and reopening the connection */ bus_kernel_drop_one(n->starter_fd); @@ -580,7 +580,7 @@ static void busname_enter_running(BusName *n) { return; fail: - log_warning_unit(UNIT(n)->id, "%s failed to queue service startup job: %s", UNIT(n)->id, bus_error_message(&error, r)); + log_unit_warning(UNIT(n)->id, "%s failed to queue service startup job: %s", UNIT(n)->id, bus_error_message(&error, r)); busname_enter_dead(n, BUSNAME_FAILURE_RESOURCES); } @@ -604,7 +604,7 @@ static int busname_start(Unit *u) { service = SERVICE(UNIT_DEREF(n->service)); if (UNIT(service)->load_state != UNIT_LOADED) { - log_error_unit(u->id, "Bus service %s not loaded, refusing.", UNIT(service)->id); + log_unit_error(u->id, "Bus service %s not loaded, refusing.", UNIT(service)->id); return -ENOENT; } } @@ -678,7 +678,7 @@ static int busname_deserialize_item(Unit *u, const char *key, const char *value, state = busname_state_from_string(value); if (state < 0) - log_debug_unit(u->id, "Failed to parse state value %s", value); + log_unit_debug(u->id, "Failed to parse state value %s", value); else n->deserialized_state = state; @@ -687,7 +687,7 @@ static int busname_deserialize_item(Unit *u, const char *key, const char *value, f = busname_result_from_string(value); if (f < 0) - log_debug_unit(u->id, "Failed to parse result value %s", value); + log_unit_debug(u->id, "Failed to parse result value %s", value); else if (f != BUSNAME_SUCCESS) n->result = f; @@ -695,20 +695,20 @@ static int busname_deserialize_item(Unit *u, const char *key, const char *value, pid_t pid; if (parse_pid(value, &pid) < 0) - log_debug_unit(u->id, "Failed to parse control-pid value %s", value); + log_unit_debug(u->id, "Failed to parse control-pid value %s", value); else n->control_pid = pid; } else if (streq(key, "starter-fd")) { int fd; if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) - log_debug_unit(u->id, "Failed to parse starter fd value %s", value); + log_unit_debug(u->id, "Failed to parse starter fd value %s", value); else { safe_close(n->starter_fd); n->starter_fd = fdset_remove(fds, fd); } } else - log_debug_unit(u->id, "Unknown serialization key '%s'", key); + log_unit_debug(u->id, "Unknown serialization key '%s'", key); return 0; } @@ -725,6 +725,88 @@ _pure_ static const char *busname_sub_state_to_string(Unit *u) { return busname_state_to_string(BUSNAME(u)->state); } +static int busname_peek_message(BusName *n) { + struct kdbus_cmd_recv cmd_recv = { + .flags = KDBUS_RECV_PEEK, + }; + struct kdbus_cmd_free cmd_free = {}; + const char *comm = NULL; + struct kdbus_item *d; + struct kdbus_msg *k; + size_t start, ps, sz, delta; + void *p = NULL; + pid_t pid = 0; + int r; + + /* Generate a friendly debug log message about which process + * caused triggering of this bus name. This simply peeks the + * metadata of the first queued message and logs it. */ + + assert(n); + + /* Let's shortcut things a bit, if debug logging is turned off + * anyway. */ + + if (log_get_max_level() < LOG_DEBUG) + return 0; + + r = ioctl(n->starter_fd, KDBUS_CMD_MSG_RECV, &cmd_recv); + if (r < 0) { + if (errno == EINTR || errno == EAGAIN) + return 0; + + log_unit_error(UNIT(n)->id, "%s: Failed to query activation message: %m", UNIT(n)->id); + return -errno; + } + + /* We map as late as possible, and unmap imemdiately after + * use. On 32bit address space is scarce and we want to be + * able to handle a lot of activator connections at the same + * time, and hence shouldn't keep the mmap()s around for + * longer than necessary. */ + + ps = page_size(); + start = (cmd_recv.offset / ps) * ps; + delta = cmd_recv.offset - start; + sz = PAGE_ALIGN(delta + cmd_recv.msg_size); + + p = mmap(NULL, sz, PROT_READ, MAP_SHARED, n->starter_fd, start); + if (p == MAP_FAILED) { + log_unit_error(UNIT(n)->id, "%s: Failed to map activation message: %m", UNIT(n)->id); + r = -errno; + goto finish; + } + + k = (struct kdbus_msg *) ((uint8_t *) p + delta); + KDBUS_ITEM_FOREACH(d, k, items) { + switch (d->type) { + + case KDBUS_ITEM_PIDS: + pid = d->pids.pid; + break; + + case KDBUS_ITEM_PID_COMM: + comm = d->str; + break; + } + } + + if (pid > 0) + log_unit_debug(UNIT(n)->id, "%s: Activation triggered by process " PID_FMT " (%s)", UNIT(n)->id, pid, strna(comm)); + + r = 0; + +finish: + if (p) + (void) munmap(p, sz); + + cmd_free.offset = cmd_recv.offset; + if (ioctl(n->starter_fd, KDBUS_CMD_FREE, &cmd_free) < 0) + log_unit_warning(UNIT(n)->id, "Failed to free peeked message, ignoring: %m"); + + return r; +} + static int busname_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { BusName *n = userdata; @@ -734,14 +816,15 @@ static int busname_dispatch_io(sd_event_source *source, int fd, uint32_t revents if (n->state != BUSNAME_LISTENING) return 0; - log_debug_unit(UNIT(n)->id, "Activation request on %s", UNIT(n)->id); + log_unit_debug(UNIT(n)->id, "Activation request on %s", UNIT(n)->id); if (revents != EPOLLIN) { - log_error_unit(UNIT(n)->id, "%s: Got unexpected poll event (0x%x) on starter fd.", + log_unit_error(UNIT(n)->id, "%s: Got unexpected poll event (0x%x) on starter fd.", UNIT(n)->id, revents); goto fail; } + busname_peek_message(n); busname_enter_running(n); return 0; fail: @@ -773,8 +856,9 @@ static void busname_sigchld_event(Unit *u, pid_t pid, int code, int status) { else assert_not_reached("Unknown sigchld code"); - log_full_unit(f == BUSNAME_SUCCESS ? LOG_DEBUG : LOG_NOTICE, - u->id, "%s control process exited, code=%s status=%i", + log_unit_full(u->id, + f == BUSNAME_SUCCESS ? LOG_DEBUG : LOG_NOTICE, + "%s control process exited, code=%s status=%i", u->id, sigchld_code_to_string(code), status); if (f != BUSNAME_SUCCESS) @@ -811,17 +895,17 @@ static int busname_dispatch_timer(sd_event_source *source, usec_t usec, void *us switch (n->state) { case BUSNAME_MAKING: - log_warning_unit(UNIT(n)->id, "%s making timed out. Terminating.", UNIT(n)->id); + log_unit_warning(UNIT(n)->id, "%s making timed out. Terminating.", UNIT(n)->id); busname_enter_signal(n, BUSNAME_SIGTERM, BUSNAME_FAILURE_TIMEOUT); break; case BUSNAME_SIGTERM: - log_warning_unit(UNIT(n)->id, "%s stopping timed out. Killing.", UNIT(n)->id); + log_unit_warning(UNIT(n)->id, "%s stopping timed out. Killing.", UNIT(n)->id); busname_enter_signal(n, BUSNAME_SIGKILL, BUSNAME_FAILURE_TIMEOUT); break; case BUSNAME_SIGKILL: - log_warning_unit(UNIT(n)->id, "%s still around after SIGKILL. Ignoring.", UNIT(n)->id); + log_unit_warning(UNIT(n)->id, "%s still around after SIGKILL. Ignoring.", UNIT(n)->id); busname_enter_dead(n, BUSNAME_FAILURE_TIMEOUT); break; diff --git a/src/core/cgroup.c b/src/core/cgroup.c index e604c3cbc6..35b862d5c2 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -105,7 +105,8 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { "%sBlockIOWeight=%lu\n" "%sStartupBlockIOWeight=%lu\n" "%sMemoryLimit=%" PRIu64 "\n" - "%sDevicePolicy=%s\n", + "%sDevicePolicy=%s\n" + "%sDelegate=%s\n", prefix, yes_no(c->cpu_accounting), prefix, yes_no(c->blockio_accounting), prefix, yes_no(c->memory_accounting), @@ -115,7 +116,8 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { prefix, c->blockio_weight, prefix, c->startup_blockio_weight, prefix, c->memory_limit, - prefix, cgroup_device_policy_to_string(c->device_policy)); + prefix, cgroup_device_policy_to_string(c->device_policy), + prefix, yes_no(c->delegate)); LIST_FOREACH(device_allow, a, c->device_allow) fprintf(f, @@ -151,10 +153,8 @@ static int lookup_blkio_device(const char *p, dev_t *dev) { assert(dev); r = stat(p, &st); - if (r < 0) { - log_warning("Couldn't stat device %s: %m", p); - return -errno; - } + if (r < 0) + return log_warning_errno(errno, "Couldn't stat device %s: %m", p); if (S_ISBLK(st.st_mode)) *dev = st.st_rdev; @@ -216,10 +216,8 @@ static int whitelist_major(const char *path, const char *name, char type, const assert(type == 'b' || type == 'c'); f = fopen("/proc/devices", "re"); - if (!f) { - log_warning("Cannot open /proc/devices to resolve %s (%c): %m", name, type); - return -errno; - } + if (!f) + return log_warning_errno(errno, "Cannot open /proc/devices to resolve %s (%c): %m", name, type); FOREACH_LINE(line, f, goto fail) { char buf[2+DECIMAL_STR_MAX(unsigned)+3+4], *p, *w; @@ -278,7 +276,7 @@ static int whitelist_major(const char *path, const char *name, char type, const return 0; fail: - log_warning("Failed to read /proc/devices: %m"); + log_warning_errno(errno, "Failed to read /proc/devices: %m"); return -errno; } @@ -461,7 +459,8 @@ CGroupControllerMask cgroup_context_get_mask(CGroupContext *c) { c->memory_limit != (uint64_t) -1) mask |= CGROUP_MEMORY; - if (c->device_allow || c->device_policy != CGROUP_AUTO) + if (c->device_allow || + c->device_policy != CGROUP_AUTO) mask |= CGROUP_DEVICE; return mask; @@ -474,6 +473,19 @@ CGroupControllerMask unit_get_cgroup_mask(Unit *u) { if (!c) return 0; + /* If delegation is turned on, then turn on all cgroups, + * unless the process we fork into it is known to drop + * privileges anyway, and shouldn't get access to the + * controllers anyway. */ + + if (c->delegate) { + ExecContext *e; + + e = unit_get_exec_context(u); + if (!e || exec_context_maintains_privileges(e)) + return _CGROUP_CONTROLLER_MASK_ALL; + } + return cgroup_context_get_mask(c); } @@ -593,40 +605,66 @@ static const char *migrate_callback(CGroupControllerMask mask, void *userdata) { } static int unit_create_cgroups(Unit *u, CGroupControllerMask mask) { - _cleanup_free_ char *path = NULL; + CGroupContext *c; int r; assert(u); - path = unit_default_cgroup_path(u); - if (!path) - return log_oom(); + c = unit_get_cgroup_context(u); + if (!c) + return 0; - r = hashmap_put(u->manager->cgroup_unit, path, u); - if (r < 0) { - log_error(r == -EEXIST ? "cgroup %s exists already: %s" : "hashmap_put failed for %s: %s", path, strerror(-r)); - return r; - } - if (r > 0) { - u->cgroup_path = path; - path = NULL; + if (!u->cgroup_path) { + _cleanup_free_ char *path = NULL; + + path = unit_default_cgroup_path(u); + if (!path) + return log_oom(); + + r = hashmap_put(u->manager->cgroup_unit, path, u); + if (r < 0) { + log_error(r == -EEXIST ? "cgroup %s exists already: %s" : "hashmap_put failed for %s: %s", path, strerror(-r)); + return r; + } + if (r > 0) { + u->cgroup_path = path; + path = NULL; + } } /* First, create our own group */ r = cg_create_everywhere(u->manager->cgroup_supported, mask, u->cgroup_path); - if (r < 0) { - log_error("Failed to create cgroup %s: %s", u->cgroup_path, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to create cgroup %s: %m", u->cgroup_path); /* Keep track that this is now realized */ u->cgroup_realized = true; u->cgroup_realized_mask = mask; - /* Then, possibly move things over */ - r = cg_migrate_everywhere(u->manager->cgroup_supported, u->cgroup_path, u->cgroup_path, migrate_callback, u); + if (u->type != UNIT_SLICE && !c->delegate) { + + /* Then, possibly move things over, but not if + * subgroups may contain processes, which is the case + * for slice and delegation units. */ + r = cg_migrate_everywhere(u->manager->cgroup_supported, u->cgroup_path, u->cgroup_path, migrate_callback, u); + if (r < 0) + log_warning_errno(r, "Failed to migrate cgroup from to %s: %m", u->cgroup_path); + } + + return 0; +} + +int unit_attach_pids_to_cgroup(Unit *u) { + int r; + assert(u); + + r = unit_realize_cgroup(u); if (r < 0) - log_warning("Failed to migrate cgroup from to %s: %s", u->cgroup_path, strerror(-r)); + return r; + + r = cg_attach_many_everywhere(u->manager->cgroup_supported, u->cgroup_path, u->pids, migrate_callback, u); + if (r < 0) + return r; return 0; } @@ -699,7 +737,7 @@ unsigned manager_dispatch_cgroup_queue(Manager *m) { r = unit_realize_cgroup_now(i, state); if (r < 0) - log_warning("Failed to realize cgroups for queued unit %s: %s", i->id, strerror(-r)); + log_warning_errno(r, "Failed to realize cgroups for queued unit %s: %m", i->id); n++; } @@ -772,7 +810,7 @@ int unit_realize_cgroup(Unit *u) { return unit_realize_cgroup_now(u, manager_state(u->manager)); } -void unit_destroy_cgroup(Unit *u) { +void unit_destroy_cgroup_if_empty(Unit *u) { int r; assert(u); @@ -781,8 +819,10 @@ void unit_destroy_cgroup(Unit *u) { return; r = cg_trim_everywhere(u->manager->cgroup_supported, u->cgroup_path, !unit_has_name(u, SPECIAL_ROOT_SLICE)); - if (r < 0) - log_debug("Failed to destroy cgroup %s: %s", u->cgroup_path, strerror(-r)); + if (r < 0) { + log_debug_errno(r, "Failed to destroy cgroup %s: %m", u->cgroup_path); + return; + } hashmap_remove(u->manager->cgroup_unit, u->cgroup_path); @@ -790,7 +830,6 @@ void unit_destroy_cgroup(Unit *u) { u->cgroup_path = NULL; u->cgroup_realized = false; u->cgroup_realized_mask = 0; - } pid_t unit_search_main_pid(Unit *u) { @@ -841,10 +880,8 @@ int manager_setup_cgroup(Manager *m) { m->cgroup_root = NULL; r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &m->cgroup_root); - if (r < 0) { - log_error("Cannot determine cgroup we are running in: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Cannot determine cgroup we are running in: %m"); /* LEGACY: Already in /system.slice? If so, let's cut this * off. This is to support live upgrades from older systemd @@ -867,10 +904,8 @@ int manager_setup_cgroup(Manager *m) { /* 2. Show data */ r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, NULL, &path); - if (r < 0) { - log_error("Cannot find cgroup mount point: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Cannot find cgroup mount point: %m"); log_debug("Using cgroup controller " SYSTEMD_CGROUP_CONTROLLER ". File system hierarchy is at %s.", path); if (!m->test_run) { @@ -879,7 +914,7 @@ int manager_setup_cgroup(Manager *m) { if (m->running_as == SYSTEMD_SYSTEM) { r = cg_install_release_agent(SYSTEMD_CGROUP_CONTROLLER, SYSTEMD_CGROUP_AGENT_PATH); if (r < 0) - log_warning("Failed to install release agent, ignoring: %s", strerror(-r)); + log_warning_errno(r, "Failed to install release agent, ignoring: %m"); else if (r > 0) log_debug("Installed release agent."); else @@ -888,19 +923,15 @@ int manager_setup_cgroup(Manager *m) { /* 4. Make sure we are in the root cgroup */ r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, 0); - if (r < 0) { - log_error("Failed to create root cgroup hierarchy: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to create root cgroup hierarchy: %m"); /* 5. And pin it, so that it cannot be unmounted */ safe_close(m->pin_cgroupfs_fd); m->pin_cgroupfs_fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY|O_NONBLOCK); - if (m->pin_cgroupfs_fd < 0) { - log_error("Failed to open pin file: %m"); - return -errno; - } + if (m->pin_cgroupfs_fd < 0) + return log_error_errno(errno, "Failed to open pin file: %m"); /* 6. Always enable hierarchial support if it exists... */ cg_set_attribute("memory", "/", "memory.use_hierarchy", "1"); diff --git a/src/core/cgroup.h b/src/core/cgroup.h index d299872b1f..7150e5e7e2 100644 --- a/src/core/cgroup.h +++ b/src/core/cgroup.h @@ -83,6 +83,8 @@ struct CGroupContext { CGroupDevicePolicy device_policy; LIST_HEAD(CGroupDeviceAllow, device_allow); + + bool delegate; }; #include "unit.h" @@ -107,7 +109,8 @@ CGroupControllerMask unit_get_target_mask(Unit *u); void unit_update_cgroup_members_masks(Unit *u); int unit_realize_cgroup(Unit *u); -void unit_destroy_cgroup(Unit *u); +void unit_destroy_cgroup_if_empty(Unit *u); +int unit_attach_pids_to_cgroup(Unit *u); int manager_setup_cgroup(Manager *m); void manager_shutdown_cgroup(Manager *m, bool delete); diff --git a/src/core/condition.c b/src/core/condition.c deleted file mode 100644 index ec78169fc3..0000000000 --- a/src/core/condition.c +++ /dev/null @@ -1,264 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2010 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 <stdlib.h> -#include <errno.h> -#include <string.h> -#include <unistd.h> -#include <sys/capability.h> -#include <sys/statvfs.h> -#include <fnmatch.h> - -#include "sd-id128.h" -#include "util.h" -#include "condition.h" -#include "virt.h" -#include "path-util.h" -#include "fileio.h" -#include "unit.h" -#include "smack-util.h" -#include "apparmor-util.h" -#include "ima-util.h" -#include "selinux-util.h" - -static bool condition_test_security(Condition *c) { - assert(c); - assert(c->parameter); - assert(c->type == CONDITION_SECURITY); - - if (streq(c->parameter, "selinux")) - return mac_selinux_use() == !c->negate; - if (streq(c->parameter, "smack")) - return mac_smack_use() == !c->negate; - if (streq(c->parameter, "apparmor")) - return mac_apparmor_use() == !c->negate; - if (streq(c->parameter, "ima")) - return use_ima() == !c->negate; - - return c->negate; -} - -static bool condition_test_capability(Condition *c) { - _cleanup_fclose_ FILE *f = NULL; - cap_value_t value; - char line[LINE_MAX]; - unsigned long long capabilities = -1; - - assert(c); - assert(c->parameter); - assert(c->type == CONDITION_CAPABILITY); - - /* If it's an invalid capability, we don't have it */ - - if (cap_from_name(c->parameter, &value) < 0) - return c->negate; - - /* If it's a valid capability we default to assume - * that we have it */ - - f = fopen("/proc/self/status", "re"); - if (!f) - return !c->negate; - - while (fgets(line, sizeof(line), f)) { - truncate_nl(line); - - if (startswith(line, "CapBnd:")) { - (void) sscanf(line+7, "%llx", &capabilities); - break; - } - } - - return !!(capabilities & (1ULL << value)) == !c->negate; -} - -static bool condition_test_needs_update(Condition *c) { - const char *p; - struct stat usr, other; - - assert(c); - assert(c->parameter); - assert(c->type == CONDITION_NEEDS_UPDATE); - - /* If the file system is read-only we shouldn't suggest an update */ - if (path_is_read_only_fs(c->parameter) > 0) - return c->negate; - - /* Any other failure means we should allow the condition to be true, - * so that we rather invoke too many update tools then too - * few. */ - - if (!path_is_absolute(c->parameter)) - return !c->negate; - - p = strappenda(c->parameter, "/.updated"); - if (lstat(p, &other) < 0) - return !c->negate; - - if (lstat("/usr/", &usr) < 0) - return !c->negate; - - return (usr.st_mtim.tv_sec > other.st_mtim.tv_sec || - (usr.st_mtim.tv_sec == other.st_mtim.tv_sec && usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec)) == !c->negate; -} - -static bool condition_test_first_boot(Condition *c) { - int r; - - assert(c); - assert(c->parameter); - assert(c->type == CONDITION_FIRST_BOOT); - - r = parse_boolean(c->parameter); - if (r < 0) - return c->negate; - - return ((access("/run/systemd/first-boot", F_OK) >= 0) == !!r) == !c->negate; -} - -static bool condition_test(Condition *c) { - assert(c); - - switch(c->type) { - - case CONDITION_PATH_EXISTS: - return (access(c->parameter, F_OK) >= 0) == !c->negate; - - case CONDITION_PATH_EXISTS_GLOB: - return (glob_exists(c->parameter) > 0) == !c->negate; - - case CONDITION_PATH_IS_DIRECTORY: { - struct stat st; - - if (stat(c->parameter, &st) < 0) - return c->negate; - return S_ISDIR(st.st_mode) == !c->negate; - } - - case CONDITION_PATH_IS_SYMBOLIC_LINK: { - struct stat st; - - if (lstat(c->parameter, &st) < 0) - return c->negate; - return S_ISLNK(st.st_mode) == !c->negate; - } - - case CONDITION_PATH_IS_MOUNT_POINT: - return (path_is_mount_point(c->parameter, true) > 0) == !c->negate; - - case CONDITION_PATH_IS_READ_WRITE: - return (path_is_read_only_fs(c->parameter) > 0) == c->negate; - - case CONDITION_DIRECTORY_NOT_EMPTY: { - int k; - - k = dir_is_empty(c->parameter); - return !(k == -ENOENT || k > 0) == !c->negate; - } - - case CONDITION_FILE_NOT_EMPTY: { - struct stat st; - - if (stat(c->parameter, &st) < 0) - return c->negate; - - return (S_ISREG(st.st_mode) && st.st_size > 0) == !c->negate; - } - - case CONDITION_FILE_IS_EXECUTABLE: { - struct stat st; - - if (stat(c->parameter, &st) < 0) - return c->negate; - - return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate; - } - - case CONDITION_KERNEL_COMMAND_LINE: - return condition_test_kernel_command_line(c); - - case CONDITION_VIRTUALIZATION: - return condition_test_virtualization(c); - - case CONDITION_SECURITY: - return condition_test_security(c); - - case CONDITION_CAPABILITY: - return condition_test_capability(c); - - case CONDITION_HOST: - return condition_test_host(c); - - case CONDITION_AC_POWER: - return condition_test_ac_power(c); - - case CONDITION_ARCHITECTURE: - return condition_test_architecture(c); - - case CONDITION_NEEDS_UPDATE: - return condition_test_needs_update(c); - - case CONDITION_FIRST_BOOT: - return condition_test_first_boot(c); - - case CONDITION_NULL: - return !c->negate; - - default: - assert_not_reached("Invalid condition type."); - } -} - -bool condition_test_list(const char *unit, Condition *first) { - Condition *c; - int triggered = -1; - - /* If the condition list is empty, then it is true */ - if (!first) - return true; - - /* Otherwise, if all of the non-trigger conditions apply and - * if any of the trigger conditions apply (unless there are - * none) we return true */ - LIST_FOREACH(conditions, c, first) { - bool b; - - b = condition_test(c); - if (unit) - log_debug_unit(unit, - "%s=%s%s%s %s for %s.", - condition_type_to_string(c->type), - c->trigger ? "|" : "", - c->negate ? "!" : "", - c->parameter, - b ? "succeeded" : "failed", - unit); - c->state = b ? 1 : -1; - - if (!c->trigger && !b) - return false; - - if (c->trigger && triggered <= 0) - triggered = b; - } - - return triggered != 0; -} diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c index 900566c29b..db998345eb 100644 --- a/src/core/dbus-cgroup.c +++ b/src/core/dbus-cgroup.c @@ -153,6 +153,7 @@ static int property_get_ulong_as_u64( const sd_bus_vtable bus_cgroup_vtable[] = { SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("Delegate", "b", bus_property_get_bool, offsetof(CGroupContext, delegate), 0), SD_BUS_PROPERTY("CPUAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, cpu_accounting), 0), SD_BUS_PROPERTY("CPUShares", "t", property_get_ulong_as_u64, offsetof(CGroupContext, cpu_shares), 0), SD_BUS_PROPERTY("StartupCPUShares", "t", property_get_ulong_as_u64, offsetof(CGroupContext, startup_cpu_shares), 0), @@ -170,6 +171,39 @@ const sd_bus_vtable bus_cgroup_vtable[] = { SD_BUS_VTABLE_END }; +static int bus_cgroup_set_transient_property( + Unit *u, + CGroupContext *c, + const char *name, + sd_bus_message *message, + UnitSetPropertiesMode mode, + sd_bus_error *error) { + + int r; + + assert(u); + assert(c); + assert(name); + assert(message); + + if (streq(name, "Delegate")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + c->delegate = b; + unit_write_drop_in_private(u, mode, name, b ? "Delegate=yes" : "Delegate=no"); + } + + return 1; + } + + return 0; +} + int bus_cgroup_set_property( Unit *u, CGroupContext *c, @@ -632,6 +666,14 @@ int bus_cgroup_set_property( } return 1; + + } + + if (u->transient && u->load_state == UNIT_STUB) { + r = bus_cgroup_set_transient_property(u, c, name, message, mode, error); + if (r != 0) + return r; + } return 0; diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 9276da4cdc..bbcd6106ad 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -508,6 +508,24 @@ static int property_get_apparmor_profile( return sd_bus_message_append(reply, "(bs)", c->apparmor_profile_ignore, c->apparmor_profile); } +static int property_get_smack_process_label( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + ExecContext *c = userdata; + + assert(bus); + assert(reply); + assert(c); + + return sd_bus_message_append(reply, "(bs)", c->smack_process_label_ignore, c->smack_process_label); +} + static int property_get_personality( sd_bus *bus, const char *path, @@ -636,6 +654,7 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("UtmpIdentifier", "s", NULL, offsetof(ExecContext, utmp_id), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SELinuxContext", "(bs)", property_get_selinux_context, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("AppArmorProfile", "(bs)", property_get_apparmor_profile, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SmackProcessLabel", "(bs)", property_get_smack_process_label, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("IgnoreSIGPIPE", "b", bus_property_get_bool, offsetof(ExecContext, ignore_sigpipe), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("NoNewPrivileges", "b", bus_property_get_bool, offsetof(ExecContext, no_new_privileges), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SystemCallFilter", "(bas)", property_get_syscall_filter, 0, SD_BUS_VTABLE_PROPERTY_CONST), diff --git a/src/core/dbus-job.c b/src/core/dbus-job.c index 09f5739315..8b5ea2566d 100644 --- a/src/core/dbus-job.c +++ b/src/core/dbus-job.c @@ -154,7 +154,7 @@ void bus_job_send_change_signal(Job *j) { r = bus_foreach_bus(j->manager, j->clients, j->sent_dbus_new_signal ? send_changed_signal : send_new_signal, j); if (r < 0) - log_debug("Failed to send job change signal for %u: %s", j->id, strerror(-r)); + log_debug_errno(r, "Failed to send job change signal for %u: %m", j->id); j->sent_dbus_new_signal = true; } @@ -198,5 +198,5 @@ void bus_job_send_removed_signal(Job *j) { r = bus_foreach_bus(j->manager, j->clients, send_removed_signal, j); if (r < 0) - log_debug("Failed to send job remove signal for %u: %s", j->id, strerror(-r)); + log_debug_errno(r, "Failed to send job remove signal for %u: %m", j->id); } diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index c54abd3b4e..6181ba8248 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -39,7 +39,7 @@ #include "dbus-unit.h" #include "dbus-snapshot.h" #include "dbus-execute.h" -#include "bus-errors.h" +#include "bus-common-errors.h" static int property_get_version( sd_bus *bus, @@ -615,6 +615,92 @@ static int method_set_unit_properties(sd_bus *bus, sd_bus_message *message, void return bus_unit_method_set_properties(bus, message, u, error); } +static int transient_unit_from_message( + Manager *m, + sd_bus_message *message, + const char *name, + Unit **unit, + sd_bus_error *error) { + + Unit *u; + int r; + + assert(m); + assert(message); + assert(name); + + r = manager_load_unit(m, name, NULL, error, &u); + if (r < 0) + return r; + + if (u->load_state != UNIT_NOT_FOUND || + set_size(u->dependencies[UNIT_REFERENCED_BY]) > 0) + return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, "Unit %s already exists.", name); + + /* OK, the unit failed to load and is unreferenced, now let's + * fill in the transient data instead */ + r = unit_make_transient(u); + if (r < 0) + return r; + + /* Set our properties */ + r = bus_unit_set_properties(u, message, UNIT_RUNTIME, false, error); + if (r < 0) + return r; + + *unit = u; + + return 0; +} + +static int transient_aux_units_from_message( + Manager *m, + sd_bus_message *message, + sd_bus_error *error) { + + Unit *u; + char *name = NULL; + int r; + + assert(m); + assert(message); + + r = sd_bus_message_enter_container(message, 'a', "(sa(sv))"); + if (r < 0) + return r; + + while ((r = sd_bus_message_enter_container(message, 'r', "sa(sv)")) > 0) { + if (r <= 0) + return r; + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + r = transient_unit_from_message(m, message, name, &u, error); + if (r < 0 && r != -EEXIST) + return r; + + if (r != -EEXIST) { + r = unit_load(u); + if (r < 0) + return r; + } + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + } + if (r < 0) + return r; + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + return 0; +} + static int method_start_transient_unit(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) { const char *name, *smode; Manager *m = userdata; @@ -652,21 +738,11 @@ static int method_start_transient_unit(sd_bus *bus, sd_bus_message *message, voi if (r < 0) return r; - r = manager_load_unit(m, name, NULL, error, &u); + r = transient_unit_from_message(m, message, name, &u, error); if (r < 0) return r; - if (u->load_state != UNIT_NOT_FOUND || set_size(u->dependencies[UNIT_REFERENCED_BY]) > 0) - return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, "Unit %s already exists.", name); - - /* OK, the unit failed to load and is unreferenced, now let's - * fill in the transient data instead */ - r = unit_make_transient(u); - if (r < 0) - return r; - - /* Set our properties */ - r = bus_unit_set_properties(u, message, UNIT_RUNTIME, false, error); + r = transient_aux_units_from_message(m, message, error); if (r < 0) return r; @@ -1514,7 +1590,7 @@ static int reply_unit_file_changes_and_free( if (n_changes > 0) { r = bus_foreach_bus(m, NULL, send_unit_files_changed, NULL); if (r < 0) - log_debug("Failed to send UnitFilesChanged signal: %s", strerror(-r)); + log_debug_errno(r, "Failed to send UnitFilesChanged signal: %m"); } r = sd_bus_message_new_method_return(message, &reply); @@ -1884,8 +1960,8 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_PROPERTY("UnitPath", "as", NULL, offsetof(Manager, lookup_paths.unit_path), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultStandardOutput", "s", bus_property_get_exec_output, offsetof(Manager, default_std_output), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultStandardError", "s", bus_property_get_exec_output, offsetof(Manager, default_std_output), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_WRITABLE_PROPERTY("RuntimeWatchdogUSec", "t", bus_property_get_usec, property_set_runtime_watchdog, offsetof(Manager, runtime_watchdog), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_WRITABLE_PROPERTY("ShutdownWatchdogUSec", "t", bus_property_get_usec, bus_property_set_usec, offsetof(Manager, shutdown_watchdog), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_WRITABLE_PROPERTY("RuntimeWatchdogUSec", "t", bus_property_get_usec, property_set_runtime_watchdog, offsetof(Manager, runtime_watchdog), 0), + SD_BUS_WRITABLE_PROPERTY("ShutdownWatchdogUSec", "t", bus_property_get_usec, bus_property_set_usec, offsetof(Manager, shutdown_watchdog), 0), SD_BUS_PROPERTY("ControlGroup", "s", NULL, offsetof(Manager, cgroup_root), 0), SD_BUS_PROPERTY("SystemState", "s", property_get_system_state, 0, 0), @@ -1998,7 +2074,7 @@ void bus_manager_send_finished( total_usec }); if (r < 0) - log_debug("Failed to send finished signal: %s", strerror(-r)); + log_debug_errno(r, "Failed to send finished signal: %m"); } static int send_reloading(sd_bus *bus, void *userdata) { @@ -2025,6 +2101,6 @@ void bus_manager_send_reloading(Manager *m, bool active) { r = bus_foreach_bus(m, NULL, send_reloading, INT_TO_PTR(active)); if (r < 0) - log_debug("Failed to send reloading signal: %s", strerror(-r)); + log_debug_errno(r, "Failed to send reloading signal: %m"); } diff --git a/src/core/dbus-scope.c b/src/core/dbus-scope.c index a762223c81..60215a1935 100644 --- a/src/core/dbus-scope.c +++ b/src/core/dbus-scope.c @@ -28,7 +28,7 @@ #include "dbus.h" #include "bus-util.h" #include "bus-internal.h" -#include "bus-errors.h" +#include "bus-common-errors.h" static int bus_scope_abandon(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) { Scope *s = userdata; diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c index f1f8c54383..43e785246c 100644 --- a/src/core/dbus-timer.c +++ b/src/core/dbus-timer.c @@ -24,6 +24,8 @@ #include "dbus-unit.h" #include "dbus-timer.h" #include "bus-util.h" +#include "errno-list.h" +#include "strv.h" static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_result, timer_result, TimerResult); @@ -183,3 +185,144 @@ const sd_bus_vtable bus_timer_vtable[] = { SD_BUS_PROPERTY("WakeSystem", "b", bus_property_get_bool, offsetof(Timer, wake_system), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_VTABLE_END }; + +static int bus_timer_set_transient_property( + Timer *t, + const char *name, + sd_bus_message *message, + UnitSetPropertiesMode mode, + sd_bus_error *error) { + + int r; + + assert(t); + assert(name); + assert(message); + + if (STR_IN_SET(name, + "OnActiveSec", + "OnBootSec", + "OnStartupSec", + "OnUnitActiveSec", + "OnUnitInactiveSec")) { + + TimerValue *v; + TimerBase b = _TIMER_BASE_INVALID; + usec_t u = 0; + + b = timer_base_from_string(name); + if (b < 0) + return -EINVAL; + + r = sd_bus_message_read(message, "t", &u); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + char time[FORMAT_TIMESPAN_MAX]; + + unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s\n", name, format_timespan(time, sizeof(time), u, USEC_PER_MSEC)); + + v = new0(TimerValue, 1); + if (!v) + return -ENOMEM; + + v->base = b; + v->value = u; + + LIST_PREPEND(value, t->values, v); + } + + return 1; + + } else if (streq(name, "OnCalendar")) { + + TimerValue *v; + CalendarSpec *c = NULL; + const char *str; + + r = sd_bus_message_read(message, "s", &str); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + r = calendar_spec_from_string(str, &c); + if (r < 0) + return r; + + unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s\n", name, str); + + v = new0(TimerValue, 1); + if (!v) { + if (c) + calendar_spec_free(c); + return -ENOMEM; + } + + v->base = TIMER_CALENDAR; + v->calendar_spec = c; + + LIST_PREPEND(value, t->values, v); + } + + return 1; + + } else if (streq(name, "AccuracySec")) { + + usec_t u = 0; + + r = sd_bus_message_read(message, "t", &u); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + char time[FORMAT_TIMESPAN_MAX]; + + t->accuracy_usec = u; + unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s\n", name, format_timespan(time, sizeof(time), u, USEC_PER_MSEC)); + } + + return 1; + + } else if (streq(name, "WakeSystem")) { + + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + t->wake_system = b; + unit_write_drop_in_private_format(UNIT(t), mode, name, "%s=%s\n", name, yes_no(t->wake_system)); + } + + return 1; + + } + + return 0; +} + +int bus_timer_set_property( + Unit *u, + const char *name, + sd_bus_message *message, + UnitSetPropertiesMode mode, + sd_bus_error *error) { + + Timer *t = TIMER(u); + int r; + + assert(t); + assert(name); + assert(message); + + if (u->transient && u->load_state == UNIT_STUB) { + r = bus_timer_set_transient_property(t, name, message, mode, error); + if (r != 0) + return r; + } + + return 0; +} diff --git a/src/core/dbus-timer.h b/src/core/dbus-timer.h index cfff88e8b7..103172f055 100644 --- a/src/core/dbus-timer.h +++ b/src/core/dbus-timer.h @@ -22,5 +22,8 @@ ***/ #include "sd-bus.h" +#include "unit.h" extern const sd_bus_vtable bus_timer_vtable[]; + +int bus_timer_set_property(Unit *u, const char *name, sd_bus_message *i, UnitSetPropertiesMode mode, sd_bus_error *error); diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 9b13c6ed1b..b968009938 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -26,7 +26,7 @@ #include "strv.h" #include "path-util.h" #include "fileio.h" -#include "bus-errors.h" +#include "bus-common-errors.h" #include "dbus.h" #include "dbus-manager.h" #include "dbus-unit.h" @@ -169,6 +169,29 @@ static int property_get_sub_state( return sd_bus_message_append(reply, "s", unit_sub_state_to_string(u)); } +static int property_get_unit_file_preset( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Unit *u = userdata; + int r; + + assert(bus); + assert(reply); + assert(u); + + r = unit_get_unit_file_preset(u); + + return sd_bus_message_append(reply, "s", + r < 0 ? "": + r > 0 ? "enabled" : "disabled"); +} + static int property_get_unit_file_state( sd_bus *bus, const char *path, @@ -315,23 +338,31 @@ static int property_get_conditions( void *userdata, sd_bus_error *error) { - Unit *u = userdata; - Condition *c; + const char *(*to_string)(ConditionType type) = NULL; + Condition **list = userdata, *c; int r; assert(bus); assert(reply); - assert(u); + assert(list); + + to_string = streq(property, "Asserts") ? assert_type_to_string : condition_type_to_string; r = sd_bus_message_open_container(reply, 'a', "(sbbsi)"); if (r < 0) return r; - LIST_FOREACH(conditions, c, u->conditions) { + LIST_FOREACH(conditions, c, *list) { + int tristate; + + tristate = + c->result == CONDITION_UNTESTED ? 0 : + c->result == CONDITION_SUCCEEDED ? 1 : -1; + r = sd_bus_message_append(reply, "(sbbsi)", - condition_type_to_string(c->type), + to_string(c->type), c->trigger, c->negate, - c->parameter, c->state); + c->parameter, tristate); if (r < 0) return r; @@ -544,6 +575,7 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_PROPERTY("SourcePath", "s", NULL, offsetof(Unit, source_path), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DropInPaths", "as", NULL, offsetof(Unit, dropin_paths), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("UnitFileState", "s", property_get_unit_file_state, 0, 0), + SD_BUS_PROPERTY("UnitFilePreset", "s", property_get_unit_file_preset, 0, 0), BUS_PROPERTY_DUAL_TIMESTAMP("InactiveExitTimestamp", offsetof(Unit, inactive_exit_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), BUS_PROPERTY_DUAL_TIMESTAMP("ActiveEnterTimestamp", offsetof(Unit, active_enter_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), BUS_PROPERTY_DUAL_TIMESTAMP("ActiveExitTimestamp", offsetof(Unit, active_exit_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), @@ -566,8 +598,11 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_PROPERTY("JobTimeoutAction", "s", property_get_failure_action, offsetof(Unit, job_timeout_action), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("JobTimeoutRebootArgument", "s", NULL, offsetof(Unit, job_timeout_reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ConditionResult", "b", bus_property_get_bool, offsetof(Unit, condition_result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("AssertResult", "b", bus_property_get_bool, offsetof(Unit, assert_result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), BUS_PROPERTY_DUAL_TIMESTAMP("ConditionTimestamp", offsetof(Unit, condition_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), - SD_BUS_PROPERTY("Conditions", "a(sbbsi)", property_get_conditions, 0, 0), + BUS_PROPERTY_DUAL_TIMESTAMP("AssertTimestamp", offsetof(Unit, assert_timestamp), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), + SD_BUS_PROPERTY("Conditions", "a(sbbsi)", property_get_conditions, offsetof(Unit, conditions), 0), + SD_BUS_PROPERTY("Asserts", "a(sbbsi)", property_get_conditions, offsetof(Unit, asserts), 0), SD_BUS_PROPERTY("LoadError", "(ss)", property_get_load_error, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("Transient", "b", bus_property_get_bool, offsetof(Unit, transient), SD_BUS_VTABLE_PROPERTY_CONST), @@ -682,7 +717,7 @@ void bus_unit_send_change_signal(Unit *u) { r = bus_foreach_bus(u->manager, NULL, u->sent_dbus_new_signal ? send_changed_signal : send_new_signal, u); if (r < 0) - log_debug("Failed to send unit change signal for %s: %s", u->id, strerror(-r)); + log_debug_errno(r, "Failed to send unit change signal for %s: %m", u->id); u->sent_dbus_new_signal = true; } @@ -728,7 +763,7 @@ void bus_unit_send_removed_signal(Unit *u) { r = bus_foreach_bus(u->manager, NULL, send_removed_signal, u); if (r < 0) - log_debug("Failed to send unit remove signal for %s: %s", u->id, strerror(-r)); + log_debug_errno(r, "Failed to send unit remove signal for %s: %m", u->id); } int bus_unit_queue_job( @@ -859,20 +894,16 @@ static int bus_unit_set_transient_property( } return 1; - - } else if (streq(name, "Requires") || - streq(name, "RequiresOverridable") || - streq(name, "Requisite") || - streq(name, "RequisiteOverridable") || - streq(name, "Wants") || - streq(name, "BindsTo") || - streq(name, "Conflicts") || - streq(name, "Before") || - streq(name, "After") || - streq(name, "OnFailure") || - streq(name, "PropagatesReloadTo") || - streq(name, "ReloadPropagatedFrom") || - streq(name, "PartOf")) { + } else if (STR_IN_SET(name, + "Requires", "RequiresOverridable", + "Requisite", "RequisiteOverridable", + "Wants", + "BindsTo", + "Conflicts", + "Before", "After", + "OnFailure", + "PropagatesReloadTo", "ReloadPropagatedFrom", + "PartOf")) { UnitDependency d; const char *other; diff --git a/src/core/dbus.c b/src/core/dbus.c index 185057b624..260775cd85 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -39,7 +39,7 @@ #include "dbus.h" #include "bus-util.h" #include "bus-error.h" -#include "bus-errors.h" +#include "bus-common-errors.h" #include "strxcpyx.h" #include "bus-internal.h" #include "selinux-access.h" @@ -64,7 +64,7 @@ int bus_send_queued_message(Manager *m) { r = sd_bus_send(m->queued_message_bus, m->queued_message, NULL); if (r < 0) - log_warning("Failed to send queued message: %s", strerror(-r)); + log_warning_errno(r, "Failed to send queued message: %m"); m->queued_message = sd_bus_message_unref(m->queued_message); m->queued_message_bus = sd_bus_unref(m->queued_message_bus); @@ -95,7 +95,7 @@ static int signal_agent_released(sd_bus *bus, sd_bus_message *message, void *use r = sd_bus_send(m->system_bus, message, NULL); if (r < 0) - log_warning("Failed to forward Released message: %s", strerror(-r)); + log_warning_errno(r, "Failed to forward Released message: %m"); } return 0; @@ -202,10 +202,8 @@ failed: } r = sd_bus_send_to(bus, reply, "org.freedesktop.DBus", NULL); - if (r < 0) { - log_error("Failed to respond with to bus activation request: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to respond with to bus activation request: %m"); return 0; } @@ -537,77 +535,55 @@ static int bus_setup_api_vtables(Manager *m, sd_bus *bus) { #ifdef HAVE_SELINUX r = sd_bus_add_filter(bus, NULL, mac_selinux_filter, m); - if (r < 0) { - log_error("Failed to add SELinux access filter: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add SELinux access filter: %m"); #endif r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", bus_manager_vtable, m); - if (r < 0) { - log_error("Failed to register Manager vtable: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to register Manager vtable: %m"); r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/job", "org.freedesktop.systemd1.Job", bus_job_vtable, bus_job_find, m); - if (r < 0) { - log_error("Failed to register Job vtable: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to register Job vtable: %m"); r = sd_bus_add_node_enumerator(bus, NULL, "/org/freedesktop/systemd1/job", bus_job_enumerate, m); - if (r < 0) { - log_error("Failed to add job enumerator: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add job enumerator: %m"); r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", "org.freedesktop.systemd1.Unit", bus_unit_vtable, bus_unit_find, m); - if (r < 0) { - log_error("Failed to register Unit vtable: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to register Unit vtable: %m"); r = sd_bus_add_node_enumerator(bus, NULL, "/org/freedesktop/systemd1/unit", bus_unit_enumerate, m); - if (r < 0) { - log_error("Failed to add job enumerator: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add job enumerator: %m"); for (t = 0; t < _UNIT_TYPE_MAX; t++) { r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, unit_vtable[t]->bus_vtable, bus_unit_interface_find, m); - if (r < 0) { - log_error("Failed to register type specific vtable for %s: %s", unit_vtable[t]->bus_interface, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to register type specific vtable for %s: %m", unit_vtable[t]->bus_interface); if (unit_vtable[t]->cgroup_context_offset > 0) { r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, bus_unit_cgroup_vtable, bus_unit_cgroup_find, m); - if (r < 0) { - log_error("Failed to register control group unit vtable for %s: %s", unit_vtable[t]->bus_interface, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to register control group unit vtable for %s: %m", unit_vtable[t]->bus_interface); r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, bus_cgroup_vtable, bus_cgroup_context_find, m); - if (r < 0) { - log_error("Failed to register control group vtable for %s: %s", unit_vtable[t]->bus_interface, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to register control group vtable for %s: %m", unit_vtable[t]->bus_interface); } if (unit_vtable[t]->exec_context_offset > 0) { r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, bus_exec_vtable, bus_exec_context_find, m); - if (r < 0) { - log_error("Failed to register execute vtable for %s: %s", unit_vtable[t]->bus_interface, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to register execute vtable for %s: %m", unit_vtable[t]->bus_interface); } if (unit_vtable[t]->kill_context_offset > 0) { r = sd_bus_add_fallback_vtable(bus, NULL, "/org/freedesktop/systemd1/unit", unit_vtable[t]->bus_interface, bus_kill_vtable, bus_kill_context_find, m); - if (r < 0) { - log_error("Failed to register kill vtable for %s: %s", unit_vtable[t]->bus_interface, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to register kill vtable for %s: %m", unit_vtable[t]->bus_interface); } } @@ -630,10 +606,8 @@ static int bus_setup_disconnected_match(Manager *m, sd_bus *bus) { "member='Disconnected'", signal_disconnected, m); - if (r < 0) { - log_error("Failed to register match for Disconnected message: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to register match for Disconnected message: %m"); return 0; } @@ -650,7 +624,7 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void nfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); if (nfd < 0) { - log_warning("Failed to accept private connection, ignoring: %m"); + log_warning_errno(errno, "Failed to accept private connection, ignoring: %m"); return 0; } @@ -667,13 +641,13 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void r = sd_bus_new(&bus); if (r < 0) { - log_warning("Failed to allocate new private connection bus: %s", strerror(-r)); + log_warning_errno(r, "Failed to allocate new private connection bus: %m"); return 0; } r = sd_bus_set_fd(bus, nfd, nfd); if (r < 0) { - log_warning("Failed to set fd on new connection bus: %s", strerror(-r)); + log_warning_errno(r, "Failed to set fd on new connection bus: %m"); return 0; } @@ -681,7 +655,7 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void r = bus_check_peercred(bus); if (r < 0) { - log_warning("Incoming private connection from unprivileged client, refusing: %s", strerror(-r)); + log_warning_errno(r, "Incoming private connection from unprivileged client, refusing: %m"); return 0; } @@ -689,19 +663,19 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void r = sd_bus_set_server(bus, 1, id); if (r < 0) { - log_warning("Failed to enable server support for new connection bus: %s", strerror(-r)); + log_warning_errno(r, "Failed to enable server support for new connection bus: %m"); return 0; } r = sd_bus_start(bus); if (r < 0) { - log_warning("Failed to start new connection bus: %s", strerror(-r)); + log_warning_errno(r, "Failed to start new connection bus: %m"); return 0; } r = sd_bus_attach_event(bus, m->event, SD_EVENT_PRIORITY_NORMAL); if (r < 0) { - log_warning("Failed to attach new connection bus to event loop: %s", strerror(-r)); + log_warning_errno(r, "Failed to attach new connection bus to event loop: %m"); return 0; } @@ -719,7 +693,7 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void signal_agent_released, m); if (r < 0) { - log_warning("Failed to register Released match on new connection bus: %s", strerror(-r)); + log_warning_errno(r, "Failed to register Released match on new connection bus: %m"); return 0; } } @@ -730,13 +704,13 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void r = bus_setup_api_vtables(m, bus); if (r < 0) { - log_warning("Failed to set up API vtables on new connection bus: %s", strerror(-r)); + log_warning_errno(r, "Failed to set up API vtables on new connection bus: %m"); return 0; } r = set_put(m->private_buses, bus); if (r < 0) { - log_warning("Failed to add new conenction bus to set: %s", strerror(-r)); + log_warning_errno(r, "Failed to add new conenction bus to set: %m"); return 0; } @@ -756,10 +730,8 @@ static int bus_list_names(Manager *m, sd_bus *bus) { assert(bus); r = sd_bus_list_names(bus, &names, NULL); - if (r < 0) { - log_error("Failed to get initial list of names: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get initial list of names: %m"); /* This is a bit hacky, we say the owner of the name is the * name itself, because we don't want the extra traffic to @@ -776,6 +748,14 @@ static int bus_setup_api(Manager *m, sd_bus *bus) { assert(m); assert(bus); + /* Let's make sure we have enough credential bits so that we can make security and selinux decisions */ + r = sd_bus_negotiate_creds(bus, 1, + SD_BUS_CREDS_PID|SD_BUS_CREDS_UID| + SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS| + SD_BUS_CREDS_SELINUX_CONTEXT); + if (r < 0) + log_warning_errno(r, "Failed to enable credential passing, ignoring: %m"); + r = bus_setup_api_vtables(m, bus); if (r < 0) return r; @@ -790,7 +770,7 @@ static int bus_setup_api(Manager *m, sd_bus *bus) { "member='NameOwnerChanged'", signal_name_owner_changed, m); if (r < 0) - log_warning("Failed to subscribe to NameOwnerChanged signal: %s", strerror(-r)); + log_warning_errno(r, "Failed to subscribe to NameOwnerChanged signal: %m"); r = sd_bus_add_match( bus, @@ -802,7 +782,7 @@ static int bus_setup_api(Manager *m, sd_bus *bus) { "member='ActivationRequest'", signal_activation_request, m); if (r < 0) - log_warning("Failed to subscribe to activation signal: %s", strerror(-r)); + log_warning_errno(r, "Failed to subscribe to activation signal: %m"); /* Allow replacing of our name, to ease implementation of * reexecution, where we keep the old connection open until @@ -810,10 +790,8 @@ static int bus_setup_api(Manager *m, sd_bus *bus) { * to allow clients to synchronously wait for reexecution to * finish */ r = sd_bus_request_name(bus,"org.freedesktop.systemd1", SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_ALLOW_REPLACEMENT); - if (r < 0) { - log_error("Failed to register name: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to register name: %m"); bus_list_names(m, bus); @@ -844,7 +822,7 @@ static int bus_init_api(Manager *m) { r = sd_bus_attach_event(bus, m->event, SD_EVENT_PRIORITY_NORMAL); if (r < 0) { - log_error("Failed to attach API bus to event loop: %s", strerror(-r)); + log_error_errno(r, "Failed to attach API bus to event loop: %m"); return 0; } @@ -855,7 +833,7 @@ static int bus_init_api(Manager *m) { r = bus_setup_api(m, bus); if (r < 0) { - log_error("Failed to set up API bus: %s", strerror(-r)); + log_error_errno(r, "Failed to set up API bus: %m"); return 0; } @@ -886,7 +864,7 @@ static int bus_setup_system(Manager *m, sd_bus *bus) { signal_agent_released, m); if (r < 0) - log_warning("Failed to register Released match on system bus: %s", strerror(-r)); + log_warning_errno(r, "Failed to register Released match on system bus: %m"); log_debug("Successfully connected to system bus."); return 0; @@ -917,13 +895,13 @@ static int bus_init_system(Manager *m) { r = sd_bus_attach_event(bus, m->event, SD_EVENT_PRIORITY_NORMAL); if (r < 0) { - log_error("Failed to attach system bus to event loop: %s", strerror(-r)); + log_error_errno(r, "Failed to attach system bus to event loop: %m"); return 0; } r = bus_setup_system(m, bus); if (r < 0) { - log_error("Failed to set up system bus: %s", strerror(-r)); + log_error_errno(r, "Failed to set up system bus: %m"); return 0; } @@ -974,35 +952,26 @@ static int bus_init_private(Manager *m) { left = strpcpy(&p, left, "/systemd/private"); salen = sizeof(sa.un) - left; - - mkdir_parents_label(sa.un.sun_path, 0755); } - unlink(sa.un.sun_path); + (void) mkdir_parents_label(sa.un.sun_path, 0755); + (void) unlink(sa.un.sun_path); fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (fd < 0) { - log_error("Failed to allocate private socket: %m"); - return -errno; - } + if (fd < 0) + return log_error_errno(errno, "Failed to allocate private socket: %m"); r = bind(fd, &sa.sa, salen); - if (r < 0) { - log_error("Failed to bind private socket: %m"); - return -errno; - } + if (r < 0) + return log_error_errno(errno, "Failed to bind private socket: %m"); r = listen(fd, SOMAXCONN); - if (r < 0) { - log_error("Failed to make private socket listening: %m"); - return -errno; - } + if (r < 0) + return log_error_errno(errno, "Failed to make private socket listening: %m"); r = sd_event_add_io(m->event, &s, fd, EPOLLIN, bus_on_connection, m); - if (r < 0) { - log_error("Failed to allocate event source: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to allocate event source: %m"); m->private_listen_fd = fd; m->private_listen_event_source = s; @@ -1175,6 +1144,7 @@ void bus_track_serialize(sd_bus_track *t, FILE *f) { int bus_track_deserialize_item(char ***l, const char *line) { const char *e; + int r; assert(l); assert(line); @@ -1183,7 +1153,11 @@ int bus_track_deserialize_item(char ***l, const char *line) { if (!e) return 0; - return strv_extend(l, e); + r = strv_extend(l, e); + if (r < 0) + return r; + + return 1; } int bus_track_coldplug(Manager *m, sd_bus_track **t, char ***l) { diff --git a/src/core/device.c b/src/core/device.c index 11c4261081..b254e45b33 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -98,7 +98,7 @@ static void device_set_state(Device *d, DeviceState state) { d->state = state; if (state != old_state) - log_debug_unit(UNIT(d)->id, + log_unit_debug(UNIT(d)->id, "%s changed %s -> %s", UNIT(d)->id, device_state_to_string(old_state), device_state_to_string(state)); @@ -249,7 +249,7 @@ static int device_add_udev_wants(Unit *u, struct udev_device *dev) { return r; } if (!isempty(state)) - log_warning_unit(u->id, "Property %s on %s has trailing garbage, ignoring.", + log_unit_warning(u->id, "Property %s on %s has trailing garbage, ignoring.", property, strna(udev_device_get_syspath(dev))); return 0; @@ -334,7 +334,7 @@ static int device_update_unit(Manager *m, struct udev_device *dev, const char *p return 0; fail: - log_warning("Failed to load device unit: %s", strerror(-r)); + log_warning_errno(r, "Failed to load device unit: %m"); if (delete && u) unit_free(u); @@ -628,7 +628,7 @@ static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, static RATELIMIT_DEFINE(limit, 10*USEC_PER_SEC, 5); if (!ratelimit_test(&limit)) - log_error("Failed to get udev event: %m"); + log_error_errno(errno, "Failed to get udev event: %m"); if (!(revents & EPOLLIN)) return 0; } @@ -650,20 +650,20 @@ static int device_dispatch_io(sd_event_source *source, int fd, uint32_t revents, if (streq(action, "remove") || !device_is_ready(dev)) { r = device_process_removed_device(m, dev); if (r < 0) - log_error("Failed to process device remove event: %s", strerror(-r)); + log_error_errno(r, "Failed to process device remove event: %m"); r = swap_process_removed_device(m, dev); if (r < 0) - log_error("Failed to process swap device remove event: %s", strerror(-r)); + log_error_errno(r, "Failed to process swap device remove event: %m"); } else { r = device_process_new_device(m, dev); if (r < 0) - log_error("Failed to process device new event: %s", strerror(-r)); + log_error_errno(r, "Failed to process device new event: %m"); r = swap_process_new_device(m, dev); if (r < 0) - log_error("Failed to process swap device new event: %s", strerror(-r)); + log_error_errno(r, "Failed to process swap device new event: %m"); manager_dispatch_load_queue(m); diff --git a/src/core/execute.c b/src/core/execute.c index c41aec222d..5e4135e030 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -83,8 +83,10 @@ #include "af-list.h" #include "mkdir.h" #include "apparmor-util.h" +#include "smack-util.h" #include "bus-kernel.h" #include "label.h" +#include "cap-list.h" #ifdef HAVE_SECCOMP #include "seccomp-util.h" @@ -425,12 +427,13 @@ static int setup_output(const ExecContext *context, int fileno, int socket_fd, c case EXEC_OUTPUT_JOURNAL_AND_CONSOLE: r = connect_logger_as(context, o, ident, unit_id, fileno); if (r < 0) { - log_struct_unit(LOG_CRIT, unit_id, - "MESSAGE=Failed to connect std%s of %s to the journal socket: %s", - fileno == STDOUT_FILENO ? "out" : "err", - unit_id, strerror(-r), - "ERRNO=%d", -r, - NULL); + log_unit_struct(unit_id, + LOG_CRIT, + LOG_MESSAGE("Failed to connect %s of %s to the journal socket: %s", + fileno == STDOUT_FILENO ? "stdout" : "stderr", + unit_id, strerror(-r)), + LOG_ERRNO(-r), + NULL); r = open_null_as(O_WRONLY, fileno); } return r; @@ -821,7 +824,7 @@ static int setup_pam( * If this fails, ignore the error - but expect sd-pam threads * to fail to exit normally */ if (setresuid(uid, uid, uid) < 0) - log_error("Error: Failed to setresuid() in sd-pam: %s", strerror(-r)); + log_error_errno(r, "Error: Failed to setresuid() in sd-pam: %m"); /* Wait until our parent died. This will only work if * the above setresuid() succeeds, otherwise the kernel @@ -883,7 +886,7 @@ fail: log_error("PAM failed: %s", pam_strerror(handle, pam_code)); err = -EPERM; /* PAM errors do not map to errno */ } else { - log_error("PAM failed: %m"); + log_error_errno(errno, "PAM failed: %m"); err = -errno; } @@ -1236,11 +1239,12 @@ static int exec_child(ExecCommand *command, int *error) { _cleanup_strv_free_ char **our_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL; + _cleanup_free_ char *mac_selinux_context_net = NULL; const char *username = NULL, *home = NULL, *shell = NULL; unsigned n_dont_close = 0; int dont_close[n_fds + 4]; - uid_t uid = (uid_t) -1; - gid_t gid = (gid_t) -1; + uid_t uid = UID_INVALID; + gid_t gid = GID_INVALID; int i, err; assert(command); @@ -1347,7 +1351,7 @@ static int exec_child(ExecCommand *command, } if (params->cgroup_path) { - err = cg_attach_everywhere(params->cgroup_supported, params->cgroup_path, 0); + err = cg_attach_everywhere(params->cgroup_supported, params->cgroup_path, 0, NULL, NULL); if (err < 0) { *error = EXIT_CGROUP; return err; @@ -1434,7 +1438,7 @@ static int exec_child(ExecCommand *command, #ifdef ENABLE_KDBUS if (params->bus_endpoint_fd >= 0 && context->bus_endpoint) { - uid_t ep_uid = (uid == (uid_t) -1) ? 0 : uid; + uid_t ep_uid = (uid == UID_INVALID) ? 0 : uid; err = bus_kernel_set_endpoint_policy(params->bus_endpoint_fd, ep_uid, context->bus_endpoint); if (err < 0) { @@ -1444,8 +1448,10 @@ static int exec_child(ExecCommand *command, } #endif -#ifdef HAVE_PAM - if (params->cgroup_path && context->user && context->pam_name) { + /* If delegation is enabled we'll pass ownership of the cgroup + * (but only in systemd's own controller hierarchy!) to the + * user of the new process. */ + if (params->cgroup_path && context->user && params->cgroup_delegate) { err = cg_set_task_access(SYSTEMD_CGROUP_CONTROLLER, params->cgroup_path, 0644, uid, gid); if (err < 0) { *error = EXIT_CGROUP; @@ -1459,7 +1465,6 @@ static int exec_child(ExecCommand *command, return err; } } -#endif if (!strv_isempty(context->runtime_directory) && params->runtime_prefix) { char **rt; @@ -1547,7 +1552,7 @@ static int exec_child(ExecCommand *command, context->mount_flags); if (err == -EPERM) - log_warning_unit(params->unit_id, "Failed to set up file system namespace due to lack of privileges. Execution sandbox will not be in effect: %s", strerror(-err)); + log_unit_warning_errno(params->unit_id, err, "Failed to set up file system namespace due to lack of privileges. Execution sandbox will not be in effect: %m"); else if (err < 0) { *error = EXIT_NAMESPACE; return err; @@ -1581,6 +1586,16 @@ static int exec_child(ExecCommand *command, } } +#ifdef HAVE_SELINUX + if (params->apply_permissions && mac_selinux_use() && params->selinux_context_net && socket_fd >= 0) { + err = mac_selinux_get_child_mls_label(socket_fd, command->path, context->selinux_context, &mac_selinux_context_net); + if (err < 0) { + *error = EXIT_SELINUX_CONTEXT; + return err; + } + } +#endif + /* We repeat the fd closing here, to make sure that * nothing is leaked from the PAM modules. Note that * we are more aggressive this time since socket_fd @@ -1617,6 +1632,16 @@ static int exec_child(ExecCommand *command, } } +#ifdef HAVE_SMACK + if (context->smack_process_label) { + err = mac_smack_apply_pid(0, context->smack_process_label); + if (err < 0) { + *error = EXIT_SMACK_PROCESS_LABEL; + return err; + } + } +#endif + if (context->user) { err = enforce_user(context, uid); if (err < 0) { @@ -1670,24 +1695,10 @@ static int exec_child(ExecCommand *command, #ifdef HAVE_SELINUX if (mac_selinux_use()) { - if (context->selinux_context) { - err = setexeccon(context->selinux_context); - if (err < 0 && !context->selinux_context_ignore) { - *error = EXIT_SELINUX_CONTEXT; - return err; - } - } - - if (params->selinux_context_net && socket_fd >= 0) { - _cleanup_free_ char *label = NULL; - - err = mac_selinux_get_child_mls_label(socket_fd, command->path, &label); - if (err < 0) { - *error = EXIT_SELINUX_CONTEXT; - return err; - } + char *exec_context = mac_selinux_context_net ?: context->selinux_context; - err = setexeccon(label); + if (exec_context) { + err = setexeccon(exec_context); if (err < 0) { *error = EXIT_SELINUX_CONTEXT; return err; @@ -1739,10 +1750,10 @@ static int exec_child(ExecCommand *command, line = exec_command_line(final_argv); if (line) { log_open(); - log_struct_unit(LOG_DEBUG, - params->unit_id, + log_unit_struct(params->unit_id, + LOG_DEBUG, "EXECUTABLE=%s", command->path, - "MESSAGE=Executing: %s", line, + LOG_MESSAGE("Executing: %s", line), NULL); log_close(); } @@ -1787,11 +1798,11 @@ int exec_spawn(ExecCommand *command, err = exec_context_load_environment(context, params->unit_id, &files_env); if (err < 0) { - log_struct_unit(LOG_ERR, - params->unit_id, - "MESSAGE=Failed to load environment files: %s", strerror(-err), - "ERRNO=%d", -err, - NULL); + log_unit_struct(params->unit_id, + LOG_ERR, + LOG_MESSAGE("Failed to load environment files: %s", strerror(-err)), + LOG_ERRNO(-err), + NULL); return err; } @@ -1801,10 +1812,10 @@ int exec_spawn(ExecCommand *command, if (!line) return log_oom(); - log_struct_unit(LOG_DEBUG, - params->unit_id, + log_unit_struct(params->unit_id, + LOG_DEBUG, "EXECUTABLE=%s", command->path, - "MESSAGE=About to execute: %s", line, + LOG_MESSAGE("About to execute: %s", line), NULL); free(line); @@ -1826,12 +1837,13 @@ int exec_spawn(ExecCommand *command, &r); if (r != 0) { log_open(); - log_struct(LOG_ERR, MESSAGE_ID(SD_MESSAGE_SPAWN_FAILED), + log_struct(LOG_ERR, + LOG_MESSAGE_ID(SD_MESSAGE_SPAWN_FAILED), "EXECUTABLE=%s", command->path, - "MESSAGE=Failed at step %s spawning %s: %s", - exit_status_to_string(r, EXIT_STATUS_SYSTEMD), - command->path, strerror(-err), - "ERRNO=%d", -err, + LOG_MESSAGE("Failed at step %s spawning %s: %s", + exit_status_to_string(r, EXIT_STATUS_SYSTEMD), + command->path, strerror(-err)), + LOG_ERRNO(-err), NULL); log_close(); } @@ -1839,10 +1851,10 @@ int exec_spawn(ExecCommand *command, _exit(r); } - log_struct_unit(LOG_DEBUG, - params->unit_id, - "MESSAGE=Forked %s as "PID_FMT, - command->path, pid, + log_unit_struct(params->unit_id, + LOG_DEBUG, + LOG_MESSAGE("Forked %s as "PID_FMT, + command->path, pid), NULL); /* We add the new process to the cgroup both in the child (so @@ -2285,13 +2297,8 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { fprintf(f, "%sCapabilityBoundingSet:", prefix); for (l = 0; l <= cap_last_cap(); l++) - if (!(c->capability_bounding_set_drop & ((uint64_t) 1ULL << (uint64_t) l))) { - _cleanup_cap_free_charp_ char *t; - - t = cap_to_name(l); - if (t) - fprintf(f, " %s", t); - } + if (!(c->capability_bounding_set_drop & ((uint64_t) 1ULL << (uint64_t) l))) + fprintf(f, " %s", strna(capability_to_name(l))); fputs("\n", f); } @@ -2402,6 +2409,21 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { prefix, c->apparmor_profile_ignore ? "-" : "", c->apparmor_profile); } +bool exec_context_maintains_privileges(ExecContext *c) { + assert(c); + + /* Returns true if the process forked off would run run under + * an unchanged UID or as root. */ + + if (!c->user) + return true; + + if (streq(c->user, "root") || streq(c->user, "0")) + return true; + + return false; +} + void exec_status_start(ExecStatus *s, pid_t pid) { assert(s); @@ -2744,7 +2766,7 @@ int exec_runtime_deserialize_item(ExecRuntime **rt, Unit *u, const char *key, co return r; if (safe_atoi(value, &fd) < 0 || !fdset_contains(fds, fd)) - log_debug_unit(u->id, "Failed to parse netns socket value %s", value); + log_unit_debug(u->id, "Failed to parse netns socket value %s", value); else { safe_close((*rt)->netns_storage_socket[0]); (*rt)->netns_storage_socket[0] = fdset_remove(fds, fd); @@ -2757,7 +2779,7 @@ int exec_runtime_deserialize_item(ExecRuntime **rt, Unit *u, const char *key, co return r; if (safe_atoi(value, &fd) < 0 || !fdset_contains(fds, fd)) - log_debug_unit(u->id, "Failed to parse netns socket value %s", value); + log_unit_debug(u->id, "Failed to parse netns socket value %s", value); else { safe_close((*rt)->netns_storage_socket[1]); (*rt)->netns_storage_socket[1] = fdset_remove(fds, fd); @@ -2790,7 +2812,7 @@ void exec_runtime_destroy(ExecRuntime *rt) { r = asynchronous_job(remove_tmpdir_thread, rt->tmp_dir); if (r < 0) { - log_warning("Failed to nuke %s: %s", rt->tmp_dir, strerror(-r)); + log_warning_errno(r, "Failed to nuke %s: %m", rt->tmp_dir); free(rt->tmp_dir); } @@ -2802,7 +2824,7 @@ void exec_runtime_destroy(ExecRuntime *rt) { r = asynchronous_job(remove_tmpdir_thread, rt->var_tmp_dir); if (r < 0) { - log_warning("Failed to nuke %s: %s", rt->var_tmp_dir, strerror(-r)); + log_warning_errno(r, "Failed to nuke %s: %m", rt->var_tmp_dir); free(rt->var_tmp_dir); } diff --git a/src/core/execute.h b/src/core/execute.h index c45dde53a6..5ed750534d 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -142,6 +142,9 @@ struct ExecContext { bool apparmor_profile_ignore; char *apparmor_profile; + bool smack_process_label_ignore; + char *smack_process_label; + char **read_write_dirs, **read_only_dirs, **inaccessible_dirs; unsigned long mount_flags; @@ -207,6 +210,7 @@ struct ExecParameters { bool selinux_context_net; CGroupControllerMask cgroup_supported; const char *cgroup_path; + bool cgroup_delegate; const char *runtime_prefix; const char *unit_id; usec_t watchdog_usec; @@ -244,6 +248,7 @@ int exec_context_destroy_runtime_directory(ExecContext *c, const char *runtime_r int exec_context_load_environment(const ExecContext *c, const char *unit_id, char ***l); bool exec_context_may_touch_console(ExecContext *c); +bool exec_context_maintains_privileges(ExecContext *c); void exec_status_start(ExecStatus *s, pid_t pid); void exec_status_exit(ExecStatus *s, ExecContext *context, pid_t pid, int code, int status); diff --git a/src/core/hostname-setup.c b/src/core/hostname-setup.c index 57baa79275..6664e8952c 100644 --- a/src/core/hostname-setup.c +++ b/src/core/hostname-setup.c @@ -64,7 +64,7 @@ int hostname_setup(void) { if (r == -ENOENT) enoent = true; else - log_warning("Failed to read configured hostname: %s", strerror(-r)); + log_warning_errno(r, "Failed to read configured hostname: %m"); hn = NULL; } else @@ -82,10 +82,8 @@ int hostname_setup(void) { hn = "localhost"; } - if (sethostname_idempotent(hn) < 0) { - log_warning("Failed to set hostname to <%s>: %m", hn); - return -errno; - } + if (sethostname_idempotent(hn) < 0) + return log_warning_errno(errno, "Failed to set hostname to <%s>: %m", hn); log_info("Set hostname to <%s>.", hn); return 0; diff --git a/src/core/ima-setup.c b/src/core/ima-setup.c index 7bffd8d9dd..8e4fed17e7 100644 --- a/src/core/ima-setup.c +++ b/src/core/ima-setup.c @@ -24,83 +24,54 @@ #include <unistd.h> #include <stdio.h> #include <errno.h> -#include <string.h> -#include <stdlib.h> -#include <fcntl.h> +#include <sys/types.h> #include <sys/stat.h> -#include <sys/mman.h> +#include <fcntl.h> #include "ima-setup.h" -#include "mount-setup.h" -#include "macro.h" +#include "copy.h" #include "util.h" #include "log.h" -#include "label.h" #define IMA_SECFS_DIR "/sys/kernel/security/ima" #define IMA_SECFS_POLICY IMA_SECFS_DIR "/policy" #define IMA_POLICY_PATH "/etc/ima/ima-policy" int ima_setup(void) { + int r = 0; #ifdef HAVE_IMA - struct stat st; - ssize_t policy_size = 0, written = 0; - char *policy; _cleanup_close_ int policyfd = -1, imafd = -1; - int result = 0; - - if (stat(IMA_POLICY_PATH, &st) < 0) - return 0; - policy_size = st.st_size; - if (stat(IMA_SECFS_DIR, &st) < 0) { + if (access(IMA_SECFS_DIR, F_OK) < 0) { log_debug("IMA support is disabled in the kernel, ignoring."); return 0; } - if (stat(IMA_SECFS_POLICY, &st) < 0) { - log_error("Another IMA custom policy has already been loaded, " - "ignoring."); + policyfd = open(IMA_POLICY_PATH, O_RDONLY|O_CLOEXEC); + if (policyfd < 0) { + log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno, + "Failed to open the IMA custom policy file "IMA_POLICY_PATH", ignoring: %m"); return 0; } - policyfd = open(IMA_POLICY_PATH, O_RDONLY|O_CLOEXEC); - if (policyfd < 0) { - log_error("Failed to open the IMA custom policy file %s (%m), " - "ignoring.", IMA_POLICY_PATH); + if (access(IMA_SECFS_POLICY, F_OK) < 0) { + log_warning("Another IMA custom policy has already been loaded, ignoring."); return 0; } imafd = open(IMA_SECFS_POLICY, O_WRONLY|O_CLOEXEC); if (imafd < 0) { - log_error("Failed to open the IMA kernel interface %s (%m), " - "ignoring.", IMA_SECFS_POLICY); - goto out; - } - - policy = mmap(NULL, policy_size, PROT_READ, MAP_PRIVATE, policyfd, 0); - if (policy == MAP_FAILED) { - log_error("mmap() failed (%m), freezing"); - result = -errno; - goto out; + log_error_errno(errno, "Failed to open the IMA kernel interface "IMA_SECFS_POLICY", ignoring: %m"); + return 0; } - written = loop_write(imafd, policy, (size_t)policy_size, false); - if (written != policy_size) { - log_error("Failed to load the IMA custom policy file %s (%m), " - "ignoring.", IMA_POLICY_PATH); - goto out_mmap; - } + r = copy_bytes(policyfd, imafd, -1); + if (r < 0) + log_error_errno(r, "Failed to load the IMA custom policy file "IMA_POLICY_PATH": %m"); + else + log_info("Successfully loaded the IMA custom policy "IMA_POLICY_PATH"."); - log_info("Successfully loaded the IMA custom policy %s.", - IMA_POLICY_PATH); -out_mmap: - munmap(policy, policy_size); -out: - if (result) - return result; #endif /* HAVE_IMA */ - - return 0; + return r; } diff --git a/src/core/job.c b/src/core/job.c index eaa4bb17fc..78bc1083de 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -160,15 +160,15 @@ Job* job_install(Job *j) { uj = *pj; if (uj) { - if (j->type != JOB_NOP && job_type_is_conflicting(uj->type, j->type)) + if (job_type_is_conflicting(uj->type, j->type)) job_finish_and_invalidate(uj, JOB_CANCELED, false); else { /* not conflicting, i.e. mergeable */ - if (j->type == JOB_NOP || uj->state == JOB_WAITING || + if (uj->state == JOB_WAITING || (job_type_allows_late_merge(j->type) && job_type_is_superset(uj->type, j->type))) { job_merge_into_installed(uj, j); - log_debug_unit(uj->unit->id, + log_unit_debug(uj->unit->id, "Merged into installed job %s/%s as %u", uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id); return uj; @@ -178,7 +178,7 @@ Job* job_install(Job *j) { /* XXX It should be safer to queue j to run after uj finishes, but it is * not currently possible to have more than one installed job per unit. */ job_merge_into_installed(uj, j); - log_debug_unit(uj->unit->id, + log_unit_debug(uj->unit->id, "Merged into running job, re-running: %s/%s as %u", uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id); uj->state = JOB_WAITING; @@ -192,7 +192,7 @@ Job* job_install(Job *j) { *pj = j; j->installed = true; j->manager->n_installed_jobs ++; - log_debug_unit(j->unit->id, + log_unit_debug(j->unit->id, "Installed new job %s/%s as %u", j->unit->id, job_type_to_string(j->type), (unsigned) j->id); return j; @@ -211,14 +211,14 @@ int job_install_deserialized(Job *j) { pj = (j->type == JOB_NOP) ? &j->unit->nop_job : &j->unit->job; if (*pj) { - log_debug_unit(j->unit->id, + log_unit_debug(j->unit->id, "Unit %s already has a job installed. Not installing deserialized job.", j->unit->id); return -EEXIST; } *pj = j; j->installed = true; - log_debug_unit(j->unit->id, + log_unit_debug(j->unit->id, "Reinstalled deserialized job %s/%s as %u", j->unit->id, job_type_to_string(j->type), (unsigned) j->id); return 0; @@ -352,6 +352,9 @@ bool job_type_is_redundant(JobType a, UnitActiveState b) { return b == UNIT_ACTIVATING; + case JOB_NOP: + return true; + default: assert_not_reached("Invalid job type"); } @@ -454,7 +457,7 @@ static bool job_is_runnable(Job *j) { } static void job_change_type(Job *j, JobType newtype) { - log_debug_unit(j->unit->id, + log_unit_debug(j->unit->id, "Converting job %s/%s -> %s/%s", j->unit->id, job_type_to_string(j->type), j->unit->id, job_type_to_string(newtype)); @@ -542,6 +545,8 @@ int job_run_and_invalidate(Job *j) { r = job_finish_and_invalidate(j, JOB_SKIPPED, true); else if (r == -ENOEXEC) r = job_finish_and_invalidate(j, JOB_INVALID, true); + else if (r == -EPROTO) + r = job_finish_and_invalidate(j, JOB_ASSERT, true); else if (r == -EAGAIN) { j->state = JOB_WAITING; m->n_running_jobs--; @@ -655,6 +660,11 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) { unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format); break; + case JOB_ASSERT: + manager_flip_auto_status(u->manager, true); + unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "ASSERT" ANSI_HIGHLIGHT_OFF, format); + break; + default: ; } @@ -720,28 +730,28 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) { sd_id128_t mid; mid = result == JOB_DONE ? SD_MESSAGE_UNIT_STARTED : SD_MESSAGE_UNIT_FAILED; - log_struct_unit(result == JOB_DONE ? LOG_INFO : LOG_ERR, - u->id, - MESSAGE_ID(mid), - "RESULT=%s", job_result_to_string(result), - "MESSAGE=%s", buf, - NULL); + log_unit_struct(u->id, + result == JOB_DONE ? LOG_INFO : LOG_ERR, + LOG_MESSAGE_ID(mid), + LOG_MESSAGE("%s", buf), + "RESULT=%s", job_result_to_string(result), + NULL); } else if (t == JOB_STOP) - log_struct_unit(result == JOB_DONE ? LOG_INFO : LOG_ERR, - u->id, - MESSAGE_ID(SD_MESSAGE_UNIT_STOPPED), - "RESULT=%s", job_result_to_string(result), - "MESSAGE=%s", buf, - NULL); + log_unit_struct(u->id, + result == JOB_DONE ? LOG_INFO : LOG_ERR, + LOG_MESSAGE_ID(SD_MESSAGE_UNIT_STOPPED), + LOG_MESSAGE("%s", buf), + "RESULT=%s", job_result_to_string(result), + NULL); else if (t == JOB_RELOAD) - log_struct_unit(result == JOB_DONE ? LOG_INFO : LOG_ERR, - u->id, - MESSAGE_ID(SD_MESSAGE_UNIT_RELOADED), - "RESULT=%s", job_result_to_string(result), - "MESSAGE=%s", buf, - NULL); + log_unit_struct(u->id, + result == JOB_DONE ? LOG_INFO : LOG_ERR, + LOG_MESSAGE_ID(SD_MESSAGE_UNIT_RELOADED), + LOG_MESSAGE("%s", buf), + "RESULT=%s", job_result_to_string(result), + NULL); } int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) { @@ -762,7 +772,7 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) { if (j->state == JOB_RUNNING) j->manager->n_running_jobs--; - log_debug_unit(u->id, "Job %s/%s finished, result=%s", + log_unit_debug(u->id, "Job %s/%s finished, result=%s", u->id, job_type_to_string(t), job_result_to_string(result)); job_print_status_message(u, t, result); @@ -827,15 +837,15 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) { * this context. And JOB_FAILURE is already handled by the * unit itself. */ if (result == JOB_TIMEOUT || result == JOB_DEPENDENCY) { - log_struct_unit(LOG_NOTICE, - u->id, - "JOB_TYPE=%s", job_type_to_string(t), - "JOB_RESULT=%s", job_result_to_string(result), - "Job %s/%s failed with result '%s'.", - u->id, - job_type_to_string(t), - job_result_to_string(result), - NULL); + log_unit_struct(u->id, + LOG_NOTICE, + "JOB_TYPE=%s", job_type_to_string(t), + "JOB_RESULT=%s", job_result_to_string(result), + LOG_MESSAGE("Job %s/%s failed with result '%s'.", + u->id, + job_type_to_string(t), + job_result_to_string(result)), + NULL); unit_start_on_failure(u); } @@ -863,7 +873,7 @@ static int job_dispatch_timer(sd_event_source *s, uint64_t monotonic, void *user assert(j); assert(s == j->timer_event_source); - log_warning_unit(j->unit->id, "Job %s/%s timed out.", j->unit->id, job_type_to_string(j->type)); + log_unit_warning(j->unit->id, "Job %s/%s timed out.", j->unit->id, job_type_to_string(j->type)); u = j->unit; job_finish_and_invalidate(j, JOB_TIMEOUT, true); @@ -1087,7 +1097,7 @@ int job_coldplug(Job *j) { j->begin_usec + j->unit->job_timeout, 0, job_dispatch_timer, j); if (r < 0) - log_debug("Failed to restart timeout for job: %s", strerror(-r)); + log_debug_errno(r, "Failed to restart timeout for job: %m"); return r; } @@ -1189,6 +1199,7 @@ static const char* const job_result_table[_JOB_RESULT_MAX] = { [JOB_DEPENDENCY] = "dependency", [JOB_SKIPPED] = "skipped", [JOB_INVALID] = "invalid", + [JOB_ASSERT] = "assert", }; DEFINE_STRING_TABLE_LOOKUP(job_result, JobResult); diff --git a/src/core/job.h b/src/core/job.h index 1e7c61b04f..223ff9cba7 100644 --- a/src/core/job.h +++ b/src/core/job.h @@ -49,9 +49,11 @@ enum JobType { _JOB_TYPE_MAX_MERGING, /* JOB_NOP can enter into a transaction, but as it won't pull in - * any dependencies, it won't have to merge with anything. - * job_install() avoids the problem of merging JOB_NOP too (it's - * special-cased, only merges with other JOB_NOPs). */ + * any dependencies and it uses the special 'nop_job' slot in Unit, + * it won't have to merge with anything (except possibly into another + * JOB_NOP, previously installed). JOB_NOP is special-cased in + * job_type_is_*() functions so that the transaction can be + * activated. */ JOB_NOP = _JOB_TYPE_MAX_MERGING, /* do nothing */ _JOB_TYPE_MAX_IN_TRANSACTION, @@ -99,6 +101,7 @@ enum JobResult { JOB_DEPENDENCY, /* A required dependency job did not result in JOB_DONE */ JOB_SKIPPED, /* Negative result of JOB_VERIFY_ACTIVE */ JOB_INVALID, /* JOB_RELOAD of inactive unit */ + JOB_ASSERT, /* Couldn't start a unit, because an assert didn't hold */ _JOB_RESULT_MAX, _JOB_RESULT_INVALID = -1 }; @@ -190,11 +193,15 @@ _pure_ static inline bool job_type_is_mergeable(JobType a, JobType b) { } _pure_ static inline bool job_type_is_conflicting(JobType a, JobType b) { - return !job_type_is_mergeable(a, b); + return a != JOB_NOP && b != JOB_NOP && !job_type_is_mergeable(a, b); } _pure_ static inline bool job_type_is_superset(JobType a, JobType b) { /* Checks whether operation a is a "superset" of b in its actions */ + if (b == JOB_NOP) + return true; + if (a == JOB_NOP) + return false; return a == job_type_lookup_merge(a, b); } diff --git a/src/core/killall.c b/src/core/killall.c index a6ff50a5a4..5a50ae6f04 100644 --- a/src/core/killall.c +++ b/src/core/killall.c @@ -102,7 +102,7 @@ static void wait_for_children(Set *pids, sigset_t *mask) { if (errno == ECHILD) break; - log_error("waitpid() failed: %m"); + log_error_errno(errno, "waitpid() failed: %m"); return; } @@ -136,7 +136,7 @@ static void wait_for_children(Set *pids, sigset_t *mask) { if (k != SIGCHLD) { if (k < 0 && errno != EAGAIN) { - log_error("sigtimedwait() failed: %m"); + log_error_errno(errno, "sigtimedwait() failed: %m"); return; } @@ -178,7 +178,7 @@ static int killall(int sig, Set *pids, bool send_sighup) { if (pids) set_put(pids, ULONG_TO_PTR(pid)); } else if (errno != ENOENT) - log_warning("Could not kill %d: %m", pid); + log_warning_errno(errno, "Could not kill %d: %m", pid); if (send_sighup) { /* Optionally, also send a SIGHUP signal, but @@ -212,12 +212,12 @@ void broadcast_signal(int sig, bool wait_for_exit, bool send_sighup) { assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) == 0); if (kill(-1, SIGSTOP) < 0 && errno != ESRCH) - log_warning("kill(-1, SIGSTOP) failed: %m"); + log_warning_errno(errno, "kill(-1, SIGSTOP) failed: %m"); killall(sig, pids, send_sighup); if (kill(-1, SIGCONT) < 0 && errno != ESRCH) - log_warning("kill(-1, SIGCONT) failed: %m"); + log_warning_errno(errno, "kill(-1, SIGCONT) failed: %m"); if (wait_for_exit) wait_for_children(pids, &mask); diff --git a/src/core/kmod-setup.c b/src/core/kmod-setup.c index 2f3f608830..c0a05b97aa 100644 --- a/src/core/kmod-setup.c +++ b/src/core/kmod-setup.c @@ -23,13 +23,17 @@ #include <unistd.h> #include <string.h> #include <errno.h> + +#ifdef HAVE_KMOD #include <libkmod.h> +#endif #include "macro.h" #include "execute.h" #include "capability.h" #include "kmod-setup.h" +#ifdef HAVE_KMOD static void systemd_kmod_log( void *data, int priority, @@ -40,20 +44,17 @@ static void systemd_kmod_log( /* library logging is enabled at debug only */ DISABLE_WARNING_FORMAT_NONLITERAL; - log_metav(LOG_DEBUG, file, line, fn, format, args); + log_internalv(LOG_DEBUG, 0, file, line, fn, format, args); REENABLE_WARNING; } static bool cmdline_check_kdbus(void) { - _cleanup_free_ char *line = NULL; - - if (proc_cmdline(&line) <= 0) - return false; - - return strstr(line, "kdbus") != NULL; + return get_proc_cmdline_key("kdbus", NULL) > 0; } +#endif int kmod_setup(void) { +#ifdef HAVE_KMOD static const struct { const char *module; @@ -62,16 +63,16 @@ int kmod_setup(void) { bool (*condition_fn)(void); } kmod_table[] = { /* auto-loading on use doesn't work before udev is up */ - { "autofs4", "/sys/class/misc/autofs", true, NULL }, + { "autofs4", "/sys/class/misc/autofs", true, NULL }, /* early configure of ::1 on the loopback device */ - { "ipv6", "/sys/module/ipv6", true, NULL }, + { "ipv6", "/sys/module/ipv6", true, NULL }, /* this should never be a module */ - { "unix", "/proc/net/unix", true, NULL }, + { "unix", "/proc/net/unix", true, NULL }, /* IPC is needed before we bring up any other services */ - { "kdbus", "/sys/bus/kdbus", false, cmdline_check_kdbus }, + { "kdbus", "/sys/fs/kdbus", false, cmdline_check_kdbus }, }; struct kmod_ctx *ctx = NULL; unsigned int i; @@ -123,5 +124,6 @@ int kmod_setup(void) { if (ctx) kmod_unref(ctx); +#endif return 0; } diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c index ffc68b4d73..8afaf45fe6 100644 --- a/src/core/load-dropin.c +++ b/src/core/load-dropin.c @@ -58,22 +58,18 @@ static int iterate_dir( if (errno == ENOENT) return 0; - log_error("Failed to open directory %s: %m", path); + log_error_errno(errno, "Failed to open directory %s: %m", path); return -errno; } for (;;) { struct dirent *de; _cleanup_free_ char *f = NULL; - int k; errno = 0; de = readdir(d); - if (!de && errno != 0) { - k = errno; - log_error("Failed to read directory %s: %s", path, strerror(k)); - return -k; - } + if (!de && errno != 0) + return log_error_errno(errno, "Failed to read directory %s: %m", path); if (!de) break; @@ -87,7 +83,7 @@ static int iterate_dir( r = unit_add_dependency_by_name(u, dependency, de->d_name, f, true); if (r < 0) - log_error("Cannot add dependency %s to %s, ignoring: %s", de->d_name, u->id, strerror(-r)); + log_error_errno(r, "Cannot add dependency %s to %s, ignoring: %m", de->d_name, u->id); } return 0; @@ -155,7 +151,7 @@ char **unit_find_dropin_paths(Unit *u) { r = conf_files_list_strv(&configs, ".conf", NULL, (const char**) strv); if (r < 0) { - log_error("Failed to get list of configuration files: %s", strerror(-r)); + log_error_errno(r, "Failed to get list of configuration files: %m"); strv_free(configs); return NULL; } diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index ca0139479b..e0ffaa605a 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -54,10 +54,10 @@ m4_ifdef(`HAVE_SECCOMP', $1.SystemCallArchitectures, config_parse_syscall_archs, 0, offsetof($1, exec_context.syscall_archs) $1.SystemCallErrorNumber, config_parse_syscall_errno, 0, offsetof($1, exec_context) $1.RestrictAddressFamilies, config_parse_address_families, 0, offsetof($1, exec_context)', -`$1.SystemCallFilter, config_parse_warn_compat, 0, 0 -$1.SystemCallArchitectures, config_parse_warn_compat, 0, 0 -$1.SystemCallErrorNumber, config_parse_warn_compat, 0, 0 -$1.RestrictAddressFamilies, config_parse_warn_compat, 0, 0') +`$1.SystemCallFilter, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 +$1.SystemCallArchitectures, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 +$1.SystemCallErrorNumber, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 +$1.RestrictAddressFamilies, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') $1.LimitCPU, config_parse_limit, RLIMIT_CPU, offsetof($1, exec_context.rlimit) $1.LimitFSIZE, config_parse_limit, RLIMIT_FSIZE, offsetof($1, exec_context.rlimit) $1.LimitDATA, config_parse_limit, RLIMIT_DATA, offsetof($1, exec_context.rlimit) @@ -88,15 +88,18 @@ $1.RuntimeDirectoryMode, config_parse_mode, 0, $1.RuntimeDirectory, config_parse_runtime_directory, 0, offsetof($1, exec_context.runtime_directory) m4_ifdef(`HAVE_PAM', `$1.PAMName, config_parse_unit_string_printf, 0, offsetof($1, exec_context.pam_name)', -`$1.PAMName, config_parse_warn_compat, 0, 0') +`$1.PAMName, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') $1.IgnoreSIGPIPE, config_parse_bool, 0, offsetof($1, exec_context.ignore_sigpipe) $1.UtmpIdentifier, config_parse_unit_string_printf, 0, offsetof($1, exec_context.utmp_id) m4_ifdef(`HAVE_SELINUX', `$1.SELinuxContext, config_parse_exec_selinux_context, 0, offsetof($1, exec_context)', -`$1.SELinuxContext, config_parse_warn_compat, 0, 0') +`$1.SELinuxContext, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') m4_ifdef(`HAVE_APPARMOR', -`$1.AppArmorProfile, config_parse_exec_apparmor_profile,0, offsetof($1, exec_context)', -`$1.AppArmorProfile, config_parse_warn_compat, 0, 0')' +`$1.AppArmorProfile, config_parse_exec_apparmor_profile, 0, offsetof($1, exec_context)', +`$1.AppArmorProfile, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') +m4_ifdef(`HAVE_SMACK', +`$1.SmackProcessLabel, config_parse_exec_smack_process_label, 0, offsetof($1, exec_context)', +`$1.SmackProcessLabel, config_parse_warn_compat, DISABLED_CONFIGURATION, 0')' )m4_dnl m4_define(`KILL_CONTEXT_CONFIG_ITEMS', `$1.SendSIGKILL, config_parse_bool, 0, offsetof($1, kill_context.send_sigkill) @@ -119,7 +122,8 @@ $1.BlockIOWeight, config_parse_blockio_weight, 0, $1.StartupBlockIOWeight, config_parse_blockio_weight, 0, offsetof($1, cgroup_context.startup_blockio_weight) $1.BlockIODeviceWeight, config_parse_blockio_device_weight, 0, offsetof($1, cgroup_context) $1.BlockIOReadBandwidth, config_parse_blockio_bandwidth, 0, offsetof($1, cgroup_context) -$1.BlockIOWriteBandwidth, config_parse_blockio_bandwidth, 0, offsetof($1, cgroup_context)' +$1.BlockIOWriteBandwidth, config_parse_blockio_bandwidth, 0, offsetof($1, cgroup_context) +$1.Delegate, config_parse_bool, 0, offsetof($1, cgroup_context.delegate)' )m4_dnl Unit.Description, config_parse_unit_string_printf, 0, offsetof(Unit, description) Unit.Documentation, config_parse_documentation, 0, offsetof(Unit, documentation) @@ -154,25 +158,44 @@ Unit.IgnoreOnSnapshot, config_parse_bool, 0, Unit.JobTimeoutSec, config_parse_sec, 0, offsetof(Unit, job_timeout) Unit.JobTimeoutAction, config_parse_failure_action, 0, offsetof(Unit, job_timeout_action) Unit.JobTimeoutRebootArgument, config_parse_string, 0, offsetof(Unit, job_timeout_reboot_arg) -Unit.ConditionPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, 0 -Unit.ConditionPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, 0 -Unit.ConditionPathIsDirectory, config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY, 0 -Unit.ConditionPathIsSymbolicLink,config_parse_unit_condition_path, CONDITION_PATH_IS_SYMBOLIC_LINK,0 -Unit.ConditionPathIsMountPoint, config_parse_unit_condition_path, CONDITION_PATH_IS_MOUNT_POINT, 0 -Unit.ConditionPathIsReadWrite, config_parse_unit_condition_path, CONDITION_PATH_IS_READ_WRITE, 0 -Unit.ConditionDirectoryNotEmpty, config_parse_unit_condition_path, CONDITION_DIRECTORY_NOT_EMPTY, 0 -Unit.ConditionFileNotEmpty, config_parse_unit_condition_path, CONDITION_FILE_NOT_EMPTY, 0 -Unit.ConditionFileIsExecutable, config_parse_unit_condition_path, CONDITION_FILE_IS_EXECUTABLE, 0 -Unit.ConditionNeedsUpdate, config_parse_unit_condition_path, CONDITION_NEEDS_UPDATE, 0 -Unit.ConditionFirstBoot, config_parse_unit_condition_string, CONDITION_FIRST_BOOT, 0 -Unit.ConditionKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, 0 -Unit.ConditionArchitecture, config_parse_unit_condition_string, CONDITION_ARCHITECTURE, 0 -Unit.ConditionVirtualization, config_parse_unit_condition_string, CONDITION_VIRTUALIZATION, 0 -Unit.ConditionSecurity, config_parse_unit_condition_string, CONDITION_SECURITY, 0 -Unit.ConditionCapability, config_parse_unit_condition_string, CONDITION_CAPABILITY, 0 -Unit.ConditionHost, config_parse_unit_condition_string, CONDITION_HOST, 0 -Unit.ConditionACPower, config_parse_unit_condition_string, CONDITION_AC_POWER, 0 -Unit.ConditionNull, config_parse_unit_condition_null, 0, 0 +Unit.ConditionPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, offsetof(Unit, conditions) +Unit.ConditionPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, offsetof(Unit, conditions) +Unit.ConditionPathIsDirectory, config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY, offsetof(Unit, conditions) +Unit.ConditionPathIsSymbolicLink,config_parse_unit_condition_path, CONDITION_PATH_IS_SYMBOLIC_LINK,offsetof(Unit, conditions) +Unit.ConditionPathIsMountPoint, config_parse_unit_condition_path, CONDITION_PATH_IS_MOUNT_POINT, offsetof(Unit, conditions) +Unit.ConditionPathIsReadWrite, config_parse_unit_condition_path, CONDITION_PATH_IS_READ_WRITE, offsetof(Unit, conditions) +Unit.ConditionDirectoryNotEmpty, config_parse_unit_condition_path, CONDITION_DIRECTORY_NOT_EMPTY, offsetof(Unit, conditions) +Unit.ConditionFileNotEmpty, config_parse_unit_condition_path, CONDITION_FILE_NOT_EMPTY, offsetof(Unit, conditions) +Unit.ConditionFileIsExecutable, config_parse_unit_condition_path, CONDITION_FILE_IS_EXECUTABLE, offsetof(Unit, conditions) +Unit.ConditionNeedsUpdate, config_parse_unit_condition_path, CONDITION_NEEDS_UPDATE, offsetof(Unit, conditions) +Unit.ConditionFirstBoot, config_parse_unit_condition_string, CONDITION_FIRST_BOOT, offsetof(Unit, conditions) +Unit.ConditionKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, offsetof(Unit, conditions) +Unit.ConditionArchitecture, config_parse_unit_condition_string, CONDITION_ARCHITECTURE, offsetof(Unit, conditions) +Unit.ConditionVirtualization, config_parse_unit_condition_string, CONDITION_VIRTUALIZATION, offsetof(Unit, conditions) +Unit.ConditionSecurity, config_parse_unit_condition_string, CONDITION_SECURITY, offsetof(Unit, conditions) +Unit.ConditionCapability, config_parse_unit_condition_string, CONDITION_CAPABILITY, offsetof(Unit, conditions) +Unit.ConditionHost, config_parse_unit_condition_string, CONDITION_HOST, offsetof(Unit, conditions) +Unit.ConditionACPower, config_parse_unit_condition_string, CONDITION_AC_POWER, offsetof(Unit, conditions) +Unit.ConditionNull, config_parse_unit_condition_null, 0, offsetof(Unit, conditions) +Unit.AssertPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, offsetof(Unit, asserts) +Unit.AssertPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, offsetof(Unit, asserts) +Unit.AssertPathIsDirectory, config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY, offsetof(Unit, asserts) +Unit.AssertPathIsSymbolicLink, config_parse_unit_condition_path, CONDITION_PATH_IS_SYMBOLIC_LINK,offsetof(Unit, asserts) +Unit.AssertPathIsMountPoint, config_parse_unit_condition_path, CONDITION_PATH_IS_MOUNT_POINT, offsetof(Unit, asserts) +Unit.AssertPathIsReadWrite, config_parse_unit_condition_path, CONDITION_PATH_IS_READ_WRITE, offsetof(Unit, asserts) +Unit.AssertDirectoryNotEmpty, config_parse_unit_condition_path, CONDITION_DIRECTORY_NOT_EMPTY, offsetof(Unit, asserts) +Unit.AssertFileNotEmpty, config_parse_unit_condition_path, CONDITION_FILE_NOT_EMPTY, offsetof(Unit, asserts) +Unit.AssertFileIsExecutable, config_parse_unit_condition_path, CONDITION_FILE_IS_EXECUTABLE, offsetof(Unit, asserts) +Unit.AssertNeedsUpdate, config_parse_unit_condition_path, CONDITION_NEEDS_UPDATE, offsetof(Unit, asserts) +Unit.AssertFirstBoot, config_parse_unit_condition_string, CONDITION_FIRST_BOOT, offsetof(Unit, asserts) +Unit.AssertKernelCommandLine, config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE, offsetof(Unit, asserts) +Unit.AssertArchitecture, config_parse_unit_condition_string, CONDITION_ARCHITECTURE, offsetof(Unit, asserts) +Unit.AssertVirtualization, config_parse_unit_condition_string, CONDITION_VIRTUALIZATION, offsetof(Unit, asserts) +Unit.AssertSecurity, config_parse_unit_condition_string, CONDITION_SECURITY, offsetof(Unit, asserts) +Unit.AssertCapability, config_parse_unit_condition_string, CONDITION_CAPABILITY, offsetof(Unit, asserts) +Unit.AssertHost, config_parse_unit_condition_string, CONDITION_HOST, offsetof(Unit, asserts) +Unit.AssertACPower, config_parse_unit_condition_string, CONDITION_AC_POWER, offsetof(Unit, asserts) +Unit.AssertNull, config_parse_unit_condition_null, 0, offsetof(Unit, asserts) m4_dnl Service.PIDFile, config_parse_unit_path_printf, 0, offsetof(Service, pid_file) Service.ExecStartPre, config_parse_exec, SERVICE_EXEC_START_PRE, offsetof(Service, exec_command) @@ -200,16 +223,14 @@ Service.GuessMainPID, config_parse_bool, 0, Service.RestartPreventExitStatus, config_parse_set_status, 0, offsetof(Service, restart_prevent_status) Service.RestartForceExitStatus, config_parse_set_status, 0, offsetof(Service, restart_force_status) Service.SuccessExitStatus, config_parse_set_status, 0, offsetof(Service, success_status) -m4_ifdef(`HAVE_SYSV_COMPAT', -`Service.SysVStartPriority, config_parse_sysv_priority, 0, offsetof(Service, sysv_start_priority)', -`Service.SysVStartPriority, config_parse_warn_compat, 0, 0') +Service.SysVStartPriority, config_parse_warn_compat, DISABLED_LEGACY, 0 Service.NonBlocking, config_parse_bool, 0, offsetof(Service, exec_context.non_blocking) Service.BusName, config_parse_unit_string_printf, 0, offsetof(Service, bus_name) Service.NotifyAccess, config_parse_notify_access, 0, offsetof(Service, notify_access) Service.Sockets, config_parse_service_sockets, 0, 0 m4_ifdef(`ENABLE_KDBUS', `Service.BusPolicy, config_parse_bus_endpoint_policy, 0, offsetof(Service, exec_context)', -`Service.BusPolicy, config_parse_warn_compat, 0, 0') +`Service.BusPolicy, config_parse_warn_compat, DISABLED_EXPERIMENTAL, 0') EXEC_CONTEXT_CONFIG_ITEMS(Service)m4_dnl CGROUP_CONTEXT_CONFIG_ITEMS(Service)m4_dnl KILL_CONTEXT_CONFIG_ITEMS(Service)m4_dnl @@ -264,12 +285,12 @@ m4_ifdef(`HAVE_SMACK', `Socket.SmackLabel, config_parse_string, 0, offsetof(Socket, smack) Socket.SmackLabelIPIn, config_parse_string, 0, offsetof(Socket, smack_ip_in) Socket.SmackLabelIPOut, config_parse_string, 0, offsetof(Socket, smack_ip_out)', -`Socket.SmackLabel, config_parse_warn_compat, 0, 0 -Socket.SmackLabelIPIn, config_parse_warn_compat, 0, 0 -Socket.SmackLabelIPOut, config_parse_warn_compat, 0, 0') +`Socket.SmackLabel, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 +Socket.SmackLabelIPIn, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 +Socket.SmackLabelIPOut, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') m4_ifdef(`HAVE_SELINUX', `Socket.SELinuxContextFromNet, config_parse_bool, 0, offsetof(Socket, selinux_context_from_net)', -`Socket.SELinuxContextFromNet, config_parse_warn_compat, 0, 0') +`Socket.SELinuxContextFromNet, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') EXEC_CONTEXT_CONFIG_ITEMS(Socket)m4_dnl CGROUP_CONTEXT_CONFIG_ITEMS(Socket)m4_dnl KILL_CONTEXT_CONFIG_ITEMS(Socket)m4_dnl diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index e193a67dcd..259323bd5c 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -59,12 +59,12 @@ #include "bus-error.h" #include "errno-list.h" #include "af-list.h" +#include "cap-list.h" #ifdef HAVE_SECCOMP #include "seccomp-util.h" #endif -#if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) || !defined(HAVE_PAM) || !defined(HAVE_SELINUX) || !defined(HAVE_SMACK) || !defined(HAVE_APPARMOR) int config_parse_warn_compat( const char *unit, const char *filename, @@ -76,13 +76,25 @@ int config_parse_warn_compat( const char *rvalue, void *data, void *userdata) { + Disabled reason = ltype; + + switch(reason) { + case DISABLED_CONFIGURATION: + log_syntax(unit, LOG_DEBUG, filename, line, EINVAL, + "Support for option %s= has been disabled at compile time and it is ignored", lvalue); + break; + case DISABLED_LEGACY: + log_syntax(unit, LOG_INFO, filename, line, EINVAL, + "Support for option %s= has been removed and it is ignored", lvalue); + break; + case DISABLED_EXPERIMENTAL: + log_syntax(unit, LOG_INFO, filename, line, EINVAL, + "Support for option %s= has not yet been enabled and it is ignored", lvalue); + break; + }; - log_syntax(unit, LOG_DEBUG, filename, line, EINVAL, - "Support for option %s= has been disabled at compile time and is ignored", - lvalue); return 0; } -#endif int config_parse_unit_deps(const char *unit, const char *filename, @@ -1029,17 +1041,15 @@ int config_parse_bounding_set(const char *unit, FOREACH_WORD_QUOTED(word, l, rvalue, state) { _cleanup_free_ char *t = NULL; - int r; - cap_value_t cap; + int cap; t = strndup(word, l); if (!t) return log_oom(); - r = cap_from_name(t, &cap); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, errno, - "Failed to parse capability in bounding set, ignoring: %s", t); + cap = capability_from_name(t); + if (cap < 0) { + log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse capability in bounding set, ignoring: %s", t); continue; } @@ -1313,6 +1323,56 @@ int config_parse_exec_apparmor_profile( return 0; } +int config_parse_exec_smack_process_label( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + ExecContext *c = data; + Unit *u = userdata; + bool ignore; + char *k; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + free(c->smack_process_label); + c->smack_process_label = NULL; + c->smack_process_label_ignore = false; + return 0; + } + + if (rvalue[0] == '-') { + ignore = true; + rvalue++; + } else + ignore = false; + + r = unit_name_printf(u, rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, -r, + "Failed to resolve specifiers, ignoring: %s", strerror(-r)); + return 0; + } + + free(c->smack_process_label); + c->smack_process_label = k; + c->smack_process_label_ignore = ignore; + + return 0; +} + int config_parse_timer(const char *unit, const char *filename, unsigned line, @@ -1955,22 +2015,23 @@ int config_parse_ip_tos(const char *unit, return 0; } -int config_parse_unit_condition_path(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_unit_condition_path( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { - ConditionType cond = ltype; - Unit *u = data; - bool trigger, negate; - Condition *c; _cleanup_free_ char *p = NULL; + Condition **list = data, *c; + ConditionType t = ltype; + bool trigger, negate; + Unit *u = userdata; int r; assert(filename); @@ -1980,8 +2041,8 @@ int config_parse_unit_condition_path(const char *unit, if (isempty(rvalue)) { /* Empty assignment resets the list */ - condition_free_list(u->conditions); - u->conditions = NULL; + condition_free_list(*list); + *list = NULL; return 0; } @@ -1994,45 +2055,41 @@ int config_parse_unit_condition_path(const char *unit, rvalue++; r = unit_full_printf(u, rvalue, &p); - if (r < 0) - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to resolve specifiers, ignoring: %s", rvalue); - if (!p) { - p = strdup(rvalue); - if (!p) - return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue); + return 0; } if (!path_is_absolute(p)) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Path in condition not absolute, ignoring: %s", p); + log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Path in condition not absolute, ignoring: %s", p); return 0; } - c = condition_new(cond, p, trigger, negate); + c = condition_new(t, p, trigger, negate); if (!c) return log_oom(); - LIST_PREPEND(conditions, u->conditions, c); + LIST_PREPEND(conditions, *list, c); return 0; } -int config_parse_unit_condition_string(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_unit_condition_string( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { - ConditionType cond = ltype; - Unit *u = data; - bool trigger, negate; - Condition *c; _cleanup_free_ char *s = NULL; + Condition **list = data, *c; + ConditionType t = ltype; + bool trigger, negate; + Unit *u = userdata; int r; assert(filename); @@ -2042,8 +2099,8 @@ int config_parse_unit_condition_string(const char *unit, if (isempty(rvalue)) { /* Empty assignment resets the list */ - condition_free_list(u->conditions); - u->conditions = NULL; + condition_free_list(*list); + *list = NULL; return 0; } @@ -2056,36 +2113,32 @@ int config_parse_unit_condition_string(const char *unit, rvalue++; r = unit_full_printf(u, rvalue, &s); - if (r < 0) - log_syntax(unit, LOG_ERR, filename, line, -r, - "Failed to resolve specifiers, ignoring: %s", rvalue); - if (!s) { - s = strdup(rvalue); - if (!s) - return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue); + return 0; } - c = condition_new(cond, s, trigger, negate); + c = condition_new(t, s, trigger, negate); if (!c) return log_oom(); - LIST_PREPEND(conditions, u->conditions, c); + LIST_PREPEND(conditions, *list, c); return 0; } -int config_parse_unit_condition_null(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_unit_condition_null( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { - Unit *u = data; - Condition *c; + Condition **list = data, *c; bool trigger, negate; int b; @@ -2096,8 +2149,8 @@ int config_parse_unit_condition_null(const char *unit, if (isempty(rvalue)) { /* Empty assignment resets the list */ - condition_free_list(u->conditions); - u->conditions = NULL; + condition_free_list(*list); + *list = NULL; return 0; } @@ -2111,9 +2164,7 @@ int config_parse_unit_condition_null(const char *unit, b = parse_boolean(rvalue); if (b < 0) { - log_syntax(unit, LOG_ERR, filename, line, -b, - "Failed to parse boolean value in condition, ignoring: %s", - rvalue); + log_syntax(unit, LOG_ERR, filename, line, -b, "Failed to parse boolean value in condition, ignoring: %s", rvalue); return 0; } @@ -2124,7 +2175,7 @@ int config_parse_unit_condition_null(const char *unit, if (!c) return log_oom(); - LIST_PREPEND(conditions, u->conditions, c); + LIST_PREPEND(conditions, *list, c); return 0; } @@ -2446,7 +2497,6 @@ int config_parse_address_families( void *userdata) { ExecContext *c = data; - Unit *u = userdata; bool invert = false; const char *word, *state; size_t l; @@ -2455,7 +2505,6 @@ int config_parse_address_families( assert(filename); assert(lvalue); assert(rvalue); - assert(u); if (isempty(rvalue)) { /* Empty assignment resets the list */ diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index 65100c9bd7..21e0871e8b 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -94,6 +94,7 @@ int config_parse_job_mode_isolate(const char *unit, const char *filename, unsign int config_parse_exec_selinux_context(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_personality(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_exec_apparmor_profile(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_exec_smack_process_label(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_address_families(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_runtime_directory(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_set_status(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); @@ -106,3 +107,9 @@ int config_parse_protect_system(const char* unit, const char *filename, unsigned /* gperf prototypes */ const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length); extern const char load_fragment_gperf_nulstr[]; + +typedef enum Disabled { + DISABLED_CONFIGURATION, + DISABLED_LEGACY, + DISABLED_EXPERIMENTAL, +} Disabled; diff --git a/src/core/locale-setup.c b/src/core/locale-setup.c index 5177dbfd08..e993c57321 100644 --- a/src/core/locale-setup.c +++ b/src/core/locale-setup.c @@ -56,7 +56,7 @@ int locale_setup(char ***environment) { NULL); if (r < 0 && r != -ENOENT) - log_warning("Failed to read /proc/cmdline: %s", strerror(-r)); + log_warning_errno(r, "Failed to read /proc/cmdline: %m"); } /* Hmm, nothing set on the kernel cmd line? Then let's @@ -80,7 +80,7 @@ int locale_setup(char ***environment) { NULL); if (r < 0 && r != -ENOENT) - log_warning("Failed to read /etc/locale.conf: %s", strerror(-r)); + log_warning_errno(r, "Failed to read /etc/locale.conf: %m"); } add = NULL; diff --git a/src/core/loopback-setup.c b/src/core/loopback-setup.c index 6ac1fffd75..98fc04dd2e 100644 --- a/src/core/loopback-setup.c +++ b/src/core/loopback-setup.c @@ -92,16 +92,10 @@ int loopback_setup(void) { r = start_loopback(rtnl); if (r == -EPERM) { - if (check_loopback() < 0) { - log_warning("Failed to configure loopback device: %s", - strerror(EPERM)); - return -EPERM; - } - } else if (r < 0) { - log_warning("Failed to configure loopback device: %s", - strerror(-r)); - return r; - } + if (check_loopback() < 0) + return log_warning_errno(EPERM, "Failed to configure loopback device: %m"); + } else if (r < 0) + return log_warning_errno(r, "Failed to configure loopback device: %m"); return 0; diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c index efb074fcbd..d91a02cf15 100644 --- a/src/core/machine-id-setup.c +++ b/src/core/machine-id-setup.c @@ -141,10 +141,8 @@ static int generate(char id[34], const char *root) { /* If that didn't work, generate a random machine id */ r = sd_id128_randomize(&buf); - if (r < 0) { - log_error("Failed to open /dev/urandom: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to open /dev/urandom: %m"); for (p = buf.bytes, q = id; p < buf.bytes + sizeof(buf); p++, q += 2) { q[0] = hexchar(*p >> 4); @@ -159,10 +157,120 @@ static int generate(char id[34], const char *root) { return 0; } +static int get_valid_machine_id(int fd, char id[34]) { + char id_to_validate[34]; + + assert(fd >= 0); + assert(id); + + if (loop_read(fd, id_to_validate, 33, false) == 33 && id_to_validate[32] == '\n') { + id_to_validate[32] = 0; + + if (id128_is_valid(id_to_validate)) { + memcpy(id, id_to_validate, 32); + id[32] = '\n'; + id[33] = 0; + return 0; + } + } + + return -EINVAL; +} + +static int write_machine_id(int fd, char id[34]) { + assert(fd >= 0); + assert(id); + lseek(fd, 0, SEEK_SET); + + if (loop_write(fd, id, 33, false) == 0) + return 0; + + return -errno; +} + +int machine_id_commit(const char *root) { + _cleanup_close_ int fd = -1, initial_mntns_fd = -1; + const char *etc_machine_id; + char id[34]; /* 32 + \n + \0 */ + int r; + + if (isempty(root)) + etc_machine_id = "/etc/machine-id"; + else { + char *x; + + x = strappenda(root, "/etc/machine-id"); + etc_machine_id = path_kill_slashes(x); + } + + r = path_is_mount_point(etc_machine_id, false); + if (r < 0) + return log_error_errno(r, "Failed to determine wether %s is a mount point: %m", etc_machine_id); + if (r == 0) { + log_debug("%s is is not a mount point. Nothing to do.", etc_machine_id); + return 0; + } + + /* Read existing machine-id */ + fd = open(etc_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY); + if (fd < 0) + return log_error_errno(errno, "Cannot open %s: %m", etc_machine_id); + + r = get_valid_machine_id(fd, id); + if (r < 0) + return log_error_errno(r, "We didn't find a valid machine ID in %s.", etc_machine_id); + + r = is_fd_on_temporary_fs(fd); + if (r < 0) + return log_error_errno(r, "Failed to determine whether %s is on a temporary file system: %m", etc_machine_id); + if (r == 0) { + log_error("%s is not on a temporary file system.", etc_machine_id); + return -EROFS; + } + + fd = safe_close(fd); + + /* Store current mount namespace */ + r = namespace_open(0, NULL, &initial_mntns_fd, NULL, NULL); + if (r < 0) + return log_error_errno(r, "Can't fetch current mount namespace: %m"); + + /* Switch to a new mount namespace, isolate ourself and unmount etc_machine_id in our new namespace */ + if (unshare(CLONE_NEWNS) < 0) + return log_error_errno(errno, "Failed to enter new namespace: %m"); + + if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) + return log_error_errno(errno, "Couldn't make-rslave / mountpoint in our private namespace: %m"); + + if (umount(etc_machine_id) < 0) + return log_error_errno(errno, "Failed to unmount transient %s file in our private namespace: %m", etc_machine_id); + + /* Update a persistent version of etc_machine_id */ + fd = open(etc_machine_id, O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444); + if (fd < 0) + return log_error_errno(errno, "Cannot open for writing %s. This is mandatory to get a persistent machine-id: %m", etc_machine_id); + + r = write_machine_id(fd, id); + if (r < 0) + return log_error_errno(r, "Cannot write %s: %m", etc_machine_id); + + fd = safe_close(fd); + + /* Return to initial namespace and proceed a lazy tmpfs unmount */ + r = namespace_enter(-1, initial_mntns_fd, -1, -1); + if (r < 0) + return log_warning_errno(r, "Failed to switch back to initial mount namespace: %m.\nWe'll keep transient %s file until next reboot.", etc_machine_id); + + if (umount2(etc_machine_id, MNT_DETACH) < 0) + return log_warning_errno(errno, "Failed to unmount transient %s file: %m.\nWe keep that mount until next reboot.", etc_machine_id); + + return 0; +} + int machine_id_setup(const char *root) { const char *etc_machine_id, *run_machine_id; _cleanup_close_ int fd = -1; - bool writable = false; + bool writable = true; struct stat st; char id[34]; /* 32 + \n + \0 */ int r; @@ -171,11 +279,13 @@ int machine_id_setup(const char *root) { etc_machine_id = "/etc/machine-id"; run_machine_id = "/run/machine-id"; } else { - etc_machine_id = strappenda(root, "/etc/machine-id"); - path_kill_slashes((char*) etc_machine_id); + char *x; + + x = strappenda(root, "/etc/machine-id"); + etc_machine_id = path_kill_slashes(x); - run_machine_id = strappenda(root, "/run/machine-id"); - path_kill_slashes((char*) run_machine_id); + x = strappenda(root, "/run/machine-id"); + run_machine_id = path_kill_slashes(x); } RUN_WITH_UMASK(0000) { @@ -186,12 +296,19 @@ int machine_id_setup(const char *root) { mkdir_parents(etc_machine_id, 0755); fd = open(etc_machine_id, O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY, 0444); - if (fd >= 0) - writable = true; - else { + if (fd < 0) { + int old_errno = errno; + fd = open(etc_machine_id, O_RDONLY|O_CLOEXEC|O_NOCTTY); if (fd < 0) { - log_error("Cannot open %s: %m", etc_machine_id); + if (old_errno == EROFS && errno == ENOENT) + log_error("System cannot boot: Missing /etc/machine-id and /etc is mounted read-only.\n" + "Booting up is supported only when:\n" + "1) /etc/machine-id exists and is populated.\n" + "2) /etc/machine-id exists and is empty.\n" + "3) /etc/machine-id is missing and /etc is writable.\n"); + else + log_error_errno(errno, "Cannot open %s: %m", etc_machine_id); return -errno; } @@ -199,18 +316,11 @@ int machine_id_setup(const char *root) { } } - if (fstat(fd, &st) < 0) { - log_error("fstat() failed: %m"); - return -errno; - } - - if (S_ISREG(st.st_mode)) - if (loop_read(fd, id, 33, false) == 33 && id[32] == '\n') { - id[32] = 0; + if (fstat(fd, &st) < 0) + return log_error_errno(errno, "fstat() failed: %m"); - if (id128_is_valid(id)) - return 0; - } + if (S_ISREG(st.st_mode) && get_valid_machine_id(fd, id) == 0) + return 0; /* Hmm, so, the id currently stored is not useful, then let's * generate one */ @@ -219,12 +329,9 @@ int machine_id_setup(const char *root) { if (r < 0) return r; - if (S_ISREG(st.st_mode) && writable) { - lseek(fd, 0, SEEK_SET); - - if (loop_write(fd, id, 33, false) == 33) + if (S_ISREG(st.st_mode) && writable) + if (write_machine_id(fd, id) == 0) return 0; - } fd = safe_close(fd); @@ -235,7 +342,7 @@ int machine_id_setup(const char *root) { r = write_string_file(run_machine_id, id); } if (r < 0) { - log_error("Cannot write %s: %s", run_machine_id, strerror(-r)); + log_error_errno(r, "Cannot write %s: %m", run_machine_id); unlink(run_machine_id); return r; } @@ -243,7 +350,7 @@ int machine_id_setup(const char *root) { /* And now, let's mount it over */ r = mount(run_machine_id, etc_machine_id, NULL, MS_BIND, NULL); if (r < 0) { - log_error("Failed to mount %s: %m", etc_machine_id); + log_error_errno(errno, "Failed to mount %s: %m", etc_machine_id); unlink_noerrno(run_machine_id); return -errno; } @@ -252,7 +359,7 @@ int machine_id_setup(const char *root) { /* Mark the mount read-only */ if (mount(NULL, etc_machine_id, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL) < 0) - log_warning("Failed to make transient %s read-only: %m", etc_machine_id); + log_warning_errno(errno, "Failed to make transient %s read-only: %m", etc_machine_id); return 0; } diff --git a/src/core/machine-id-setup.h b/src/core/machine-id-setup.h index b0583eefc8..f7707c3bf9 100644 --- a/src/core/machine-id-setup.h +++ b/src/core/machine-id-setup.h @@ -21,4 +21,5 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +int machine_id_commit(const char *root); int machine_id_setup(const char *root); diff --git a/src/core/main.c b/src/core/main.c index d48604e673..140f2195ac 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -75,9 +75,7 @@ #include "selinux-setup.h" #include "ima-setup.h" #include "smack-setup.h" -#ifdef HAVE_KMOD #include "kmod-setup.h" -#endif static enum { ACTION_RUN, @@ -133,7 +131,7 @@ noreturn static void crash(int sig) { /* Pass this on immediately, if this is not PID 1 */ raise(sig); else if (!arg_dump_core) - log_error("Caught <%s>, not dumping core.", signal_to_string(sig)); + log_emergency("Caught <%s>, not dumping core.", signal_to_string(sig)); else { struct sigaction sa = { .sa_handler = nop_handler, @@ -146,7 +144,7 @@ noreturn static void crash(int sig) { pid = fork(); if (pid < 0) - log_error("Caught <%s>, cannot fork for core dump: %m", signal_to_string(sig)); + log_emergency_errno(errno, "Caught <%s>, cannot fork for core dump: %m", signal_to_string(sig)); else if (pid == 0) { struct rlimit rl = {}; @@ -177,11 +175,11 @@ noreturn static void crash(int sig) { /* Order things nicely. */ r = wait_for_terminate(pid, &status); if (r < 0) - log_error("Caught <%s>, waitpid() failed: %s", signal_to_string(sig), strerror(-r)); + log_emergency_errno(r, "Caught <%s>, waitpid() failed: %m", signal_to_string(sig)); else if (status.si_code != CLD_DUMPED) - log_error("Caught <%s>, core dump failed.", signal_to_string(sig)); + log_emergency("Caught <%s>, core dump failed.", signal_to_string(sig)); else - log_error("Caught <%s>, dumped core as pid "PID_FMT".", signal_to_string(sig), pid); + log_emergency("Caught <%s>, dumped core as pid "PID_FMT".", signal_to_string(sig), pid); } } @@ -203,19 +201,19 @@ noreturn static void crash(int sig) { pid = fork(); if (pid < 0) - log_error("Failed to fork off crash shell: %m"); + log_emergency_errno(errno, "Failed to fork off crash shell: %m"); else if (pid == 0) { make_console_stdio(); execl("/bin/sh", "/bin/sh", NULL); - log_error("execl() failed: %m"); + log_emergency_errno(errno, "execl() failed: %m"); _exit(1); } log_info("Successfully spawned crash shell as pid "PID_FMT".", pid); } - log_info("Freezing execution."); + log_emergency("Freezing execution."); freeze(); } @@ -233,18 +231,14 @@ static int console_setup(void) { int r; tty_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC); - if (tty_fd < 0) { - log_error("Failed to open /dev/console: %s", strerror(-tty_fd)); - return tty_fd; - } + if (tty_fd < 0) + return log_error_errno(tty_fd, "Failed to open /dev/console: %m"); /* We don't want to force text mode. plymouth may be showing * pictures already from initrd. */ r = reset_terminal_fd(tty_fd, false); - if (r < 0) { - log_error("Failed to reset /dev/console: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to reset /dev/console: %m"); return 0; } @@ -356,7 +350,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value) { if (env) arg_default_environment = env; else - log_warning("Setting environment variable '%s' failed, ignoring: %s", value, strerror(ENOMEM)); + log_warning_errno(ENOMEM, "Setting environment variable '%s' failed, ignoring: %m", value); } else log_warning("Environment variable name '%s' is not valid. Ignoring.", value); @@ -470,7 +464,7 @@ static int config_parse_cpu_affinity2( if (c) { if (sched_setaffinity(0, CPU_ALLOC_SIZE(ncpus), c) < 0) - log_warning_unit(unit, "Failed to set CPU affinity: %m"); + log_unit_warning(unit, "Failed to set CPU affinity: %m"); CPU_FREE(c); } @@ -673,13 +667,12 @@ static int parse_config_file(void) { {} }; - const char *fn; + const char *fn, *conf_dirs_nulstr; fn = arg_running_as == SYSTEMD_SYSTEM ? PKGSYSCONFDIR "/system.conf" : PKGSYSCONFDIR "/user.conf"; - config_parse(NULL, fn, NULL, - "Manager\0", - config_item_table_lookup, items, - false, false, true, NULL); + conf_dirs_nulstr = arg_running_as == SYSTEMD_SYSTEM ? CONF_DIRS_NULSTR("systemd/system.conf") : CONF_DIRS_NULSTR("systemd/user.conf"); + config_parse_many(fn, conf_dirs_nulstr, "Manager\0", + config_item_table_lookup, items, false, NULL); return 0; } @@ -808,10 +801,8 @@ static int parse_argv(int argc, char *argv[]) { case ARG_UNIT: r = set_default_unit(optarg); - if (r < 0) { - log_error("Failed to set default unit %s: %s", optarg, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to set default unit %s: %m", optarg); break; @@ -892,10 +883,8 @@ static int parse_argv(int argc, char *argv[]) { fd_cloexec(fd, true); f = fdopen(fd, "r"); - if (!f) { - log_error("Failed to open serialization fd: %m"); - return -errno; - } + if (!f) + return log_error_errno(errno, "Failed to open serialization fd: %m"); if (arg_serialization) fclose(arg_serialization); @@ -992,7 +981,7 @@ static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool switching r = manager_open_serialization(m, &f); if (r < 0) { - log_error("Failed to create serialization file: %s", strerror(-r)); + log_error_errno(r, "Failed to create serialization file: %m"); goto fail; } @@ -1003,30 +992,30 @@ static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool switching fds = fdset_new(); if (!fds) { r = -ENOMEM; - log_error("Failed to allocate fd set: %s", strerror(-r)); + log_error_errno(r, "Failed to allocate fd set: %m"); goto fail; } r = manager_serialize(m, f, fds, switching_root); if (r < 0) { - log_error("Failed to serialize state: %s", strerror(-r)); + log_error_errno(r, "Failed to serialize state: %m"); goto fail; } if (fseeko(f, 0, SEEK_SET) < 0) { - log_error("Failed to rewind serialization fd: %m"); + log_error_errno(errno, "Failed to rewind serialization fd: %m"); goto fail; } r = fd_cloexec(fileno(f), false); if (r < 0) { - log_error("Failed to disable O_CLOEXEC for serialization: %s", strerror(-r)); + log_error_errno(r, "Failed to disable O_CLOEXEC for serialization: %m"); goto fail; } r = fdset_cloexec(fds, false); if (r < 0) { - log_error("Failed to disable O_CLOEXEC for serialization fds: %s", strerror(-r)); + log_error_errno(r, "Failed to disable O_CLOEXEC for serialization fds: %m"); goto fail; } @@ -1053,10 +1042,8 @@ static int bump_rlimit_nofile(struct rlimit *saved_rlimit) { /* Save the original RLIMIT_NOFILE so that we can reset it * later when transitioning from the initrd to the main * systemd or suchlike. */ - if (getrlimit(RLIMIT_NOFILE, saved_rlimit) < 0) { - log_error("Reading RLIMIT_NOFILE failed: %m"); - return -errno; - } + if (getrlimit(RLIMIT_NOFILE, saved_rlimit) < 0) + return log_error_errno(errno, "Reading RLIMIT_NOFILE failed: %m"); /* Make sure forked processes get the default kernel setting */ if (!arg_default_rlimit[RLIMIT_NOFILE]) { @@ -1072,10 +1059,8 @@ static int bump_rlimit_nofile(struct rlimit *saved_rlimit) { /* Bump up the resource limit for ourselves substantially */ nl.rlim_cur = nl.rlim_max = 64*1024; r = setrlimit_closest(RLIMIT_NOFILE, &nl); - if (r < 0) { - log_error("Setting RLIMIT_NOFILE failed: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Setting RLIMIT_NOFILE failed: %m"); return 0; } @@ -1156,20 +1141,20 @@ static int enforce_syscall_archs(Set *archs) { if (r == -EEXIST) continue; if (r < 0) { - log_error("Failed to add architecture to seccomp: %s", strerror(-r)); + log_error_errno(r, "Failed to add architecture to seccomp: %m"); goto finish; } } r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0); if (r < 0) { - log_error("Failed to unset NO_NEW_PRIVS: %s", strerror(-r)); + log_error_errno(r, "Failed to unset NO_NEW_PRIVS: %m"); goto finish; } r = seccomp_load(seccomp); if (r < 0) - log_error("Failed to add install architecture seccomp: %s", strerror(-r)); + log_error_errno(r, "Failed to add install architecture seccomp: %m"); finish: seccomp_release(seccomp); @@ -1195,7 +1180,7 @@ static int status_welcome(void) { } if (r < 0 && r != -ENOENT) - log_warning("Failed to read os-release file: %s", strerror(-r)); + log_warning_errno(r, "Failed to read os-release file: %m"); return status_printf(NULL, false, false, "\nWelcome to \x1B[%sm%s\x1B[0m!\n", @@ -1235,6 +1220,7 @@ int main(int argc, char *argv[]) { bool empty_etc = false; char *switch_root_dir = NULL, *switch_root_init = NULL; static struct rlimit saved_rlimit_nofile = { 0, 0 }; + const char *error_message = NULL; #ifdef HAVE_SYSV_COMPAT if (getpid() != 1 && strstr(program_invocation_short_name, "init")) { @@ -1243,7 +1229,7 @@ int main(int argc, char *argv[]) { errno = -ENOENT; execv(SYSTEMCTL_BINARY_PATH, argv); - log_error("Failed to exec " SYSTEMCTL_BINARY_PATH ": %m"); + log_error_errno(errno, "Failed to exec " SYSTEMCTL_BINARY_PATH ": %m"); return 1; } #endif @@ -1293,17 +1279,23 @@ int main(int argc, char *argv[]) { if (!skip_setup) { mount_setup_early(); dual_timestamp_get(&security_start_timestamp); - if (mac_selinux_setup(&loaded_policy) < 0) + if (mac_selinux_setup(&loaded_policy) < 0) { + error_message = "Failed to load SELinux policy"; goto finish; - if (ima_setup() < 0) + } else if (ima_setup() < 0) { + error_message = "Failed to load IMA policy"; goto finish; - if (mac_smack_setup(&loaded_policy) < 0) + } else if (mac_smack_setup(&loaded_policy) < 0) { + error_message = "Failed to load SMACK policy"; goto finish; + } dual_timestamp_get(&security_finish_timestamp); } - if (mac_selinux_init(NULL) < 0) + if (mac_selinux_init(NULL) < 0) { + error_message = "Failed to initialize SELinux policy"; goto finish; + } if (!skip_setup) { if (clock_is_localtime() > 0) { @@ -1319,7 +1311,7 @@ int main(int argc, char *argv[]) { */ r = clock_set_timezone(&min); if (r < 0) - log_error("Failed to apply local time delta, ignoring: %s", strerror(-r)); + log_error_errno(r, "Failed to apply local time delta, ignoring: %m"); else log_info("RTC configured in localtime, applying delta of %i minutes to system time.", min); } else if (!in_initrd()) { @@ -1378,20 +1370,30 @@ int main(int argc, char *argv[]) { /* Initialize default unit */ r = set_default_unit(SPECIAL_DEFAULT_TARGET); if (r < 0) { - log_error("Failed to set default unit %s: %s", SPECIAL_DEFAULT_TARGET, strerror(-r)); + log_emergency_errno(r, "Failed to set default unit %s: %m", SPECIAL_DEFAULT_TARGET); + error_message = "Failed to set default unit"; goto finish; } r = initialize_join_controllers(); - if (r < 0) + if (r < 0) { + error_message = "Failed to initalize cgroup controllers"; goto finish; + } /* Mount /proc, /sys and friends, so that /proc/cmdline and * /proc/$PID/fd is available. */ if (getpid() == 1) { + + /* Load the kernel modules early, so that we kdbus.ko is loaded before kdbusfs shall be mounted */ + if (!skip_setup) + kmod_setup(); + r = mount_setup(loaded_policy); - if (r < 0) + if (r < 0) { + error_message = "Failed to mount API filesystems"; goto finish; + } } /* Reset all signal handlers. */ @@ -1399,19 +1401,25 @@ int main(int argc, char *argv[]) { ignore_signals(SIGNALS_IGNORE, -1); - if (parse_config_file() < 0) + if (parse_config_file() < 0) { + error_message = "Failed to parse config file"; goto finish; + } - if (arg_running_as == SYSTEMD_SYSTEM) - if (parse_proc_cmdline(parse_proc_cmdline_item) < 0) - goto finish; + if (arg_running_as == SYSTEMD_SYSTEM) { + r = parse_proc_cmdline(parse_proc_cmdline_item); + if (r < 0) + log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); + } /* Note that this also parses bits from the kernel command * line, including "debug". */ log_parse_environment(); - if (parse_argv(argc, argv) < 0) + if (parse_argv(argc, argv) < 0) { + error_message = "Failed to parse commandline arguments"; goto finish; + } if (arg_action == ACTION_TEST && geteuid() == 0) { @@ -1467,7 +1475,8 @@ int main(int argc, char *argv[]) { /* Remember open file descriptors for later deserialization */ r = fdset_new_fill(&fds); if (r < 0) { - log_error("Failed to allocate fd set: %s", strerror(-r)); + log_emergency_errno(r, "Failed to allocate fd set: %m"); + error_message = "Failed to allocate fd set"; goto finish; } else fdset_cloexec(fds, true); @@ -1551,9 +1560,6 @@ int main(int argc, char *argv[]) { if (arg_show_status > 0 || plymouth_running()) status_welcome(); -#ifdef HAVE_KMOD - kmod_setup(); -#endif hostname_setup(); machine_id_setup(NULL); loopback_setup(); @@ -1567,31 +1573,35 @@ int main(int argc, char *argv[]) { if (arg_timer_slack_nsec != NSEC_INFINITY) if (prctl(PR_SET_TIMERSLACK, arg_timer_slack_nsec) < 0) - log_error("Failed to adjust timer slack: %m"); + log_error_errno(errno, "Failed to adjust timer slack: %m"); if (arg_capability_bounding_set_drop) { r = capability_bounding_set_drop_usermode(arg_capability_bounding_set_drop); if (r < 0) { - log_error("Failed to drop capability bounding set of usermode helpers: %s", strerror(-r)); + log_emergency_errno(r, "Failed to drop capability bounding set of usermode helpers: %m"); + error_message = "Failed to drop capability bounding set of usermode helpers"; goto finish; } r = capability_bounding_set_drop(arg_capability_bounding_set_drop, true); if (r < 0) { - log_error("Failed to drop capability bounding set: %s", strerror(-r)); + log_emergency_errno(r, "Failed to drop capability bounding set: %m"); + error_message = "Failed to drop capability bounding set"; goto finish; } } if (arg_syscall_archs) { r = enforce_syscall_archs(arg_syscall_archs); - if (r < 0) + if (r < 0) { + error_message = "Failed to set syscall architectures"; goto finish; + } } if (arg_running_as == SYSTEMD_USER) { /* Become reaper of our children */ if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0) { - log_warning("Failed to make us a subreaper: %m"); + log_warning_errno(errno, "Failed to make us a subreaper: %m"); if (errno == EINVAL) log_info("Perhaps the kernel version is too old (< 3.4?)"); } @@ -1603,7 +1613,7 @@ int main(int argc, char *argv[]) { if (empty_etc) { r = unit_file_preset_all(UNIT_FILE_SYSTEM, false, NULL, UNIT_FILE_PRESET_FULL, false, NULL, 0); if (r < 0) - log_warning("Failed to populate /etc with preset unit settings, ignoring: %s", strerror(-r)); + log_warning_errno(r, "Failed to populate /etc with preset unit settings, ignoring: %m"); else log_info("Populated /etc with preset unit settings."); } @@ -1611,7 +1621,8 @@ int main(int argc, char *argv[]) { r = manager_new(arg_running_as, arg_action == ACTION_TEST, &m); if (r < 0) { - log_error("Failed to allocate manager object: %s", strerror(-r)); + log_emergency_errno(r, "Failed to allocate manager object: %m"); + error_message = "Failed to allocate manager object"; goto finish; } @@ -1648,7 +1659,7 @@ int main(int argc, char *argv[]) { r = manager_startup(m, arg_serialization, fds); if (r < 0) - log_error("Failed to fully start up daemon: %s", strerror(-r)); + log_error_errno(r, "Failed to fully start up daemon: %m"); /* This will close all file descriptors that were opened, but * not claimed by any unit. */ @@ -1671,7 +1682,7 @@ int main(int argc, char *argv[]) { if (r < 0) log_error("Failed to load default target: %s", bus_error_message(&error, r)); else if (target->load_state == UNIT_ERROR || target->load_state == UNIT_NOT_FOUND) - log_error("Failed to load default target: %s", strerror(-target->load_error)); + log_error_errno(target->load_error, "Failed to load default target: %m"); else if (target->load_state == UNIT_MASKED) log_error("Default target masked."); @@ -1680,13 +1691,16 @@ int main(int argc, char *argv[]) { r = manager_load_unit(m, SPECIAL_RESCUE_TARGET, NULL, &error, &target); if (r < 0) { - log_error("Failed to load rescue target: %s", bus_error_message(&error, r)); + log_emergency("Failed to load rescue target: %s", bus_error_message(&error, r)); + error_message = "Failed to load rescue target"; goto finish; } else if (target->load_state == UNIT_ERROR || target->load_state == UNIT_NOT_FOUND) { - log_error("Failed to load rescue target: %s", strerror(-target->load_error)); + log_emergency_errno(target->load_error, "Failed to load rescue target: %m"); + error_message = "Failed to load rescue target"; goto finish; } else if (target->load_state == UNIT_MASKED) { - log_error("Rescue target masked."); + log_emergency("Rescue target masked."); + error_message = "Rescue target masked"; goto finish; } } @@ -1704,11 +1718,13 @@ int main(int argc, char *argv[]) { r = manager_add_job(m, JOB_START, target, JOB_REPLACE, false, &error, &default_unit_job); if (r < 0) { - log_error("Failed to start default target: %s", bus_error_message(&error, r)); + log_emergency("Failed to start default target: %s", bus_error_message(&error, r)); + error_message = "Failed to start default target"; goto finish; } } else if (r < 0) { - log_error("Failed to isolate default target: %s", bus_error_message(&error, r)); + log_emergency("Failed to isolate default target: %s", bus_error_message(&error, r)); + error_message = "Failed to isolate default target"; goto finish; } @@ -1730,7 +1746,8 @@ int main(int argc, char *argv[]) { for (;;) { r = manager_loop(m); if (r < 0) { - log_error("Failed to run mainloop: %s", strerror(-r)); + log_emergency_errno(r, "Failed to run main loop: %m"); + error_message = "Failed to run main loop"; goto finish; } @@ -1745,13 +1762,15 @@ int main(int argc, char *argv[]) { log_info("Reloading."); r = manager_reload(m); if (r < 0) - log_error("Failed to reload: %s", strerror(-r)); + log_error_errno(r, "Failed to reload: %m"); break; case MANAGER_REEXECUTE: - if (prepare_reexecute(m, &arg_serialization, &fds, false) < 0) + if (prepare_reexecute(m, &arg_serialization, &fds, false) < 0) { + error_message = "Failed to prepare for reexection"; goto finish; + } reexecute = true; log_notice("Reexecuting."); @@ -1764,8 +1783,10 @@ int main(int argc, char *argv[]) { m->switch_root = m->switch_root_init = NULL; if (!switch_root_init) - if (prepare_reexecute(m, &arg_serialization, &fds, true) < 0) + if (prepare_reexecute(m, &arg_serialization, &fds, true) < 0) { + error_message = "Failed to prepare for reexection"; goto finish; + } reexecute = true; log_notice("Switching root."); @@ -1797,10 +1818,7 @@ int main(int argc, char *argv[]) { finish: pager_close(); - if (m) { - manager_free(m); - m = NULL; - } + m = manager_free(m); for (j = 0; j < ELEMENTSOF(arg_default_rlimit); j++) { free(arg_default_rlimit[j]); @@ -1845,7 +1863,7 @@ finish: /* And switch root with MS_MOVE, because we remove the old directory afterwards and detach it. */ r = switch_root(switch_root_dir, "/mnt", true, MS_MOVE); if (r < 0) - log_error("Failed to switch root, trying to continue: %s", strerror(-r)); + log_error_errno(r, "Failed to switch root, trying to continue: %m"); } args_size = MAX(6, argc+1); @@ -1914,7 +1932,7 @@ finish: if (switch_root_init) { args[0] = switch_root_init; execv(args[0], (char* const*) args); - log_warning("Failed to execute configured init, trying fallback: %m"); + log_warning_errno(errno, "Failed to execute configured init, trying fallback: %m"); } args[0] = "/sbin/init"; @@ -1926,9 +1944,9 @@ finish: args[0] = "/bin/sh"; args[1] = NULL; execv(args[0], (char* const*) args); - log_error("Failed to execute /bin/sh, giving up: %m"); + log_error_errno(errno, "Failed to execute /bin/sh, giving up: %m"); } else - log_warning("Failed to execute /sbin/init, giving up: %m"); + log_warning_errno(errno, "Failed to execute /sbin/init, giving up: %m"); } if (arg_serialization) { @@ -2009,12 +2027,17 @@ finish: cg_uninstall_release_agent(SYSTEMD_CGROUP_CONTROLLER); execve(SYSTEMD_SHUTDOWN_BINARY_PATH, (char **) command_line, env_block); - log_error("Failed to execute shutdown binary, %s: %m", + log_error_errno(errno, "Failed to execute shutdown binary, %s: %m", getpid() == 1 ? "freezing" : "quitting"); } - if (getpid() == 1) + if (getpid() == 1) { + if (error_message) + manager_status_printf(NULL, STATUS_TYPE_EMERGENCY, + ANSI_HIGHLIGHT_RED_ON "!!!!!!" ANSI_HIGHLIGHT_OFF, + "%s, freezing.", error_message); freeze(); + } return retval; } diff --git a/src/core/manager.c b/src/core/manager.c index d427d88d4e..6382400af4 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -69,7 +69,7 @@ #include "audit-fd.h" #include "boot-timestamps.h" #include "env-util.h" -#include "bus-errors.h" +#include "bus-common-errors.h" #include "bus-error.h" #include "bus-util.h" #include "dbus.h" @@ -79,9 +79,6 @@ #include "bus-kernel.h" #include "time-util.h" -/* As soon as 5s passed since a unit was added to our GC queue, make sure to run a gc sweep */ -#define GC_QUEUE_USEC_MAX (10*USEC_PER_SEC) - /* Initial delay and the interval for printing status messages about running jobs */ #define JOBS_IN_PROGRESS_WAIT_USEC (5*USEC_PER_SEC) #define JOBS_IN_PROGRESS_PERIOD_USEC (USEC_PER_SEC / 3) @@ -241,7 +238,7 @@ static int manager_dispatch_ask_password_fd(sd_event_source *source, if (m->have_ask_password < 0) /* Log error but continue. Negative have_ask_password * is treated as unknown status. */ - log_error("Failed to list /run/systemd/ask-password: %s", strerror(m->have_ask_password)); + log_error_errno(m->have_ask_password, "Failed to list /run/systemd/ask-password: %m"); return 0; } @@ -265,13 +262,11 @@ static int manager_check_ask_password(Manager *m) { mkdir_p_label("/run/systemd/ask-password", 0755); m->ask_password_inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); - if (m->ask_password_inotify_fd < 0) { - log_error("inotify_init1() failed: %m"); - return -errno; - } + if (m->ask_password_inotify_fd < 0) + return log_error_errno(errno, "inotify_init1() failed: %m"); if (inotify_add_watch(m->ask_password_inotify_fd, "/run/systemd/ask-password", IN_CREATE|IN_DELETE|IN_MOVE) < 0) { - log_error("Failed to add watch on /run/systemd/ask-password: %m"); + log_error_errno(errno, "Failed to add watch on /run/systemd/ask-password: %m"); manager_close_ask_password(m); return -errno; } @@ -280,7 +275,7 @@ static int manager_check_ask_password(Manager *m) { m->ask_password_inotify_fd, EPOLLIN, manager_dispatch_ask_password_fd, m); if (r < 0) { - log_error("Failed to add event source for /run/systemd/ask-password: %m"); + log_error_errno(errno, "Failed to add event source for /run/systemd/ask-password: %m"); manager_close_ask_password(m); return -errno; } @@ -305,10 +300,8 @@ static int manager_watch_idle_pipe(Manager *m) { return 0; r = sd_event_add_io(m->event, &m->idle_pipe_event_source, m->idle_pipe[2], EPOLLIN, manager_dispatch_idle_pipe_fd, m); - if (r < 0) { - log_error("Failed to watch idle pipe: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to watch idle pipe: %m"); return 0; } @@ -339,22 +332,18 @@ static int manager_setup_time_change(Manager *m) { * CLOCK_REALTIME makes a jump relative to CLOCK_MONOTONIC */ m->time_change_fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC); - if (m->time_change_fd < 0) { - log_error("Failed to create timerfd: %m"); - return -errno; - } + if (m->time_change_fd < 0) + return log_error_errno(errno, "Failed to create timerfd: %m"); if (timerfd_settime(m->time_change_fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0) { - log_debug("Failed to set up TFD_TIMER_CANCEL_ON_SET, ignoring: %m"); + log_debug_errno(errno, "Failed to set up TFD_TIMER_CANCEL_ON_SET, ignoring: %m"); m->time_change_fd = safe_close(m->time_change_fd); return 0; } r = sd_event_add_io(m->event, &m->time_change_event_source, m->time_change_fd, EPOLLIN, manager_dispatch_time_change_fd, m); - if (r < 0) { - log_error("Failed to create time change event source: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to create time change event source: %m"); log_debug("Set up TFD_TIMER_CANCEL_ON_SET timerfd."); @@ -370,17 +359,17 @@ static int enable_special_signals(Manager *m) { * this will fail with EPERM (older) or EINVAL (newer), so * ignore that. */ if (reboot(RB_DISABLE_CAD) < 0 && errno != EPERM && errno != EINVAL) - log_warning("Failed to enable ctrl-alt-del handling: %m"); + log_warning_errno(errno, "Failed to enable ctrl-alt-del handling: %m"); fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC); if (fd < 0) { /* Support systems without virtual console */ if (fd != -ENOENT) - log_warning("Failed to open /dev/tty0: %m"); + log_warning_errno(errno, "Failed to open /dev/tty0: %m"); } else { /* Enable that we get SIGWINCH on kbrequest */ if (ioctl(fd, KDSIGACCEPT, SIGWINCH) < 0) - log_warning("Failed to enable kbrequest handling: %m"); + log_warning_errno(errno, "Failed to enable kbrequest handling: %m"); } return 0; @@ -396,9 +385,6 @@ static int manager_setup_signals(Manager *m) { assert(m); - if (m->test_run) - return 0; - assert_se(sigaction(SIGCHLD, &sa, NULL) == 0); /* We make liberal use of realtime signals here. On @@ -554,7 +540,7 @@ int manager_new(SystemdRunningAs running_as, bool test_run, Manager **_m) { m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1; - m->pin_cgroupfs_fd = m->notify_fd = m->signal_fd = m->time_change_fd = m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = -1; + m->pin_cgroupfs_fd = m->notify_fd = m->signal_fd = m->time_change_fd = m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->utab_inotify_fd = -1; m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */ m->ask_password_inotify_fd = -1; @@ -657,10 +643,8 @@ static int manager_setup_notify(Manager *m) { m->notify_event_source = sd_event_source_unref(m->notify_event_source); fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (fd < 0) { - log_error("Failed to allocate notification socket: %m"); - return -errno; - } + if (fd < 0) + return log_error_errno(errno, "Failed to allocate notification socket: %m"); if (m->running_as == SYSTEMD_SYSTEM) m->notify_socket = strdup("/run/systemd/notify"); @@ -669,7 +653,7 @@ static int manager_setup_notify(Manager *m) { e = getenv("XDG_RUNTIME_DIR"); if (!e) { - log_error("XDG_RUNTIME_DIR is not set: %m"); + log_error_errno(errno, "XDG_RUNTIME_DIR is not set: %m"); return -EINVAL; } @@ -678,32 +662,17 @@ static int manager_setup_notify(Manager *m) { if (!m->notify_socket) return log_oom(); + (void) mkdir_parents_label(m->notify_socket, 0755); + (void) unlink(m->notify_socket); + strncpy(sa.un.sun_path, m->notify_socket, sizeof(sa.un.sun_path)-1); r = bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)); - if (r < 0) { - log_error("bind(%s) failed: %m", sa.un.sun_path); - if (errno == EADDRINUSE) { - log_notice("Removing %s socket and trying again.", m->notify_socket); - r = unlink(m->notify_socket); - if (r < 0) { - log_error("Failed to remove %s: %m", m->notify_socket); - return -EADDRINUSE; - } - - r = bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)); - if (r < 0) { - log_error("bind(%s) failed: %m", sa.un.sun_path); - return -errno; - } - } else - return -errno; - } + if (r < 0) + return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); r = setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); - if (r < 0) { - log_error("SO_PASSCRED failed: %m"); - return -errno; - } + if (r < 0) + return log_error_errno(errno, "SO_PASSCRED failed: %m"); m->notify_fd = fd; fd = -1; @@ -713,18 +682,14 @@ static int manager_setup_notify(Manager *m) { if (!m->notify_event_source) { r = sd_event_add_io(m->event, &m->notify_event_source, m->notify_fd, EPOLLIN, manager_dispatch_notify_fd, m); - if (r < 0) { - log_error("Failed to allocate notify event source: %s", strerror(-r)); - return -errno; - } + if (r < 0) + return log_error_errno(r, "Failed to allocate notify event source: %m"); /* Process signals a bit earlier than SIGCHLD, so that we can * still identify to which service an exit message belongs */ r = sd_event_source_set_priority(m->notify_event_source, -7); - if (r < 0) { - log_error("Failed to set priority of notify event source: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to set priority of notify event source: %m"); } return 0; @@ -739,20 +704,17 @@ static int manager_setup_kdbus(Manager *m) { if (m->test_run || m->kdbus_fd >= 0) return 0; - m->kdbus_fd = bus_kernel_create_bus(m->running_as == SYSTEMD_SYSTEM ? "system" : "user", m->running_as == SYSTEMD_SYSTEM, &p); - if (m->kdbus_fd < 0) { - log_debug("Failed to set up kdbus: %s", strerror(-m->kdbus_fd)); - return m->kdbus_fd; - } + if (getpid() == 1) + bus_kernel_fix_attach_mask(); - log_debug("Successfully set up kdbus on %s", p); + m->kdbus_fd = bus_kernel_create_bus( + m->running_as == SYSTEMD_SYSTEM ? "system" : "user", + m->running_as == SYSTEMD_SYSTEM, &p); - /* Create the namespace directory here, so that the contents - * of that directory is not visible to non-root users. This is - * necessary to ensure that users cannot get access to busses - * of virtualized users when no UID namespacing is used. */ - if (m->running_as == SYSTEMD_SYSTEM) - mkdir_p_label("/dev/kdbus/domain", 0700); + if (m->kdbus_fd < 0) + return log_debug_errno(m->kdbus_fd, "Failed to set up kdbus: %m"); + + log_debug("Successfully set up kdbus on %s", p); #endif return 0; @@ -878,7 +840,7 @@ static unsigned manager_dispatch_gc_queue(Manager *m) { if (u->gc_marker == gc_marker + GC_OFFSET_BAD || u->gc_marker == gc_marker + GC_OFFSET_UNSURE) { - log_debug_unit(u->id, "Collecting %s", u->id); + log_unit_debug(u->id, "Collecting %s", u->id); u->gc_marker = gc_marker + GC_OFFSET_BAD; unit_add_to_cleanup_queue(u); } @@ -913,11 +875,12 @@ static void manager_clear_jobs_and_units(Manager *m) { m->n_running_jobs = 0; } -void manager_free(Manager *m) { +Manager* manager_free(Manager *m) { UnitType c; int i; - assert(m); + if (!m) + return NULL; manager_clear_jobs_and_units(m); @@ -979,6 +942,7 @@ void manager_free(Manager *m) { hashmap_free(m->units_requiring_mounts_for); free(m); + return NULL; } int manager_enumerate(Manager *m) { @@ -1048,7 +1012,7 @@ static void manager_build_unit_path_cache(Manager *m) { d = opendir(*i); if (!d) { if (errno != ENOENT) - log_error("Failed to open directory %s: %m", *i); + log_error_errno(errno, "Failed to open directory %s: %m", *i); continue; } @@ -1076,7 +1040,7 @@ static void manager_build_unit_path_cache(Manager *m) { return; fail: - log_error("Failed to build unit path cache: %s", strerror(-r)); + log_error_errno(r, "Failed to build unit path cache: %m"); set_free_free(m->unit_path_cache); m->unit_path_cache = NULL; @@ -1189,17 +1153,13 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool ove assert(unit); assert(mode < _JOB_MODE_MAX); - if (mode == JOB_ISOLATE && type != JOB_START) { - sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Isolate is only valid for start."); - return -EINVAL; - } + if (mode == JOB_ISOLATE && type != JOB_START) + return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Isolate is only valid for start."); - if (mode == JOB_ISOLATE && !unit->allow_isolate) { - sd_bus_error_setf(e, BUS_ERROR_NO_ISOLATION, "Operation refused, unit may not be isolated."); - return -EPERM; - } + if (mode == JOB_ISOLATE && !unit->allow_isolate) + return sd_bus_error_setf(e, BUS_ERROR_NO_ISOLATION, "Operation refused, unit may not be isolated."); - log_debug_unit(unit->id, + log_unit_debug(unit->id, "Trying to enqueue job %s/%s/%s", unit->id, job_type_to_string(type), job_mode_to_string(mode)); @@ -1225,7 +1185,7 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool ove if (r < 0) goto tr_abort; - log_debug_unit(unit->id, + log_unit_debug(unit->id, "Enqueued job %s/%s as %u", unit->id, job_type_to_string(type), (unsigned) tr->anchor_job->id); @@ -1495,7 +1455,7 @@ static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, char * return; } - log_debug_unit(u->id, "Got notification message for unit %s", u->id); + log_unit_debug(u->id, "Got notification message for unit %s", u->id); if (UNIT_VTABLE(u)->notify_message) UNIT_VTABLE(u)->notify_message(u, pid, tags); @@ -1591,7 +1551,7 @@ static void invoke_sigchld_event(Manager *m, Unit *u, siginfo_t *si) { assert(u); assert(si); - log_debug_unit(u->id, "Child "PID_FMT" belongs to %s", si->si_pid, u->id); + log_unit_debug(u->id, "Child "PID_FMT" belongs to %s", si->si_pid, u->id); unit_unwatch_pid(u, si->si_pid); UNIT_VTABLE(u)->sigchld_event(u, si->si_pid, si->si_code, si->si_status); @@ -1663,11 +1623,11 @@ static int manager_start_target(Manager *m, const char *name, JobMode mode) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; int r; - log_debug_unit(name, "Activating special unit %s", name); + log_unit_debug(name, "Activating special unit %s", name); r = manager_add_job_by_name(m, JOB_START, name, mode, true, &error, NULL); if (r < 0) - log_error_unit(name, "Failed to enqueue %s job: %s", name, bus_error_message(&error, r)); + log_unit_error(name, "Failed to enqueue %s job: %s", name, bus_error_message(&error, r)); return r; } @@ -1902,8 +1862,8 @@ static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint assert(m->time_change_fd == fd); log_struct(LOG_INFO, - MESSAGE_ID(SD_MESSAGE_TIME_CHANGE), - "MESSAGE=Time has been changed", + LOG_MESSAGE_ID(SD_MESSAGE_TIME_CHANGE), + LOG_MESSAGE("Time has been changed"), NULL); /* Restart the watch */ @@ -2008,10 +1968,8 @@ int manager_loop(Manager *m) { wait_usec = USEC_INFINITY; r = sd_event_run(m->event, wait_usec); - if (r < 0) { - log_error("Failed to run event loop: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to run event loop: %m"); } return m->exit_code; @@ -2070,6 +2028,7 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { #ifdef HAVE_AUDIT _cleanup_free_ char *p = NULL; + const char *msg; int audit_fd; audit_fd = get_audit_fd(); @@ -2089,18 +2048,19 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { p = unit_name_to_prefix_and_instance(u->id); if (!p) { - log_error_unit(u->id, - "Failed to allocate unit name for audit message: %s", strerror(ENOMEM)); + log_oom(); return; } - if (audit_log_user_comm_message(audit_fd, type, "", p, NULL, NULL, NULL, success) < 0) { - if (errno == EPERM) { + msg = strappenda("unit=", p); + + if (audit_log_user_comm_message(audit_fd, type, msg, "systemd", NULL, NULL, NULL, success) < 0) { + if (errno == EPERM) /* We aren't allowed to send audit messages? * Then let's not retry again. */ close_audit_fd(); - } else - log_warning("Failed to send audit message: %m"); + else + log_warning_errno(errno, "Failed to send audit message: %m"); } #endif @@ -2133,14 +2093,14 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) { * message then wait for plymouth */ fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (fd < 0) { - log_error("socket() failed: %m"); + log_error_errno(errno, "socket() failed: %m"); return; } if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) { if (!IN_SET(errno, EPIPE, EAGAIN, ENOENT, ECONNREFUSED, ECONNRESET, ECONNABORTED)) - log_error("connect() failed: %m"); + log_error_errno(errno, "connect() failed: %m"); return; } @@ -2152,7 +2112,7 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) { errno = 0; if (write(fd, message, n + 1) != n + 1) if (!IN_SET(errno, EPIPE, EAGAIN, ENOENT, ECONNREFUSED, ECONNRESET, ECONNABORTED)) - log_error("Failed to write Plymouth message: %m"); + log_error_errno(errno, "Failed to write Plymouth message: %m"); } void manager_dispatch_bus_name_owner_changed( @@ -2329,7 +2289,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { uint32_t id; if (safe_atou32(l+15, &id) < 0) - log_warning("Failed to parse current job id value %s", l+15); + log_debug("Failed to parse current job id value %s", l+15); else m->current_job_id = MAX(m->current_job_id, id); @@ -2337,7 +2297,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { uint32_t n; if (safe_atou32(l+17, &n) < 0) - log_warning("Failed to parse installed jobs counter %s", l+17); + log_debug("Failed to parse installed jobs counter %s", l+17); else m->n_installed_jobs += n; @@ -2345,7 +2305,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { uint32_t n; if (safe_atou32(l+14, &n) < 0) - log_warning("Failed to parse failed jobs counter %s", l+14); + log_debug("Failed to parse failed jobs counter %s", l+14); else m->n_failed_jobs += n; @@ -2354,7 +2314,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { b = parse_boolean(l+10); if (b < 0) - log_warning("Failed to parse taint /usr flag %s", l+10); + log_debug("Failed to parse taint /usr flag %s", l+10); else m->taint_usr = m->taint_usr || b; @@ -2405,7 +2365,7 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { int fd; if (safe_atoi(l + 10, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) - log_warning("Failed to parse notify fd: %s", l + 10); + log_debug("Failed to parse notify fd: %s", l + 10); else { m->notify_event_source = sd_event_source_unref(m->notify_event_source); safe_close(m->notify_fd); @@ -2428,14 +2388,21 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { int fd; if (safe_atoi(l + 9, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) - log_warning("Failed to parse kdbus fd: %s", l + 9); + log_debug("Failed to parse kdbus fd: %s", l + 9); else { safe_close(m->kdbus_fd); m->kdbus_fd = fdset_remove(fds, fd); } - } else if (bus_track_deserialize_item(&m->deserialized_subscribed, l) == 0) - log_warning("Unknown serialization item '%s'", l); + } else { + int k; + + k = bus_track_deserialize_item(&m->deserialized_subscribed, l); + if (k < 0) + log_debug_errno(k, "Failed to deserialize bus tracker object: %m"); + else if (k == 0) + log_debug("Unknown serialization item '%s'", l); + } } for (;;) { @@ -2584,45 +2551,13 @@ bool manager_unit_inactive_or_pending(Manager *m, const char *name) { return unit_inactive_or_pending(u); } -void manager_check_finished(Manager *m) { +static void manager_notify_finished(Manager *m) { char userspace[FORMAT_TIMESPAN_MAX], initrd[FORMAT_TIMESPAN_MAX], kernel[FORMAT_TIMESPAN_MAX], sum[FORMAT_TIMESPAN_MAX]; usec_t firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec; - Unit *u = NULL; - Iterator i; - - assert(m); - - if (m->n_running_jobs == 0) - m->jobs_in_progress_event_source = sd_event_source_unref(m->jobs_in_progress_event_source); - - if (hashmap_size(m->jobs) > 0) { - - if (m->jobs_in_progress_event_source) - sd_event_source_set_time(m->jobs_in_progress_event_source, now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC); - return; - } - - manager_flip_auto_status(m, false); - - /* Notify Type=idle units that we are done now */ - m->idle_pipe_event_source = sd_event_source_unref(m->idle_pipe_event_source); - manager_close_idle_pipe(m); - - /* Turn off confirm spawn now */ - m->confirm_spawn = false; - - /* No need to update ask password status when we're going non-interactive */ - manager_close_ask_password(m); - - /* This is no longer the first boot */ - manager_set_first_boot(m, false); - - if (dual_timestamp_is_set(&m->finish_timestamp)) + if (m->test_run) return; - dual_timestamp_get(&m->finish_timestamp); - if (m->running_as == SYSTEMD_SYSTEM && detect_container(NULL) <= 0) { /* Note that m->kernel_usec.monotonic is always at 0, @@ -2641,28 +2576,28 @@ void manager_check_finished(Manager *m) { initrd_usec = m->userspace_timestamp.monotonic - m->initrd_timestamp.monotonic; log_struct(LOG_INFO, - MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED), + LOG_MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED), "KERNEL_USEC="USEC_FMT, kernel_usec, "INITRD_USEC="USEC_FMT, initrd_usec, "USERSPACE_USEC="USEC_FMT, userspace_usec, - "MESSAGE=Startup finished in %s (kernel) + %s (initrd) + %s (userspace) = %s.", - format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC), - format_timespan(initrd, sizeof(initrd), initrd_usec, USEC_PER_MSEC), - format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC), - format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC), + LOG_MESSAGE("Startup finished in %s (kernel) + %s (initrd) + %s (userspace) = %s.", + format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC), + format_timespan(initrd, sizeof(initrd), initrd_usec, USEC_PER_MSEC), + format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC), + format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)), NULL); } else { kernel_usec = m->userspace_timestamp.monotonic - m->kernel_timestamp.monotonic; initrd_usec = 0; log_struct(LOG_INFO, - MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED), + LOG_MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED), "KERNEL_USEC="USEC_FMT, kernel_usec, "USERSPACE_USEC="USEC_FMT, userspace_usec, - "MESSAGE=Startup finished in %s (kernel) + %s (userspace) = %s.", - format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC), - format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC), - format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC), + LOG_MESSAGE("Startup finished in %s (kernel) + %s (userspace) = %s.", + format_timespan(kernel, sizeof(kernel), kernel_usec, USEC_PER_MSEC), + format_timespan(userspace, sizeof(userspace), userspace_usec, USEC_PER_MSEC), + format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)), NULL); } } else { @@ -2670,17 +2605,13 @@ void manager_check_finished(Manager *m) { total_usec = userspace_usec = m->finish_timestamp.monotonic - m->userspace_timestamp.monotonic; log_struct(LOG_INFO, - MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED), + LOG_MESSAGE_ID(SD_MESSAGE_STARTUP_FINISHED), "USERSPACE_USEC="USEC_FMT, userspace_usec, - "MESSAGE=Startup finished in %s.", - format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC), + LOG_MESSAGE("Startup finished in %s.", + format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)), NULL); } - SET_FOREACH(u, m->startup_units, i) - if (u->cgroup_path) - cgroup_context_apply(unit_get_cgroup_context(u), unit_get_cgroup_mask(u), u->cgroup_path, manager_state(m)); - bus_manager_send_finished(m, firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec); sd_notifyf(false, @@ -2689,6 +2620,50 @@ void manager_check_finished(Manager *m) { format_timespan(sum, sizeof(sum), total_usec, USEC_PER_MSEC)); } +void manager_check_finished(Manager *m) { + Unit *u = NULL; + Iterator i; + + assert(m); + + if (m->n_running_jobs == 0) + m->jobs_in_progress_event_source = sd_event_source_unref(m->jobs_in_progress_event_source); + + if (hashmap_size(m->jobs) > 0) { + + if (m->jobs_in_progress_event_source) + sd_event_source_set_time(m->jobs_in_progress_event_source, now(CLOCK_MONOTONIC) + JOBS_IN_PROGRESS_WAIT_USEC); + + return; + } + + manager_flip_auto_status(m, false); + + /* Notify Type=idle units that we are done now */ + m->idle_pipe_event_source = sd_event_source_unref(m->idle_pipe_event_source); + manager_close_idle_pipe(m); + + /* Turn off confirm spawn now */ + m->confirm_spawn = false; + + /* No need to update ask password status when we're going non-interactive */ + manager_close_ask_password(m); + + /* This is no longer the first boot */ + manager_set_first_boot(m, false); + + if (dual_timestamp_is_set(&m->finish_timestamp)) + return; + + dual_timestamp_get(&m->finish_timestamp); + + manager_notify_finished(m); + + SET_FOREACH(u, m->startup_units, i) + if (u->cgroup_path) + cgroup_context_apply(unit_get_cgroup_context(u), unit_get_cgroup_mask(u), u->cgroup_path, manager_state(m)); +} + static int create_generator_dir(Manager *m, char **generator, const char *name) { char *p; int r; @@ -2709,8 +2684,7 @@ static int create_generator_dir(Manager *m, char **generator, const char *name) r = mkdir_p_label(p, 0755); if (r < 0) { - log_error("Failed to create generator directory %s: %s", - p, strerror(-r)); + log_error_errno(r, "Failed to create generator directory %s: %m", p); free(p); return r; } @@ -2726,8 +2700,7 @@ static int create_generator_dir(Manager *m, char **generator, const char *name) r = mkdir_p_label(p, 0755); if (r < 0) { - log_error("Failed to create generator directory %s: %s", - p, strerror(-r)); + log_error_errno(r, "Failed to create generator directory %s: %m", p); free(p); return r; } @@ -2739,7 +2712,7 @@ static int create_generator_dir(Manager *m, char **generator, const char *name) return log_oom(); if (!mkdtemp(p)) { - log_error("Failed to create generator directory %s: %m", + log_error_errno(errno, "Failed to create generator directory %s: %m", p); free(p); return -errno; @@ -2782,7 +2755,7 @@ void manager_run_generators(Manager *m) { if (errno == ENOENT) return; - log_error("Failed to enumerate generator directory %s: %m", + log_error_errno(errno, "Failed to enumerate generator directory %s: %m", generator_path); return; } @@ -2972,12 +2945,14 @@ void manager_set_first_boot(Manager *m, bool b) { void manager_status_printf(Manager *m, StatusType type, const char *status, const char *format, ...) { va_list ap; - if (!manager_get_show_status(m, type)) + /* If m is NULL, assume we're after shutdown and let the messages through. */ + + if (m && !manager_get_show_status(m, type)) return; /* XXX We should totally drop the check for ephemeral here * and thus effectively make 'Type=idle' pointless. */ - if (type == STATUS_TYPE_EPHEMERAL && m->n_on_console > 0) + if (type == STATUS_TYPE_EPHEMERAL && m && m->n_on_console > 0) return; va_start(ap, format); diff --git a/src/core/manager.h b/src/core/manager.h index ab7254849c..ab75f902e5 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -182,6 +182,8 @@ struct Manager { /* Data specific to the mount subsystem */ FILE *proc_self_mountinfo; sd_event_source *mount_event_source; + int utab_inotify_fd; + sd_event_source *mount_utab_event_source; /* Data specific to the swap filesystem */ FILE *proc_swaps; @@ -296,7 +298,7 @@ struct Manager { }; int manager_new(SystemdRunningAs running_as, bool test_run, Manager **m); -void manager_free(Manager *m); +Manager* manager_free(Manager *m); int manager_enumerate(Manager *m); int manager_startup(Manager *m, FILE *serialization, FDSet *fds); diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c index cda25ede13..342f5520c9 100644 --- a/src/core/mount-setup.c +++ b/src/core/mount-setup.c @@ -72,41 +72,45 @@ typedef struct MountPoint { #endif static const MountPoint mount_table[] = { - { "sysfs", "/sys", "sysfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, - NULL, MNT_FATAL|MNT_IN_CONTAINER }, - { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, - NULL, MNT_FATAL|MNT_IN_CONTAINER }, - { "devtmpfs", "/dev", "devtmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, - NULL, MNT_FATAL|MNT_IN_CONTAINER }, - { "securityfs", "/sys/kernel/security", "securityfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, - NULL, MNT_NONE }, + { "sysfs", "/sys", "sysfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + NULL, MNT_FATAL|MNT_IN_CONTAINER }, + { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + NULL, MNT_FATAL|MNT_IN_CONTAINER }, + { "devtmpfs", "/dev", "devtmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, + NULL, MNT_FATAL|MNT_IN_CONTAINER }, + { "securityfs", "/sys/kernel/security", "securityfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + NULL, MNT_NONE }, #ifdef HAVE_SMACK - { "smackfs", "/sys/fs/smackfs", "smackfs", "smackfsdef=*", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, - mac_smack_use, MNT_FATAL }, - { "tmpfs", "/dev/shm", "tmpfs", "mode=1777,smackfsroot=*", MS_NOSUID|MS_NODEV|MS_STRICTATIME, - mac_smack_use, MNT_FATAL }, + { "smackfs", "/sys/fs/smackfs", "smackfs", "smackfsdef=*", MS_NOSUID|MS_NOEXEC|MS_NODEV, + mac_smack_use, MNT_FATAL }, + { "tmpfs", "/dev/shm", "tmpfs", "mode=1777,smackfsroot=*", MS_NOSUID|MS_NODEV|MS_STRICTATIME, + mac_smack_use, MNT_FATAL }, #endif - { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, - NULL, MNT_FATAL|MNT_IN_CONTAINER }, - { "devpts", "/dev/pts", "devpts", "mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC, - NULL, MNT_IN_CONTAINER }, + { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, + NULL, MNT_FATAL|MNT_IN_CONTAINER }, + { "devpts", "/dev/pts", "devpts", "mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC, + NULL, MNT_IN_CONTAINER }, #ifdef HAVE_SMACK - { "tmpfs", "/run", "tmpfs", "mode=755,smackfsroot=*", MS_NOSUID|MS_NODEV|MS_STRICTATIME, - mac_smack_use, MNT_FATAL }, + { "tmpfs", "/run", "tmpfs", "mode=755,smackfsroot=*", MS_NOSUID|MS_NODEV|MS_STRICTATIME, + mac_smack_use, MNT_FATAL }, #endif - { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, - NULL, MNT_FATAL|MNT_IN_CONTAINER }, - { "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, - NULL, MNT_FATAL|MNT_IN_CONTAINER }, - { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd,xattr", MS_NOSUID|MS_NOEXEC|MS_NODEV, - NULL, MNT_IN_CONTAINER }, - { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV, - NULL, MNT_FATAL|MNT_IN_CONTAINER }, - { "pstore", "/sys/fs/pstore", "pstore", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, - NULL, MNT_NONE }, + { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, + NULL, MNT_FATAL|MNT_IN_CONTAINER }, + { "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, + NULL, MNT_FATAL|MNT_IN_CONTAINER }, + { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd,xattr", MS_NOSUID|MS_NOEXEC|MS_NODEV, + NULL, MNT_IN_CONTAINER }, + { "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV, + NULL, MNT_FATAL|MNT_IN_CONTAINER }, + { "pstore", "/sys/fs/pstore", "pstore", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + NULL, MNT_NONE }, #ifdef ENABLE_EFI - { "efivarfs", "/sys/firmware/efi/efivars", "efivarfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, - is_efi_boot, MNT_NONE }, + { "efivarfs", "/sys/firmware/efi/efivars", "efivarfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + is_efi_boot, MNT_NONE }, +#endif +#ifdef ENABLE_KDBUS + { "kdbusfs", "/sys/fs/kdbus", "kdbusfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + NULL, MNT_IN_CONTAINER }, #endif }; @@ -231,7 +235,7 @@ int mount_cgroup_controllers(char ***join_controllers) { f = fopen("/proc/cgroups", "re"); if (!f) { - log_error("Failed to enumerate cgroup controllers: %m"); + log_error_errno(errno, "Failed to enumerate cgroup controllers: %m"); return 0; } @@ -336,10 +340,8 @@ int mount_cgroup_controllers(char ***join_controllers) { return log_oom(); r = symlink(options, t); - if (r < 0 && errno != EEXIST) { - log_error("Failed to create symlink %s: %m", t); - return -errno; - } + if (r < 0 && errno != EEXIST) + return log_error_errno(errno, "Failed to create symlink %s: %m", t); } } } @@ -376,16 +378,20 @@ static int nftw_cb( #endif int mount_setup(bool loaded_policy) { - int r; unsigned i; + int r = 0; for (i = 0; i < ELEMENTSOF(mount_table); i ++) { - r = mount_one(mount_table + i, true); + int j; - if (r < 0) - return r; + j = mount_one(mount_table + i, loaded_policy); + if (r == 0) + r = j; } + if (r < 0) + return r; + #if defined(HAVE_SELINUX) || defined(HAVE_SMACK) /* Nodes in devtmpfs and /run need to be manually updated for * the appropriate labels, after mounting. The other virtual @@ -420,7 +426,7 @@ int mount_setup(bool loaded_policy) { * propagation mode to private if needed. */ if (detect_container(NULL) <= 0) if (mount(NULL, "/", NULL, MS_REC|MS_SHARED, NULL) < 0) - log_warning("Failed to set up the root directory for shared mount propagation: %m"); + log_warning_errno(errno, "Failed to set up the root directory for shared mount propagation: %m"); /* Create a few directories we always want around, Note that * sd_booted() checks for /run/systemd/system, so this mkdir diff --git a/src/core/mount-setup.h b/src/core/mount-setup.h index 4b521ad0e1..b32fbc5a52 100644 --- a/src/core/mount-setup.h +++ b/src/core/mount-setup.h @@ -24,7 +24,6 @@ #include <stdbool.h> int mount_setup_early(void); - int mount_setup(bool loaded_policy); int mount_cgroup_controllers(char ***join_controllers); diff --git a/src/core/mount.c b/src/core/mount.c index 01243c381a..e271d437cb 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -24,6 +24,8 @@ #include <mntent.h> #include <sys/epoll.h> #include <signal.h> +#include <libmount.h> +#include <sys/inotify.h> #include "manager.h" #include "unit.h" @@ -39,10 +41,13 @@ #include "unit-name.h" #include "dbus-mount.h" #include "special.h" -#include "bus-errors.h" +#include "bus-common-errors.h" #include "exit-status.h" #include "def.h" +DEFINE_TRIVIAL_CLEANUP_FUNC(struct libmnt_table*, mnt_free_table); +DEFINE_TRIVIAL_CLEANUP_FUNC(struct libmnt_iter*, mnt_free_iter); + static const UnitActiveState state_translation_table[_MOUNT_STATE_MAX] = { [MOUNT_DEAD] = UNIT_INACTIVE, [MOUNT_MOUNTING] = UNIT_ACTIVATING, @@ -62,19 +67,23 @@ static const UnitActiveState state_translation_table[_MOUNT_STATE_MAX] = { static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata); static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata); -static bool mount_is_network(MountParameters *p) { - assert(p); - - if (mount_test_option(p->options, "_netdev")) +static bool mount_needs_network(const char *options, const char *fstype) { + if (mount_test_option(options, "_netdev")) return true; - if (p->fstype && fstype_is_network(p->fstype)) + if (fstype && fstype_is_network(fstype)) return true; return false; } -static bool mount_is_bind(MountParameters *p) { +static bool mount_is_network(const MountParameters *p) { + assert(p); + + return mount_needs_network(p->options, p->fstype); +} + +static bool mount_is_bind(const MountParameters *p) { assert(p); if (mount_test_option(p->options, "bind")) @@ -92,13 +101,13 @@ static bool mount_is_bind(MountParameters *p) { return false; } -static bool mount_is_auto(MountParameters *p) { +static bool mount_is_auto(const MountParameters *p) { assert(p); return !mount_test_option(p->options, "noauto"); } -static bool needs_quota(MountParameters *p) { +static bool needs_quota(const MountParameters *p) { assert(p); if (mount_is_network(p)) @@ -437,22 +446,22 @@ static int mount_verify(Mount *m) { b = unit_has_name(UNIT(m), e); if (!b) { - log_error_unit(UNIT(m)->id, "%s's Where= setting doesn't match unit name. Refusing.", UNIT(m)->id); + log_unit_error(UNIT(m)->id, "%s's Where= setting doesn't match unit name. Refusing.", UNIT(m)->id); return -EINVAL; } if (mount_point_is_api(m->where) || mount_point_ignore(m->where)) { - log_error_unit(UNIT(m)->id, "Cannot create mount unit for API file system %s. Refusing.", m->where); + log_unit_error(UNIT(m)->id, "Cannot create mount unit for API file system %s. Refusing.", m->where); return -EINVAL; } if (UNIT(m)->fragment_path && !m->parameters_fragment.what) { - log_error_unit(UNIT(m)->id, "%s's What setting is missing. Refusing.", UNIT(m)->id); + log_unit_error(UNIT(m)->id, "%s's What setting is missing. Refusing.", UNIT(m)->id); return -EBADMSG; } if (m->exec_context.pam_name && m->kill_context.kill_mode != KILL_CONTROL_GROUP) { - log_error_unit(UNIT(m)->id, "%s has PAM enabled. Kill mode must be set to control-group'. Refusing.",UNIT(m)->id); + log_unit_error(UNIT(m)->id, "%s has PAM enabled. Kill mode must be set to control-group'. Refusing.",UNIT(m)->id); return -EINVAL; } @@ -597,7 +606,7 @@ static void mount_set_state(Mount *m, MountState state) { } if (state != old_state) - log_debug_unit(UNIT(m)->id, + log_unit_debug(UNIT(m)->id, "%s changed %s -> %s", UNIT(m)->id, mount_state_to_string(old_state), @@ -715,6 +724,7 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { exec_params.confirm_spawn = UNIT(m)->manager->confirm_spawn; exec_params.cgroup_supported = UNIT(m)->manager->cgroup_supported; exec_params.cgroup_path = UNIT(m)->cgroup_path; + exec_params.cgroup_delegate = m->cgroup_context.delegate; exec_params.runtime_prefix = manager_get_runtime_prefix(UNIT(m)->manager); exec_params.unit_id = UNIT(m)->id; @@ -803,7 +813,7 @@ static void mount_enter_signal(Mount *m, MountState state, MountResult f) { return; fail: - log_warning_unit(UNIT(m)->id, + log_unit_warning(UNIT(m)->id, "%s failed to kill processes: %s", UNIT(m)->id, strerror(-r)); if (state == MOUNT_REMOUNTING_SIGTERM || state == MOUNT_REMOUNTING_SIGKILL) @@ -813,31 +823,38 @@ fail: } void warn_if_dir_nonempty(const char *unit, const char* where) { + int r; + assert(unit); assert(where); - if (dir_is_empty(where) > 0) + r = dir_is_empty(where); + if (r > 0) return; - - log_struct_unit(LOG_NOTICE, - unit, - "MESSAGE=%s: Directory %s to mount over is not empty, mounting anyway.", - unit, where, - "WHERE=%s", where, - MESSAGE_ID(SD_MESSAGE_OVERMOUNTING), - NULL); + else if (r == 0) + log_unit_struct(unit, + LOG_NOTICE, + LOG_MESSAGE_ID(SD_MESSAGE_OVERMOUNTING), + LOG_MESSAGE("%s: Directory %s to mount over is not empty, mounting anyway.", + unit, where), + "WHERE=%s", where, + NULL); + else + log_unit_warning(unit, + "MESSAGE=Failed to check directory %s: %s", + where, strerror(-r)); } static int fail_if_symlink(const char *unit, const char* where) { assert(where); if (is_symlink(where) > 0) { - log_struct_unit(LOG_WARNING, - unit, - "MESSAGE=%s: Mount on symlink %s not allowed.", - unit, where, + log_unit_struct(unit, + LOG_ERR, + LOG_MESSAGE_ID(SD_MESSAGE_OVERMOUNTING), + LOG_MESSAGE("%s: Mount on symlink %s not allowed.", + unit, where), "WHERE=%s", where, - MESSAGE_ID(SD_MESSAGE_OVERMOUNTING), NULL); return -ELOOP; @@ -871,7 +888,7 @@ static void mount_enter_unmounting(Mount *m) { return; fail: - log_warning_unit(UNIT(m)->id, + log_unit_warning(UNIT(m)->id, "%s failed to run 'umount' task: %s", UNIT(m)->id, strerror(-r)); mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES); @@ -926,7 +943,7 @@ static void mount_enter_mounting(Mount *m) { return; fail: - log_warning_unit(UNIT(m)->id, + log_unit_warning(UNIT(m)->id, "%s failed to run 'mount' task: %s", UNIT(m)->id, strerror(-r)); mount_enter_dead(m, MOUNT_FAILURE_RESOURCES); @@ -974,7 +991,7 @@ static void mount_enter_remounting(Mount *m) { return; fail: - log_warning_unit(UNIT(m)->id, + log_unit_warning(UNIT(m)->id, "%s failed to run 'remount' task: %s", UNIT(m)->id, strerror(-r)); m->reload_result = MOUNT_FAILURE_RESOURCES; @@ -1078,7 +1095,7 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F MountState state; if ((state = mount_state_from_string(value)) < 0) - log_debug_unit(u->id, "Failed to parse state value %s", value); + log_unit_debug(u->id, "Failed to parse state value %s", value); else m->deserialized_state = state; } else if (streq(key, "result")) { @@ -1086,7 +1103,7 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F f = mount_result_from_string(value); if (f < 0) - log_debug_unit(UNIT(m)->id, + log_unit_debug(UNIT(m)->id, "Failed to parse result value %s", value); else if (f != MOUNT_SUCCESS) m->result = f; @@ -1096,7 +1113,7 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F f = mount_result_from_string(value); if (f < 0) - log_debug_unit(UNIT(m)->id, + log_unit_debug(UNIT(m)->id, "Failed to parse reload result value %s", value); else if (f != MOUNT_SUCCESS) m->reload_result = f; @@ -1105,7 +1122,7 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F pid_t pid; if (parse_pid(value, &pid) < 0) - log_debug_unit(UNIT(m)->id, + log_unit_debug(UNIT(m)->id, "Failed to parse control-pid value %s", value); else m->control_pid = pid; @@ -1113,14 +1130,14 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F MountExecCommand id; if ((id = mount_exec_command_from_string(value)) < 0) - log_debug_unit(UNIT(m)->id, + log_unit_debug(UNIT(m)->id, "Failed to parse exec-command value %s", value); else { m->control_command_id = id; m->control_command = m->exec_command + id; } } else - log_debug_unit(UNIT(m)->id, + log_unit_debug(UNIT(m)->id, "Unknown serialization key '%s'", key); return 0; @@ -1179,7 +1196,8 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) { m->control_command_id = _MOUNT_EXEC_COMMAND_INVALID; } - log_full_unit(f == MOUNT_SUCCESS ? LOG_DEBUG : LOG_NOTICE, u->id, + log_unit_full(u->id, + f == MOUNT_SUCCESS ? LOG_DEBUG : LOG_NOTICE, "%s mount process exited, code=%s status=%i", u->id, sigchld_code_to_string(code), status); @@ -1247,31 +1265,31 @@ static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *user case MOUNT_MOUNTING: case MOUNT_MOUNTING_DONE: - log_warning_unit(UNIT(m)->id, + log_unit_warning(UNIT(m)->id, "%s mounting timed out. Stopping.", UNIT(m)->id); mount_enter_signal(m, MOUNT_MOUNTING_SIGTERM, MOUNT_FAILURE_TIMEOUT); break; case MOUNT_REMOUNTING: - log_warning_unit(UNIT(m)->id, + log_unit_warning(UNIT(m)->id, "%s remounting timed out. Stopping.", UNIT(m)->id); m->reload_result = MOUNT_FAILURE_TIMEOUT; mount_enter_mounted(m, MOUNT_SUCCESS); break; case MOUNT_UNMOUNTING: - log_warning_unit(UNIT(m)->id, + log_unit_warning(UNIT(m)->id, "%s unmounting timed out. Stopping.", UNIT(m)->id); mount_enter_signal(m, MOUNT_UNMOUNTING_SIGTERM, MOUNT_FAILURE_TIMEOUT); break; case MOUNT_MOUNTING_SIGTERM: if (m->kill_context.send_sigkill) { - log_warning_unit(UNIT(m)->id, + log_unit_warning(UNIT(m)->id, "%s mounting timed out. Killing.", UNIT(m)->id); mount_enter_signal(m, MOUNT_MOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT); } else { - log_warning_unit(UNIT(m)->id, + log_unit_warning(UNIT(m)->id, "%s mounting timed out. Skipping SIGKILL. Ignoring.", UNIT(m)->id); @@ -1284,11 +1302,11 @@ static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *user case MOUNT_REMOUNTING_SIGTERM: if (m->kill_context.send_sigkill) { - log_warning_unit(UNIT(m)->id, + log_unit_warning(UNIT(m)->id, "%s remounting timed out. Killing.", UNIT(m)->id); mount_enter_signal(m, MOUNT_REMOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT); } else { - log_warning_unit(UNIT(m)->id, + log_unit_warning(UNIT(m)->id, "%s remounting timed out. Skipping SIGKILL. Ignoring.", UNIT(m)->id); @@ -1301,11 +1319,11 @@ static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *user case MOUNT_UNMOUNTING_SIGTERM: if (m->kill_context.send_sigkill) { - log_warning_unit(UNIT(m)->id, + log_unit_warning(UNIT(m)->id, "%s unmounting timed out. Killing.", UNIT(m)->id); mount_enter_signal(m, MOUNT_UNMOUNTING_SIGKILL, MOUNT_FAILURE_TIMEOUT); } else { - log_warning_unit(UNIT(m)->id, + log_unit_warning(UNIT(m)->id, "%s unmounting timed out. Skipping SIGKILL. Ignoring.", UNIT(m)->id); @@ -1319,7 +1337,7 @@ static int mount_dispatch_timer(sd_event_source *source, usec_t usec, void *user case MOUNT_MOUNTING_SIGKILL: case MOUNT_REMOUNTING_SIGKILL: case MOUNT_UNMOUNTING_SIGKILL: - log_warning_unit(UNIT(m)->id, + log_unit_warning(UNIT(m)->id, "%s mount process still around after SIGKILL. Ignoring.", UNIT(m)->id); @@ -1401,8 +1419,7 @@ static int mount_add_one( if (m->running_as == SYSTEMD_SYSTEM) { const char* target; - target = fstype_is_network(fstype) ? SPECIAL_REMOTE_FS_TARGET : SPECIAL_LOCAL_FS_TARGET; - + target = mount_needs_network(options, fstype) ? SPECIAL_REMOTE_FS_TARGET : SPECIAL_LOCAL_FS_TARGET; r = unit_add_dependency_by_name(u, UNIT_BEFORE, target, NULL, true); if (r < 0) goto fail; @@ -1427,6 +1444,15 @@ static int mount_add_one( } } + if (m->running_as == SYSTEMD_SYSTEM && + mount_needs_network(options, fstype)) { + /* _netdev option may have shown up late, or on a + * remount. Add remote-fs dependencies, even though + * local-fs ones may already be there. */ + unit_add_dependency_by_name(u, UNIT_BEFORE, SPECIAL_REMOTE_FS_TARGET, NULL, true); + load_extras = true; + } + if (u->load_state == UNIT_NOT_FOUND) { u->load_state = UNIT_LOADED; u->load_error = 0; @@ -1492,55 +1518,46 @@ fail: } static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) { + _cleanup_(mnt_free_tablep) struct libmnt_table *tb = NULL; + _cleanup_(mnt_free_iterp) struct libmnt_iter *itr = NULL; + struct libmnt_fs *fs; int r = 0; - unsigned i; assert(m); - rewind(m->proc_self_mountinfo); + tb = mnt_new_table(); + itr = mnt_new_iter(MNT_ITER_FORWARD); + if (!tb || !itr) + return log_oom(); + + r = mnt_table_parse_mtab(tb, NULL); + if (r < 0) + return r; - for (i = 1;; i++) { - _cleanup_free_ char *device = NULL, *path = NULL, *options = NULL, *options2 = NULL, *fstype = NULL, *d = NULL, *p = NULL, *o = NULL; + r = 0; + for (;;) { + const char *device, *path, *options, *fstype; + _cleanup_free_ const char *d = NULL, *p = NULL; int k; - k = fscanf(m->proc_self_mountinfo, - "%*s " /* (1) mount id */ - "%*s " /* (2) parent id */ - "%*s " /* (3) major:minor */ - "%*s " /* (4) root */ - "%ms " /* (5) mount point */ - "%ms" /* (6) mount options */ - "%*[^-]" /* (7) optional fields */ - "- " /* (8) separator */ - "%ms " /* (9) file system type */ - "%ms" /* (10) mount source */ - "%ms" /* (11) mount options 2 */ - "%*[^\n]", /* some rubbish at the end */ - &path, - &options, - &fstype, - &device, - &options2); - - if (k == EOF) + k = mnt_table_next_fs(tb, itr, &fs); + if (k == 1) break; + else if (k < 0) + return log_error_errno(k, "Failed to get next entry from /etc/fstab: %m"); - if (k != 5) { - log_warning("Failed to parse /proc/self/mountinfo:%u.", i); - continue; - } - - o = strjoin(options, ",", options2, NULL); - if (!o) - return log_oom(); + device = mnt_fs_get_source(fs); + path = mnt_fs_get_target(fs); + options = mnt_fs_get_options(fs); + fstype = mnt_fs_get_fstype(fs); d = cunescape(device); p = cunescape(path); if (!d || !p) return log_oom(); - k = mount_add_one(m, d, p, o, fstype, set_flags); - if (k < 0) + k = mount_add_one(m, d, p, options, fstype, set_flags); + if (r == 0 && k < 0) r = k; } @@ -1551,11 +1568,13 @@ static void mount_shutdown(Manager *m) { assert(m); m->mount_event_source = sd_event_source_unref(m->mount_event_source); + m->mount_utab_event_source = sd_event_source_unref(m->mount_utab_event_source); if (m->proc_self_mountinfo) { fclose(m->proc_self_mountinfo); m->proc_self_mountinfo = NULL; } + m->utab_inotify_fd = safe_close(m->utab_inotify_fd); } static int mount_get_timeout(Unit *u, uint64_t *timeout) { @@ -1576,6 +1595,8 @@ static int mount_enumerate(Manager *m) { int r; assert(m); + mnt_init_debug(0); + if (!m->proc_self_mountinfo) { m->proc_self_mountinfo = fopen("/proc/self/mountinfo", "re"); if (!m->proc_self_mountinfo) @@ -1593,6 +1614,30 @@ static int mount_enumerate(Manager *m) { goto fail; } + if (m->utab_inotify_fd < 0) { + m->utab_inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); + if (m->utab_inotify_fd < 0) { + r = -errno; + goto fail; + } + + (void) mkdir_p_label("/run/mount", 0755); + + r = inotify_add_watch(m->utab_inotify_fd, "/run/mount", IN_MOVED_TO); + if (r < 0) { + r = -errno; + goto fail; + } + + r = sd_event_add_io(m->event, &m->mount_utab_event_source, m->utab_inotify_fd, EPOLLIN, mount_dispatch_io, m); + if (r < 0) + goto fail; + + r = sd_event_source_set_priority(m->mount_utab_event_source, -10); + if (r < 0) + goto fail; + } + r = mount_load_proc_self_mountinfo(m, false); if (r < 0) goto fail; @@ -1610,15 +1655,52 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, int r; assert(m); - assert(revents & EPOLLPRI); + assert(revents & (EPOLLPRI | EPOLLIN)); /* The manager calls this for every fd event happening on the * /proc/self/mountinfo file, which informs us about mounting - * table changes */ + * table changes, and for /run/mount events which we watch + * for mount options. */ + + if (fd == m->utab_inotify_fd) { + bool rescan = false; + + /* FIXME: We *really* need to replace this with + * libmount's own API for this, we should not hardcode + * internal behaviour of libmount here. */ + + for (;;) { + uint8_t buffer[INOTIFY_EVENT_MAX] _alignas_(struct inotify_event); + struct inotify_event *e; + ssize_t l; + + l = read(fd, buffer, sizeof(buffer)); + if (l < 0) { + if (errno == EAGAIN || errno == EINTR) + break; + + log_error_errno(errno, "Failed to read utab inotify: %m"); + break; + } + + FOREACH_INOTIFY_EVENT(e, buffer, l) { + /* Only care about changes to utab, + * but we have to monitor the + * directory to reliably get + * notifications about when utab is + * replaced using rename(2) */ + if ((e->mask & IN_Q_OVERFLOW) || streq(e->name, "utab")) + rescan = true; + } + } + + if (!rescan) + return 0; + } r = mount_load_proc_self_mountinfo(m, true); if (r < 0) { - log_error("Failed to reread /proc/self/mountinfo: %s", strerror(-r)); + log_error_errno(r, "Failed to reread /proc/self/mountinfo: %m"); /* Reset flags, just in case, for later calls */ LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_MOUNT]) { diff --git a/src/core/namespace.c b/src/core/namespace.c index 4bc288de1d..4c411096a1 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -145,7 +145,7 @@ static int mount_dev(BindMount *m) { "/dev/tty\0"; char temporary_mount[] = "/tmp/namespace-dev-XXXXXX"; - const char *d, *dev = NULL, *devpts = NULL, *devshm = NULL, *devkdbus = NULL, *devhugepages = NULL, *devmqueue = NULL, *devlog = NULL, *devptmx = NULL; + const char *d, *dev = NULL, *devpts = NULL, *devshm = NULL, *devhugepages = NULL, *devmqueue = NULL, *devlog = NULL, *devptmx = NULL; _cleanup_umask_ mode_t u; int r; @@ -157,14 +157,14 @@ static int mount_dev(BindMount *m) { return -errno; dev = strappenda(temporary_mount, "/dev"); - mkdir(dev, 0755); + (void)mkdir(dev, 0755); if (mount("tmpfs", dev, "tmpfs", MS_NOSUID|MS_STRICTATIME, "mode=755") < 0) { r = -errno; goto fail; } devpts = strappenda(temporary_mount, "/dev/pts"); - mkdir(devpts, 0755); + (void)mkdir(devpts, 0755); if (mount("/dev/pts", devpts, NULL, MS_BIND, NULL) < 0) { r = -errno; goto fail; @@ -174,7 +174,7 @@ static int mount_dev(BindMount *m) { symlink("pts/ptmx", devptmx); devshm = strappenda(temporary_mount, "/dev/shm"); - mkdir(devshm, 01777); + (void)mkdir(devshm, 01777); r = mount("/dev/shm", devshm, NULL, MS_BIND, NULL); if (r < 0) { r = -errno; @@ -182,15 +182,11 @@ static int mount_dev(BindMount *m) { } devmqueue = strappenda(temporary_mount, "/dev/mqueue"); - mkdir(devmqueue, 0755); + (void)mkdir(devmqueue, 0755); mount("/dev/mqueue", devmqueue, NULL, MS_BIND, NULL); - devkdbus = strappenda(temporary_mount, "/dev/kdbus"); - mkdir(devkdbus, 0755); - mount("/dev/kdbus", devkdbus, NULL, MS_BIND, NULL); - devhugepages = strappenda(temporary_mount, "/dev/hugepages"); - mkdir(devhugepages, 0755); + (void)mkdir(devhugepages, 0755); mount("/dev/hugepages", devhugepages, NULL, MS_BIND, NULL); devlog = strappenda(temporary_mount, "/dev/log"); @@ -254,9 +250,6 @@ fail: if (devshm) umount(devshm); - if (devkdbus) - umount(devkdbus); - if (devhugepages) umount(devhugepages); @@ -283,13 +276,11 @@ static int mount_kdbus(BindMount *m) { u = umask(0000); - if (!mkdtemp(temporary_mount)) { - log_error("Failed create temp dir: %m"); - return -errno; - } + if (!mkdtemp(temporary_mount)) + return log_error_errno(errno, "Failed create temp dir: %m"); root = strappenda(temporary_mount, "/kdbus"); - mkdir(root, 0755); + (void)mkdir(root, 0755); if (mount("tmpfs", root, "tmpfs", MS_NOSUID|MS_STRICTATIME, "mode=777") < 0) { r = -errno; goto fail; @@ -298,21 +289,21 @@ static int mount_kdbus(BindMount *m) { /* create a new /dev/null dev node copy so we have some fodder to * bind-mount the custom endpoint over. */ if (stat("/dev/null", &st) < 0) { - log_error("Failed to stat /dev/null: %m"); + log_error_errno(errno, "Failed to stat /dev/null: %m"); r = -errno; goto fail; } busnode = strappenda(root, "/bus"); if (mknod(busnode, (st.st_mode & ~07777) | 0600, st.st_rdev) < 0) { - log_error("mknod() for %s failed: %m", busnode); + log_error_errno(errno, "mknod() for %s failed: %m", busnode); r = -errno; goto fail; } r = mount(m->path, busnode, "bind", MS_BIND, NULL); if (r < 0) { - log_error("bind mount of %s failed: %m", m->path); + log_error_errno(errno, "bind mount of %s failed: %m", m->path); r = -errno; goto fail; } @@ -324,7 +315,7 @@ static int mount_kdbus(BindMount *m) { } if (mount(root, basepath, NULL, MS_MOVE, NULL) < 0) { - log_error("bind mount of %s failed: %m", basepath); + log_error_errno(errno, "bind mount of %s failed: %m", basepath); r = -errno; goto fail; } diff --git a/src/core/path.c b/src/core/path.c index f54c77f6c3..656ed6941d 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -136,7 +136,7 @@ int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) { } if (!exists) { - log_error("Failed to add watch on any of the components of %s: %m", + log_error_errno(errno, "Failed to add watch on any of the components of %s: %m", s->path); r = -errno; /* either EACCESS or ENOENT */ goto fail; @@ -157,10 +157,9 @@ void path_spec_unwatch(PathSpec *s) { } int path_spec_fd_event(PathSpec *s, uint32_t revents) { - _cleanup_free_ uint8_t *buf = NULL; + uint8_t buffer[INOTIFY_EVENT_MAX] _alignas_(struct inotify_event); struct inotify_event *e; - ssize_t k; - int l; + ssize_t l; int r = 0; if (revents != EPOLLIN) { @@ -168,37 +167,18 @@ int path_spec_fd_event(PathSpec *s, uint32_t revents) { return -EINVAL; } - if (ioctl(s->inotify_fd, FIONREAD, &l) < 0) { - log_error("FIONREAD failed: %m"); - return -errno; - } - - assert(l > 0); + l = read(s->inotify_fd, buffer, sizeof(buffer)); + if (l < 0) { + if (errno == EAGAIN || errno == EINTR) + return 0; - buf = malloc(l); - if (!buf) - return log_oom(); - - k = read(s->inotify_fd, buf, l); - if (k < 0) { - log_error("Failed to read inotify event: %m"); - return -errno; + return log_error_errno(errno, "Failed to read inotify event: %m"); } - e = (struct inotify_event*) buf; - - while (k > 0) { - size_t step; - + FOREACH_INOTIFY_EVENT(e, buffer, l) { if ((s->type == PATH_CHANGED || s->type == PATH_MODIFIED) && s->primary_wd == e->wd) r = 1; - - step = sizeof(struct inotify_event) + e->len; - assert(step <= (size_t) k); - - e = (struct inotify_event*) ((uint8_t*) e + step); - k -= step; } return r; @@ -250,7 +230,7 @@ static void path_spec_mkdir(PathSpec *s, mode_t mode) { r = mkdir_p_label(s->path, mode); if (r < 0) - log_warning("mkdir(%s) failed: %s", s->path, strerror(-r)); + log_warning_errno(r, "mkdir(%s) failed: %m", s->path); } static void path_spec_dump(PathSpec *s, FILE *f, const char *prefix) { @@ -320,7 +300,7 @@ static int path_verify(Path *p) { return 0; if (!p->specs) { - log_error_unit(UNIT(p)->id, + log_unit_error(UNIT(p)->id, "%s lacks path setting. Refusing.", UNIT(p)->id); return -EINVAL; } @@ -562,8 +542,7 @@ static void path_enter_waiting(Path *p, bool initial, bool recheck) { return; fail: - log_warning("%s failed to enter waiting state: %s", - UNIT(p)->id, strerror(-r)); + log_warning_errno(r, "%s failed to enter waiting state: %m", UNIT(p)->id); path_enter_dead(p, PATH_FAILURE_RESOURCES); } @@ -724,7 +703,7 @@ static void path_trigger_notify(Unit *u, Unit *other) { if (p->state == PATH_RUNNING && UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) { - log_debug_unit(UNIT(p)->id, + log_unit_debug(UNIT(p)->id, "%s got notified about unit deactivation.", UNIT(p)->id); diff --git a/src/core/scope.c b/src/core/scope.c index 0f7c1f97ce..e0da6e4db7 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -133,7 +133,7 @@ static int scope_verify(Scope *s) { return 0; if (set_isempty(UNIT(s)->pids) && UNIT(s)->manager->n_reloading <= 0) { - log_error_unit(UNIT(s)->id, "Scope %s has no PIDs. Refusing.", UNIT(s)->id); + log_unit_error(UNIT(s)->id, "Scope %s has no PIDs. Refusing.", UNIT(s)->id); return -EINVAL; } @@ -264,7 +264,7 @@ static void scope_enter_signal(Scope *s, ScopeState state, ScopeResult f) { return; fail: - log_warning_unit(UNIT(s)->id, + log_unit_warning(UNIT(s)->id, "%s failed to kill processes: %s", UNIT(s)->id, strerror(-r)); scope_enter_dead(s, SCOPE_FAILURE_RESOURCES); @@ -288,13 +288,7 @@ static int scope_start(Unit *u) { if (!u->transient && UNIT(s)->manager->n_reloading <= 0) return -ENOENT; - r = unit_realize_cgroup(u); - if (r < 0) { - log_error("Failed to realize cgroup: %s", strerror(-r)); - return r; - } - - r = cg_attach_many_everywhere(u->manager->cgroup_supported, u->cgroup_path, UNIT(s)->pids); + r = unit_attach_pids_to_cgroup(u); if (r < 0) return r; @@ -384,15 +378,14 @@ static int scope_deserialize_item(Unit *u, const char *key, const char *value, F } static bool scope_check_gc(Unit *u) { - Scope *s = SCOPE(u); - int r; - - assert(s); + assert(u); /* Never clean up scopes that still have a process around, * even if the scope is formally dead. */ if (u->cgroup_path) { + int r; + r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true); if (r <= 0) return true; @@ -405,7 +398,7 @@ static void scope_notify_cgroup_empty_event(Unit *u) { Scope *s = SCOPE(u); assert(u); - log_debug_unit(u->id, "%s: cgroup is empty", u->id); + log_unit_debug(u->id, "%s: cgroup is empty", u->id); if (IN_SET(s->state, SCOPE_RUNNING, SCOPE_ABANDONED, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL)) scope_enter_dead(s, SCOPE_SUCCESS); @@ -437,17 +430,17 @@ static int scope_dispatch_timer(sd_event_source *source, usec_t usec, void *user case SCOPE_STOP_SIGTERM: if (s->kill_context.send_sigkill) { - log_warning_unit(UNIT(s)->id, "%s stopping timed out. Killing.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s stopping timed out. Killing.", UNIT(s)->id); scope_enter_signal(s, SCOPE_STOP_SIGKILL, SCOPE_FAILURE_TIMEOUT); } else { - log_warning_unit(UNIT(s)->id, "%s stopping timed out. Skipping SIGKILL.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s stopping timed out. Skipping SIGKILL.", UNIT(s)->id); scope_enter_dead(s, SCOPE_FAILURE_TIMEOUT); } break; case SCOPE_STOP_SIGKILL: - log_warning_unit(UNIT(s)->id, "%s still around after SIGKILL. Ignoring.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s still around after SIGKILL. Ignoring.", UNIT(s)->id); scope_enter_dead(s, SCOPE_FAILURE_TIMEOUT); break; diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c index a4694b33f3..b45a4513e2 100644 --- a/src/core/selinux-access.c +++ b/src/core/selinux-access.c @@ -112,7 +112,7 @@ _printf_(2, 3) static int log_callback(int type, const char *fmt, ...) { #endif va_start(ap, fmt); - log_metav(LOG_USER | LOG_INFO, __FILE__, __LINE__, __FUNCTION__, fmt, ap); + log_internalv(LOG_AUTH | LOG_INFO, 0, __FILE__, __LINE__, __FUNCTION__, fmt, ap); va_end(ap); return 0; @@ -126,10 +126,8 @@ _printf_(2, 3) static int log_callback(int type, const char *fmt, ...) { static int access_init(void) { int r = 0; - if (avc_open(NULL, 0)) { - log_error("avc_open() failed: %m"); - return -errno; - } + if (avc_open(NULL, 0)) + return log_error_errno(errno, "avc_open() failed: %m"); selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback); selinux_set_callback(SELINUX_CB_LOG, (union selinux_callback) log_callback); @@ -207,7 +205,8 @@ int mac_selinux_generic_access_check( message, SD_BUS_CREDS_PID|SD_BUS_CREDS_UID|SD_BUS_CREDS_GID| SD_BUS_CREDS_CMDLINE|SD_BUS_CREDS_AUDIT_LOGIN_UID| - SD_BUS_CREDS_SELINUX_CONTEXT, + SD_BUS_CREDS_SELINUX_CONTEXT| + SD_BUS_CREDS_AUGMENT /* get more bits from /proc */, &creds); if (r < 0) goto finish; diff --git a/src/core/selinux-access.h b/src/core/selinux-access.h index bccf0d2913..dd1e8bb9d0 100644 --- a/src/core/selinux-access.h +++ b/src/core/selinux-access.h @@ -40,7 +40,7 @@ int mac_selinux_unit_access_check_strv(char **units, sd_bus_message *message, Ma #define mac_selinux_unit_access_check(unit, message, permission, error) \ ({ \ Unit *_unit = (unit); \ - mac_selinux_generic_access_check((message), _unit->fragment_path ?: _unit->fragment_path, (permission), (error)); \ + mac_selinux_generic_access_check((message), _unit->source_path ?: _unit->fragment_path, (permission), (error)); \ }) #else diff --git a/src/core/selinux-setup.c b/src/core/selinux-setup.c index 25e22b6c77..fba915d7da 100644 --- a/src/core/selinux-setup.c +++ b/src/core/selinux-setup.c @@ -113,7 +113,7 @@ int mac_selinux_setup(bool *loaded_policy) { if (enforce > 0) { if (!initialized) { - log_error("Failed to load SELinux policy. Freezing."); + log_emergency("Failed to load SELinux policy."); return -EIO; } diff --git a/src/core/service.c b/src/core/service.c index f27e63eb9a..bfbe959edb 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -137,7 +137,7 @@ static void service_unwatch_pid_file(Service *s) { if (!s->pid_file_pathspec) return; - log_debug_unit(UNIT(s)->id, "Stopping watch for %s's PID file %s", UNIT(s)->id, s->pid_file_pathspec->path); + log_unit_debug(UNIT(s)->id, "Stopping watch for %s's PID file %s", UNIT(s)->id, s->pid_file_pathspec->path); path_spec_unwatch(s->pid_file_pathspec); path_spec_done(s->pid_file_pathspec); free(s->pid_file_pathspec); @@ -167,7 +167,7 @@ static int service_set_main_pid(Service *s, pid_t pid) { s->main_pid_known = true; if (get_parent_of_pid(pid, &ppid) >= 0 && ppid != getpid()) { - log_warning_unit(UNIT(s)->id, "%s: Supervising process "PID_FMT" which is not our child. We'll most likely not notice when it exits.", UNIT(s)->id, pid); + log_unit_warning(UNIT(s)->id, "%s: Supervising process "PID_FMT" which is not our child. We'll most likely not notice when it exits.", UNIT(s)->id, pid); s->main_pid_alien = true; } else s->main_pid_alien = false; @@ -209,7 +209,7 @@ static void service_start_watchdog(Service *s) { if (s->watchdog_event_source) { r = sd_event_source_set_time(s->watchdog_event_source, s->watchdog_timestamp.monotonic + s->watchdog_usec); if (r < 0) { - log_warning_unit(UNIT(s)->id, "%s failed to reset watchdog timer: %s", UNIT(s)->id, strerror(-r)); + log_unit_warning_errno(UNIT(s)->id, r, "%s failed to reset watchdog timer: %m", UNIT(s)->id); return; } @@ -222,7 +222,7 @@ static void service_start_watchdog(Service *s) { s->watchdog_timestamp.monotonic + s->watchdog_usec, 0, service_dispatch_watchdog, s); if (r < 0) { - log_warning_unit(UNIT(s)->id, "%s failed to add watchdog timer: %s", UNIT(s)->id, strerror(-r)); + log_unit_warning_errno(UNIT(s)->id, r, "%s failed to add watchdog timer: %m", UNIT(s)->id); return; } @@ -232,7 +232,7 @@ static void service_start_watchdog(Service *s) { } if (r < 0) - log_warning_unit(UNIT(s)->id, "%s failed to install watchdog timer: %s", UNIT(s)->id, strerror(-r)); + log_unit_warning_errno(UNIT(s)->id, r, "%s failed to install watchdog timer: %m", UNIT(s)->id); } static void service_reset_watchdog(Service *s) { @@ -316,45 +316,45 @@ static int service_verify(Service *s) { return 0; if (!s->exec_command[SERVICE_EXEC_START] && !s->exec_command[SERVICE_EXEC_STOP]) { - log_error_unit(UNIT(s)->id, "%s lacks both ExecStart= and ExecStop= setting. Refusing.", UNIT(s)->id); + log_unit_error(UNIT(s)->id, "%s lacks both ExecStart= and ExecStop= setting. Refusing.", UNIT(s)->id); return -EINVAL; } if (s->type != SERVICE_ONESHOT && !s->exec_command[SERVICE_EXEC_START]) { - log_error_unit(UNIT(s)->id, "%s has no ExecStart= setting, which is only allowed for Type=oneshot services. Refusing.", UNIT(s)->id); + log_unit_error(UNIT(s)->id, "%s has no ExecStart= setting, which is only allowed for Type=oneshot services. Refusing.", UNIT(s)->id); return -EINVAL; } if (!s->remain_after_exit && !s->exec_command[SERVICE_EXEC_START]) { - log_error_unit(UNIT(s)->id, "%s has no ExecStart= setting, which is only allowed for RemainAfterExit=yes services. Refusing.", UNIT(s)->id); + log_unit_error(UNIT(s)->id, "%s has no ExecStart= setting, which is only allowed for RemainAfterExit=yes services. Refusing.", UNIT(s)->id); return -EINVAL; } if (s->type != SERVICE_ONESHOT && s->exec_command[SERVICE_EXEC_START]->command_next) { - log_error_unit(UNIT(s)->id, "%s has more than one ExecStart= setting, which is only allowed for Type=oneshot services. Refusing.", UNIT(s)->id); + log_unit_error(UNIT(s)->id, "%s has more than one ExecStart= setting, which is only allowed for Type=oneshot services. Refusing.", UNIT(s)->id); return -EINVAL; } if (s->type == SERVICE_ONESHOT && s->restart != SERVICE_RESTART_NO) { - log_error_unit(UNIT(s)->id, "%s has Restart= setting other than no, which isn't allowed for Type=oneshot services. Refusing.", UNIT(s)->id); + log_unit_error(UNIT(s)->id, "%s has Restart= setting other than no, which isn't allowed for Type=oneshot services. Refusing.", UNIT(s)->id); return -EINVAL; } if (s->type == SERVICE_ONESHOT && !exit_status_set_is_empty(&s->restart_force_status)) { - log_error_unit(UNIT(s)->id, "%s has RestartForceStatus= set, which isn't allowed for Type=oneshot services. Refusing.", UNIT(s)->id); + log_unit_error(UNIT(s)->id, "%s has RestartForceStatus= set, which isn't allowed for Type=oneshot services. Refusing.", UNIT(s)->id); return -EINVAL; } if (s->type == SERVICE_DBUS && !s->bus_name) { - log_error_unit(UNIT(s)->id, "%s is of type D-Bus but no D-Bus service name has been specified. Refusing.", UNIT(s)->id); + log_unit_error(UNIT(s)->id, "%s is of type D-Bus but no D-Bus service name has been specified. Refusing.", UNIT(s)->id); return -EINVAL; } if (s->bus_name && s->type != SERVICE_DBUS) - log_warning_unit(UNIT(s)->id, "%s has a D-Bus service name specified, but is not of type dbus. Ignoring.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s has a D-Bus service name specified, but is not of type dbus. Ignoring.", UNIT(s)->id); if (s->exec_context.pam_name && !(s->kill_context.kill_mode == KILL_CONTROL_GROUP || s->kill_context.kill_mode == KILL_MIXED)) { - log_error_unit(UNIT(s)->id, "%s has PAM enabled. Kill mode must be set to 'control-group' or 'mixed'. Refusing.", UNIT(s)->id); + log_unit_error(UNIT(s)->id, "%s has PAM enabled. Kill mode must be set to 'control-group' or 'mixed'. Refusing.", UNIT(s)->id); return -EINVAL; } @@ -546,13 +546,6 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { exec_command_dump_list(s->exec_command[c], f, prefix2); } -#ifdef HAVE_SYSV_COMPAT - if (s->sysv_start_priority >= 0) - fprintf(f, - "%sSysVStartPriority: %i\n", - prefix, s->sysv_start_priority); -#endif - if (s->status_text) fprintf(f, "%sStatus Text: %s\n", prefix, s->status_text); @@ -571,20 +564,20 @@ static int service_load_pid_file(Service *s, bool may_warn) { r = read_one_line_file(s->pid_file, &k); if (r < 0) { if (may_warn) - log_info_unit(UNIT(s)->id, "PID file %s not readable (yet?) after %s.", s->pid_file, service_state_to_string(s->state)); + log_unit_info(UNIT(s)->id, "PID file %s not readable (yet?) after %s.", s->pid_file, service_state_to_string(s->state)); return r; } r = parse_pid(k, &pid); if (r < 0) { if (may_warn) - log_info_unit(UNIT(s)->id, "Failed to read PID from file %s: %s", s->pid_file, strerror(-r)); + log_unit_info_errno(UNIT(s)->id, r, "Failed to read PID from file %s: %m", s->pid_file); return r; } if (!pid_is_alive(pid)) { if (may_warn) - log_info_unit(UNIT(s)->id, "PID "PID_FMT" read from file %s does not exist or is a zombie.", pid, s->pid_file); + log_unit_info(UNIT(s)->id, "PID "PID_FMT" read from file %s does not exist or is a zombie.", pid, s->pid_file); return -ESRCH; } @@ -592,12 +585,12 @@ static int service_load_pid_file(Service *s, bool may_warn) { if (pid == s->main_pid) return 0; - log_debug_unit(UNIT(s)->id, "Main PID changing: "PID_FMT" -> "PID_FMT, s->main_pid, pid); + log_unit_debug(UNIT(s)->id, "Main PID changing: "PID_FMT" -> "PID_FMT, s->main_pid, pid); service_unwatch_main_pid(s); s->main_pid_known = false; } else - log_debug_unit(UNIT(s)->id, "Main PID loaded: "PID_FMT, pid); + log_unit_debug(UNIT(s)->id, "Main PID loaded: "PID_FMT, pid); r = service_set_main_pid(s, pid); if (r < 0) @@ -606,7 +599,7 @@ static int service_load_pid_file(Service *s, bool may_warn) { r = unit_watch_pid(UNIT(s), pid); if (r < 0) { /* FIXME: we need to do something here */ - log_warning_unit(UNIT(s)->id, "Failed to watch PID "PID_FMT" from service %s", pid, UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "Failed to watch PID "PID_FMT" from service %s", pid, UNIT(s)->id); return r; } @@ -633,7 +626,7 @@ static int service_search_main_pid(Service *s) { if (pid <= 0) return -ENOENT; - log_debug_unit(UNIT(s)->id, "Main PID guessed: "PID_FMT, pid); + log_unit_debug(UNIT(s)->id, "Main PID guessed: "PID_FMT, pid); r = service_set_main_pid(s, pid); if (r < 0) return r; @@ -641,7 +634,7 @@ static int service_search_main_pid(Service *s) { r = unit_watch_pid(UNIT(s), pid); if (r < 0) { /* FIXME: we need to do something here */ - log_warning_unit(UNIT(s)->id, "Failed to watch PID "PID_FMT" from service %s", pid, UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "Failed to watch PID "PID_FMT" from service %s", pid, UNIT(s)->id); return r; } @@ -710,7 +703,7 @@ static void service_set_state(Service *s, ServiceState state) { /* For the inactive states unit_notify() will trim the cgroup, * but for exit we have to do that ourselves... */ if (state == SERVICE_EXITED && UNIT(s)->manager->n_reloading <= 0) - unit_destroy_cgroup(UNIT(s)); + unit_destroy_cgroup_if_empty(UNIT(s)); /* For remain_after_exit services, let's see if we can "release" the * hold on the console, since unit_notify() only does that in case of @@ -733,7 +726,7 @@ static void service_set_state(Service *s, ServiceState state) { } if (old_state != state) - log_debug_unit(UNIT(s)->id, "%s changed %s -> %s", UNIT(s)->id, service_state_to_string(old_state), service_state_to_string(state)); + log_unit_debug(UNIT(s)->id, "%s changed %s -> %s", UNIT(s)->id, service_state_to_string(old_state), service_state_to_string(state)); unit_notify(UNIT(s), table[old_state], table[state], s->reload_result == SERVICE_SUCCESS); s->reload_result = SERVICE_SUCCESS; @@ -1000,6 +993,7 @@ static int service_spawn( exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn; exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported; exec_params.cgroup_path = path; + exec_params.cgroup_delegate = s->cgroup_context.delegate; exec_params.runtime_prefix = manager_get_runtime_prefix(UNIT(s)->manager); exec_params.unit_id = UNIT(s)->id; exec_params.watchdog_usec = s->watchdog_usec; @@ -1087,7 +1081,7 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) service_set_state(s, s->result != SERVICE_SUCCESS ? SERVICE_FAILED : SERVICE_DEAD); if (s->result != SERVICE_SUCCESS) { - log_warning_unit(UNIT(s)->id, "%s failed.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s failed.", UNIT(s)->id); failure_action(UNIT(s)->manager, s->failure_action, s->reboot_arg); } @@ -1129,7 +1123,7 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) return; fail: - log_warning_unit(UNIT(s)->id, "%s failed to run install restart timer: %s", UNIT(s)->id, strerror(-r)); + log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run install restart timer: %m", UNIT(s)->id); service_enter_dead(s, SERVICE_FAILURE_RESOURCES, false); } @@ -1166,7 +1160,7 @@ static void service_enter_stop_post(Service *s, ServiceResult f) { return; fail: - log_warning_unit(UNIT(s)->id, "%s failed to run 'stop-post' task: %s", UNIT(s)->id, strerror(-r)); + log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run 'stop-post' task: %m", UNIT(s)->id); service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); } @@ -1212,7 +1206,7 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f return; fail: - log_warning_unit(UNIT(s)->id, "%s failed to kill processes: %s", UNIT(s)->id, strerror(-r)); + log_unit_warning_errno(UNIT(s)->id, r, "%s failed to kill processes: %m", UNIT(s)->id); if (state == SERVICE_STOP_SIGTERM || state == SERVICE_STOP_SIGKILL || state == SERVICE_STOP_SIGABRT) @@ -1267,7 +1261,7 @@ static void service_enter_stop(Service *s, ServiceResult f) { return; fail: - log_warning_unit(UNIT(s)->id, "%s failed to run 'stop' task: %s", UNIT(s)->id, strerror(-r)); + log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run 'stop' task: %m", UNIT(s)->id); service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_RESOURCES); } @@ -1329,7 +1323,7 @@ static void service_enter_start_post(Service *s) { return; fail: - log_warning_unit(UNIT(s)->id, "%s failed to run 'start-post' task: %s", UNIT(s)->id, strerror(-r)); + log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run 'start-post' task: %m", UNIT(s)->id); service_enter_stop(s, SERVICE_FAILURE_RESOURCES); } @@ -1422,7 +1416,7 @@ static void service_enter_start(Service *s) { return; fail: - log_warning_unit(UNIT(s)->id, "%s failed to run 'start' task: %s", UNIT(s)->id, strerror(-r)); + log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run 'start' task: %m", UNIT(s)->id); service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); } @@ -1460,7 +1454,7 @@ static void service_enter_start_pre(Service *s) { return; fail: - log_warning_unit(UNIT(s)->id, "%s failed to run 'start-pre' task: %s", UNIT(s)->id, strerror(-r)); + log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run 'start-pre' task: %m", UNIT(s)->id); service_enter_dead(s, SERVICE_FAILURE_RESOURCES, true); } @@ -1472,7 +1466,7 @@ static void service_enter_restart(Service *s) { if (UNIT(s)->job && UNIT(s)->job->type == JOB_STOP) { /* Don't restart things if we are going down anyway */ - log_info_unit(UNIT(s)->id, "Stop job pending for unit, delaying automatic restart."); + log_unit_info(UNIT(s)->id, "Stop job pending for unit, delaying automatic restart."); r = service_arm_timer(s, s->restart_usec); if (r < 0) @@ -1493,11 +1487,11 @@ static void service_enter_restart(Service *s) { * it will be canceled as part of the service_stop() call that * is executed as part of JOB_RESTART. */ - log_debug_unit(UNIT(s)->id, "%s scheduled restart job.", UNIT(s)->id); + log_unit_debug(UNIT(s)->id, "%s scheduled restart job.", UNIT(s)->id); return; fail: - log_warning_unit(UNIT(s)->id, "%s failed to schedule restart job: %s", UNIT(s)->id, bus_error_message(&error, -r)); + log_unit_warning(UNIT(s)->id, "%s failed to schedule restart job: %s", UNIT(s)->id, bus_error_message(&error, -r)); service_enter_dead(s, SERVICE_FAILURE_RESOURCES, false); } @@ -1540,7 +1534,7 @@ static void service_enter_reload(Service *s) { return; fail: - log_warning_unit(UNIT(s)->id, "%s failed to run 'reload' task: %s", UNIT(s)->id, strerror(-r)); + log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run 'reload' task: %m", UNIT(s)->id); s->reload_result = SERVICE_FAILURE_RESOURCES; service_enter_running(s, SERVICE_SUCCESS); } @@ -1573,7 +1567,7 @@ static void service_run_next_control(Service *s) { return; fail: - log_warning_unit(UNIT(s)->id, "%s failed to run next control task: %s", UNIT(s)->id, strerror(-r)); + log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run next control task: %m", UNIT(s)->id); if (s->state == SERVICE_START_PRE) service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_RESOURCES); @@ -1617,7 +1611,7 @@ static void service_run_next_main(Service *s) { return; fail: - log_warning_unit(UNIT(s)->id, "%s failed to run next main task: %s", UNIT(s)->id, strerror(-r)); + log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run next main task: %m", UNIT(s)->id); service_enter_stop(s, SERVICE_FAILURE_RESOURCES); } @@ -1627,7 +1621,7 @@ static int service_start_limit_test(Service *s) { if (ratelimit_test(&s->start_limit)) return 0; - log_warning_unit(UNIT(s)->id, "start request repeated too quickly for %s", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "start request repeated too quickly for %s", UNIT(s)->id); return failure_action(UNIT(s)->manager, s->start_limit_action, s->reboot_arg); } @@ -1835,7 +1829,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, state = service_state_from_string(value); if (state < 0) - log_debug_unit(u->id, "Failed to parse state value %s", value); + log_unit_debug(u->id, "Failed to parse state value %s", value); else s->deserialized_state = state; } else if (streq(key, "result")) { @@ -1843,7 +1837,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, f = service_result_from_string(value); if (f < 0) - log_debug_unit(u->id, "Failed to parse result value %s", value); + log_unit_debug(u->id, "Failed to parse result value %s", value); else if (f != SERVICE_SUCCESS) s->result = f; @@ -1852,7 +1846,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, f = service_result_from_string(value); if (f < 0) - log_debug_unit(u->id, "Failed to parse reload result value %s", value); + log_unit_debug(u->id, "Failed to parse reload result value %s", value); else if (f != SERVICE_SUCCESS) s->reload_result = f; @@ -1860,14 +1854,14 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, pid_t pid; if (parse_pid(value, &pid) < 0) - log_debug_unit(u->id, "Failed to parse control-pid value %s", value); + log_unit_debug(u->id, "Failed to parse control-pid value %s", value); else s->control_pid = pid; } else if (streq(key, "main-pid")) { pid_t pid; if (parse_pid(value, &pid) < 0) - log_debug_unit(u->id, "Failed to parse main-pid value %s", value); + log_unit_debug(u->id, "Failed to parse main-pid value %s", value); else { service_set_main_pid(s, pid); unit_watch_pid(UNIT(s), pid); @@ -1877,7 +1871,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, b = parse_boolean(value); if (b < 0) - log_debug_unit(u->id, "Failed to parse main-pid-known value %s", value); + log_unit_debug(u->id, "Failed to parse main-pid-known value %s", value); else s->main_pid_known = b; } else if (streq(key, "status-text")) { @@ -1896,7 +1890,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, id = service_exec_command_from_string(value); if (id < 0) - log_debug_unit(u->id, "Failed to parse exec-command value %s", value); + log_unit_debug(u->id, "Failed to parse exec-command value %s", value); else { s->control_command_id = id; s->control_command = s->exec_command[id]; @@ -1905,7 +1899,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, int fd; if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) - log_debug_unit(u->id, "Failed to parse socket-fd value %s", value); + log_unit_debug(u->id, "Failed to parse socket-fd value %s", value); else { asynchronous_close(s->socket_fd); s->socket_fd = fdset_remove(fds, fd); @@ -1914,7 +1908,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, int fd; if (safe_atoi(value, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) - log_debug_unit(u->id, "Failed to parse endpoint-fd value %s", value); + log_unit_debug(u->id, "Failed to parse endpoint-fd value %s", value); else { safe_close(s->bus_endpoint_fd); s->bus_endpoint_fd = fdset_remove(fds, fd); @@ -1923,21 +1917,21 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, pid_t pid; if (parse_pid(value, &pid) < 0) - log_debug_unit(u->id, "Failed to parse main-exec-status-pid value %s", value); + log_unit_debug(u->id, "Failed to parse main-exec-status-pid value %s", value); else s->main_exec_status.pid = pid; } else if (streq(key, "main-exec-status-code")) { int i; if (safe_atoi(value, &i) < 0) - log_debug_unit(u->id, "Failed to parse main-exec-status-code value %s", value); + log_unit_debug(u->id, "Failed to parse main-exec-status-code value %s", value); else s->main_exec_status.code = i; } else if (streq(key, "main-exec-status-status")) { int i; if (safe_atoi(value, &i) < 0) - log_debug_unit(u->id, "Failed to parse main-exec-status-status value %s", value); + log_unit_debug(u->id, "Failed to parse main-exec-status-status value %s", value); else s->main_exec_status.status = i; } else if (streq(key, "main-exec-status-start")) @@ -1951,11 +1945,11 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, b = parse_boolean(value); if (b < 0) - log_debug_unit(u->id, "Failed to parse forbid-restart value %s", value); + log_unit_debug(u->id, "Failed to parse forbid-restart value %s", value); else s->forbid_restart = b; } else - log_debug_unit(u->id, "Unknown serialization key '%s'", key); + log_unit_debug(u->id, "Unknown serialization key '%s'", key); return 0; } @@ -2018,19 +2012,19 @@ static int service_retry_pid_file(Service *s) { static int service_watch_pid_file(Service *s) { int r; - log_debug_unit(UNIT(s)->id, "Setting watch for %s's PID file %s", UNIT(s)->id, s->pid_file_pathspec->path); + log_unit_debug(UNIT(s)->id, "Setting watch for %s's PID file %s", UNIT(s)->id, s->pid_file_pathspec->path); r = path_spec_watch(s->pid_file_pathspec, service_dispatch_io); if (r < 0) goto fail; /* the pidfile might have appeared just before we set the watch */ - log_debug_unit(UNIT(s)->id, "Trying to read %s's PID file %s in case it changed", UNIT(s)->id, s->pid_file_pathspec->path); + log_unit_debug(UNIT(s)->id, "Trying to read %s's PID file %s in case it changed", UNIT(s)->id, s->pid_file_pathspec->path); service_retry_pid_file(s); return 0; fail: - log_error_unit(UNIT(s)->id, "Failed to set a watch for %s's PID file %s: %s", UNIT(s)->id, s->pid_file_pathspec->path, strerror(-r)); + log_unit_error_errno(UNIT(s)->id, r, "Failed to set a watch for %s's PID file %s: %m", UNIT(s)->id, s->pid_file_pathspec->path); service_unwatch_pid_file(s); return r; } @@ -2078,7 +2072,7 @@ static int service_dispatch_io(sd_event_source *source, int fd, uint32_t events, assert(s->pid_file_pathspec); assert(path_spec_owns_inotify_fd(s->pid_file_pathspec, fd)); - log_debug_unit(UNIT(s)->id, "inotify event for %s", UNIT(s)->id); + log_unit_debug(UNIT(s)->id, "inotify event for %s", UNIT(s)->id); if (path_spec_fd_event(p, events) < 0) goto fail; @@ -2102,7 +2096,7 @@ static void service_notify_cgroup_empty_event(Unit *u) { assert(u); - log_debug_unit(u->id, "%s: cgroup is empty", u->id); + log_unit_debug(u->id, "%s: cgroup is empty", u->id); switch (s->state) { @@ -2117,7 +2111,7 @@ static void service_notify_cgroup_empty_event(Unit *u) { /* If we were hoping for the daemon to write its PID file, * we can give up now. */ if (s->pid_file_pathspec) { - log_warning_unit(u->id, "%s never wrote its PID file. Failing.", UNIT(s)->id); + log_unit_warning(u->id, "%s never wrote its PID file. Failing.", UNIT(s)->id); service_unwatch_pid_file(s); if (s->state == SERVICE_START) @@ -2204,16 +2198,16 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { f = SERVICE_SUCCESS; } - log_struct_unit(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE, - u->id, - "MESSAGE=%s: main process exited, code=%s, status=%i/%s", - u->id, sigchld_code_to_string(code), status, - strna(code == CLD_EXITED - ? exit_status_to_string(status, EXIT_STATUS_FULL) - : signal_to_string(status)), - "EXIT_CODE=%s", sigchld_code_to_string(code), - "EXIT_STATUS=%i", status, - NULL); + log_unit_struct(u->id, + f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE, + LOG_MESSAGE("%s: main process exited, code=%s, status=%i/%s", + u->id, sigchld_code_to_string(code), status, + strna(code == CLD_EXITED + ? exit_status_to_string(status, EXIT_STATUS_FULL) + : signal_to_string(status))), + "EXIT_CODE=%s", sigchld_code_to_string(code), + "EXIT_STATUS=%i", status, + NULL); if (f != SERVICE_SUCCESS) s->result = f; @@ -2225,7 +2219,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { /* There is another command to * * execute, so let's do that. */ - log_debug_unit(u->id, "%s running next main command for state %s", u->id, service_state_to_string(s->state)); + log_unit_debug(u->id, "%s running next main command for state %s", u->id, service_state_to_string(s->state)); service_run_next_main(s); } else { @@ -2292,7 +2286,8 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { f = SERVICE_SUCCESS; } - log_full_unit(f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE, u->id, + log_unit_full(u->id, + f == SERVICE_SUCCESS ? LOG_DEBUG : LOG_NOTICE, "%s: control process exited, code=%s status=%i", u->id, sigchld_code_to_string(code), status); @@ -2311,7 +2306,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { /* There is another command to * * execute, so let's do that. */ - log_debug_unit(u->id, "%s running next control command for state %s", u->id, service_state_to_string(s->state)); + log_unit_debug(u->id, "%s running next control command for state %s", u->id, service_state_to_string(s->state)); service_run_next_control(s); } else { @@ -2321,7 +2316,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { s->control_command = NULL; s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID; - log_debug_unit(u->id, "%s got final SIGCHLD for state %s", u->id, service_state_to_string(s->state)); + log_unit_debug(u->id, "%s got final SIGCHLD for state %s", u->id, service_state_to_string(s->state)); switch (s->state) { @@ -2450,38 +2445,38 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us case SERVICE_START_PRE: case SERVICE_START: - log_warning_unit(UNIT(s)->id, "%s %s operation timed out. Terminating.", UNIT(s)->id, s->state == SERVICE_START ? "start" : "start-pre"); + log_unit_warning(UNIT(s)->id, "%s %s operation timed out. Terminating.", UNIT(s)->id, s->state == SERVICE_START ? "start" : "start-pre"); service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_TIMEOUT); break; case SERVICE_START_POST: - log_warning_unit(UNIT(s)->id, "%s start-post operation timed out. Stopping.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s start-post operation timed out. Stopping.", UNIT(s)->id); service_enter_stop(s, SERVICE_FAILURE_TIMEOUT); break; case SERVICE_RELOAD: - log_warning_unit(UNIT(s)->id, "%s reload operation timed out. Stopping.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s reload operation timed out. Stopping.", UNIT(s)->id); s->reload_result = SERVICE_FAILURE_TIMEOUT; service_enter_running(s, SERVICE_SUCCESS); break; case SERVICE_STOP: - log_warning_unit(UNIT(s)->id, "%s stopping timed out. Terminating.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s stopping timed out. Terminating.", UNIT(s)->id); service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT); break; case SERVICE_STOP_SIGABRT: - log_warning_unit(UNIT(s)->id, + log_unit_warning(UNIT(s)->id, "%s stop-sigabrt timed out. Terminating.", UNIT(s)->id); service_enter_signal(s, SERVICE_STOP_SIGTERM, s->result); break; case SERVICE_STOP_SIGTERM: if (s->kill_context.send_sigkill) { - log_warning_unit(UNIT(s)->id, "%s stop-sigterm timed out. Killing.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s stop-sigterm timed out. Killing.", UNIT(s)->id); service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_FAILURE_TIMEOUT); } else { - log_warning_unit(UNIT(s)->id, "%s stop-sigterm timed out. Skipping SIGKILL.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s stop-sigterm timed out. Skipping SIGKILL.", UNIT(s)->id); service_enter_stop_post(s, SERVICE_FAILURE_TIMEOUT); } @@ -2492,33 +2487,33 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us * Must be something we cannot kill, so let's just be * weirded out and continue */ - log_warning_unit(UNIT(s)->id, "%s still around after SIGKILL. Ignoring.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s still around after SIGKILL. Ignoring.", UNIT(s)->id); service_enter_stop_post(s, SERVICE_FAILURE_TIMEOUT); break; case SERVICE_STOP_POST: - log_warning_unit(UNIT(s)->id, "%s stop-post timed out. Terminating.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s stop-post timed out. Terminating.", UNIT(s)->id); service_enter_signal(s, SERVICE_FINAL_SIGTERM, SERVICE_FAILURE_TIMEOUT); break; case SERVICE_FINAL_SIGTERM: if (s->kill_context.send_sigkill) { - log_warning_unit(UNIT(s)->id, "%s stop-final-sigterm timed out. Killing.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s stop-final-sigterm timed out. Killing.", UNIT(s)->id); service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_FAILURE_TIMEOUT); } else { - log_warning_unit(UNIT(s)->id, "%s stop-final-sigterm timed out. Skipping SIGKILL. Entering failed mode.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s stop-final-sigterm timed out. Skipping SIGKILL. Entering failed mode.", UNIT(s)->id); service_enter_dead(s, SERVICE_FAILURE_TIMEOUT, false); } break; case SERVICE_FINAL_SIGKILL: - log_warning_unit(UNIT(s)->id, "%s still around after final SIGKILL. Entering failed mode.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s still around after final SIGKILL. Entering failed mode.", UNIT(s)->id); service_enter_dead(s, SERVICE_FAILURE_TIMEOUT, true); break; case SERVICE_AUTO_RESTART: - log_info_unit(UNIT(s)->id, + log_unit_info(UNIT(s)->id, s->restart_usec > 0 ? "%s holdoff time over, scheduling restart." : "%s has no holdoff time, scheduling restart.", @@ -2540,7 +2535,7 @@ static int service_dispatch_watchdog(sd_event_source *source, usec_t usec, void assert(s); assert(source == s->watchdog_event_source); - log_error_unit(UNIT(s)->id, "%s watchdog timeout (limit %s)!", UNIT(s)->id, + log_unit_error(UNIT(s)->id, "%s watchdog timeout (limit %s)!", UNIT(s)->id, format_timespan(t, sizeof(t), s->watchdog_usec, 1)); service_enter_signal(s, SERVICE_STOP_SIGABRT, SERVICE_FAILURE_WATCHDOG); @@ -2557,19 +2552,19 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) { assert(u); cc = strv_join(tags, ", "); - log_debug_unit(u->id, "%s: Got notification message from PID "PID_FMT" (%s)", + log_unit_debug(u->id, "%s: Got notification message from PID "PID_FMT" (%s)", u->id, pid, isempty(cc) ? "n/a" : cc); if (s->notify_access == NOTIFY_NONE) { - log_warning_unit(u->id, "%s: Got notification message from PID "PID_FMT", but reception is disabled.", u->id, pid); + log_unit_warning(u->id, "%s: Got notification message from PID "PID_FMT", but reception is disabled.", u->id, pid); return; } if (s->notify_access == NOTIFY_MAIN && pid != s->main_pid) { if (s->main_pid != 0) - log_warning_unit(u->id, "%s: Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT, u->id, pid, s->main_pid); + log_unit_warning(u->id, "%s: Got notification message from PID "PID_FMT", but reception only permitted for main PID "PID_FMT, u->id, pid, s->main_pid); else - log_debug_unit(u->id, "%s: Got notification message from PID "PID_FMT", but reception only permitted for main PID which is currently not known", u->id, pid); + log_unit_debug(u->id, "%s: Got notification message from PID "PID_FMT", but reception only permitted for main PID which is currently not known", u->id, pid); return; } @@ -2577,9 +2572,9 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) { e = strv_find_startswith(tags, "MAINPID="); if (e && IN_SET(s->state, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD)) { if (parse_pid(e, &pid) < 0) - log_warning_unit(u->id, "Failed to parse MAINPID= field in notification message: %s", e); + log_unit_warning(u->id, "Failed to parse MAINPID= field in notification message: %s", e); else { - log_debug_unit(u->id, "%s: got MAINPID=%s", u->id, e); + log_unit_debug(u->id, "%s: got MAINPID=%s", u->id, e); service_set_main_pid(s, pid); unit_watch_pid(UNIT(s), pid); @@ -2590,7 +2585,7 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) { /* Interpret RELOADING= */ if (strv_find(tags, "RELOADING=1")) { - log_debug_unit(u->id, "%s: got RELOADING=1", u->id); + log_unit_debug(u->id, "%s: got RELOADING=1", u->id); s->notify_state = NOTIFY_RELOADING; if (s->state == SERVICE_RUNNING) @@ -2602,7 +2597,7 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) { /* Interpret READY= */ if (strv_find(tags, "READY=1")) { - log_debug_unit(u->id, "%s: got READY=1", u->id); + log_unit_debug(u->id, "%s: got READY=1", u->id); s->notify_state = NOTIFY_READY; /* Type=notify services inform us about completed @@ -2621,7 +2616,7 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) { /* Interpret STOPPING= */ if (strv_find(tags, "STOPPING=1")) { - log_debug_unit(u->id, "%s: got STOPPING=1", u->id); + log_unit_debug(u->id, "%s: got STOPPING=1", u->id); s->notify_state = NOTIFY_STOPPING; if (s->state == SERVICE_RUNNING) @@ -2637,9 +2632,9 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) { if (!isempty(e)) { if (!utf8_is_valid(e)) - log_warning_unit(u->id, "Status message in notification is not UTF-8 clean."); + log_unit_warning(u->id, "Status message in notification is not UTF-8 clean."); else { - log_debug_unit(u->id, "%s: got STATUS=%s", u->id, e); + log_unit_debug(u->id, "%s: got STATUS=%s", u->id, e); t = strdup(e); if (!t) @@ -2663,9 +2658,9 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) { int status_errno; if (safe_atoi(e, &status_errno) < 0 || status_errno < 0) - log_warning_unit(u->id, "Failed to parse ERRNO= field in notification message: %s", e); + log_unit_warning(u->id, "Failed to parse ERRNO= field in notification message: %s", e); else { - log_debug_unit(u->id, "%s: got ERRNO=%s", u->id, e); + log_unit_debug(u->id, "%s: got ERRNO=%s", u->id, e); if (s->status_errno != status_errno) { s->status_errno = status_errno; @@ -2676,7 +2671,7 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) { /* Interpret WATCHDOG= */ if (strv_find(tags, "WATCHDOG=1")) { - log_debug_unit(u->id, "%s: got WATCHDOG=1", u->id); + log_unit_debug(u->id, "%s: got WATCHDOG=1", u->id); service_reset_watchdog(s); } @@ -2715,11 +2710,11 @@ static void service_bus_name_owner_change( assert(old_owner || new_owner); if (old_owner && new_owner) - log_debug_unit(u->id, "%s's D-Bus name %s changed owner from %s to %s", u->id, name, old_owner, new_owner); + log_unit_debug(u->id, "%s's D-Bus name %s changed owner from %s to %s", u->id, name, old_owner, new_owner); else if (old_owner) - log_debug_unit(u->id, "%s's D-Bus name %s no longer registered by %s", u->id, name, old_owner); + log_unit_debug(u->id, "%s's D-Bus name %s no longer registered by %s", u->id, name, old_owner); else - log_debug_unit(u->id, "%s's D-Bus name %s now registered by %s", u->id, name, new_owner); + log_unit_debug(u->id, "%s's D-Bus name %s now registered by %s", u->id, name, new_owner); s->bus_name_good = !!new_owner; @@ -2748,7 +2743,7 @@ static void service_bus_name_owner_change( if (r >= 0) r = sd_bus_creds_get_pid(creds, &pid); if (r >= 0) { - log_debug_unit(u->id, "%s's D-Bus name %s is now owned by process %u", u->id, name, (unsigned) pid); + log_unit_debug(u->id, "%s's D-Bus name %s is now owned by process %u", u->id, name, (unsigned) pid); service_set_main_pid(s, pid); unit_watch_pid(UNIT(s), pid); diff --git a/src/core/service.h b/src/core/service.h index 54fbe46fa4..f6a78c403b 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -180,9 +180,6 @@ struct Service { bool bus_name_good:1; bool forbid_restart:1; bool start_timeout_defined:1; -#ifdef HAVE_SYSV_COMPAT - int sysv_start_priority; -#endif char *bus_name; diff --git a/src/core/shutdown.c b/src/core/shutdown.c index 20cf526ba2..def20f5022 100644 --- a/src/core/shutdown.c +++ b/src/core/shutdown.c @@ -75,7 +75,9 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 1); assert(argv); - while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) + /* "-" prevents getopt from permuting argv[] and moving the verb away + * from argv[1]. Our interface to initrd promises it'll be there. */ + while ((c = getopt_long(argc, argv, "-", options, NULL)) >= 0) switch (c) { case ARG_LOG_LEVEL: @@ -113,6 +115,13 @@ static int parse_argv(int argc, char *argv[]) { break; + case '\001': + if (!arg_verb) + arg_verb = optarg; + else + log_error("Excess arguments, ignoring"); + break; + case '?': return -EINVAL; @@ -120,28 +129,20 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached("Unhandled option code."); } - if (optind >= argc) { + if (!arg_verb) { log_error("Verb argument missing."); return -EINVAL; } - arg_verb = argv[optind]; - - if (optind + 1 < argc) - log_error("Excess arguments, ignoring"); return 0; } static int switch_root_initramfs(void) { - if (mount("/run/initramfs", "/run/initramfs", NULL, MS_BIND, NULL) < 0) { - log_error("Failed to mount bind /run/initramfs on /run/initramfs: %m"); - return -errno; - } + if (mount("/run/initramfs", "/run/initramfs", NULL, MS_BIND, NULL) < 0) + return log_error_errno(errno, "Failed to mount bind /run/initramfs on /run/initramfs: %m"); - if (mount(NULL, "/run/initramfs", NULL, MS_PRIVATE, NULL) < 0) { - log_error("Failed to make /run/initramfs private mount: %m"); - return -errno; - } + if (mount(NULL, "/run/initramfs", NULL, MS_PRIVATE, NULL) < 0) + return log_error_errno(errno, "Failed to make /run/initramfs private mount: %m"); /* switch_root with MS_BIND, because there might still be processes lurking around, which have open file desriptors. * /run/initramfs/shutdown will take care of these. @@ -165,7 +166,7 @@ int main(int argc, char *argv[]) { goto error; /* journald will die if not gone yet. The log target defaults - * to console, but may have been changed by commandline options. */ + * to console, but may have been changed by command line options. */ log_close_console(); /* force reopen of /dev/console */ log_open(); @@ -235,7 +236,7 @@ int main(int argc, char *argv[]) { } else if (r > 0) log_info("Not all file systems unmounted, %d left.", r); else - log_error("Failed to unmount file systems: %s", strerror(-r)); + log_error_errno(r, "Failed to unmount file systems: %m"); } if (need_swapoff) { @@ -247,7 +248,7 @@ int main(int argc, char *argv[]) { } else if (r > 0) log_info("Not all swaps deactivated, %d left.", r); else - log_error("Failed to deactivate swaps: %s", strerror(-r)); + log_error_errno(r, "Failed to deactivate swaps: %m"); } if (need_loop_detach) { @@ -259,7 +260,7 @@ int main(int argc, char *argv[]) { } else if (r > 0) log_info("Not all loop devices detached, %d left.", r); else - log_error("Failed to detach loop devices: %s", strerror(-r)); + log_error_errno(r, "Failed to detach loop devices: %m"); } if (need_dm_detach) { @@ -271,7 +272,7 @@ int main(int argc, char *argv[]) { } else if (r > 0) log_info("Not all DM devices detached, %d left.", r); else - log_error("Failed to detach DM devices: %s", strerror(-r)); + log_error_errno(r, "Failed to detach DM devices: %m"); } if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) { @@ -322,9 +323,9 @@ int main(int argc, char *argv[]) { "Returning to initrd..."); execv("/shutdown", argv); - log_error("Failed to execute shutdown binary: %m"); + log_error_errno(errno, "Failed to execute shutdown binary: %m"); } else - log_error("Failed to switch root to \"/run/initramfs\": %s", strerror(-r)); + log_error_errno(r, "Failed to switch root to \"/run/initramfs\": %m"); } @@ -354,7 +355,7 @@ int main(int argc, char *argv[]) { pid = fork(); if (pid < 0) - log_error("Failed to fork: %m"); + log_error_errno(errno, "Failed to fork: %m"); else if (pid == 0) { const char * const args[] = { @@ -366,7 +367,7 @@ int main(int argc, char *argv[]) { execv(args[0], (char * const *) args); _exit(EXIT_FAILURE); } else - wait_for_terminate_and_warn("kexec", pid); + wait_for_terminate_and_warn("kexec", pid, true); } cmd = RB_AUTOBOOT; @@ -407,11 +408,11 @@ int main(int argc, char *argv[]) { exit(0); } - log_error("Failed to invoke reboot(): %m"); + log_error_errno(errno, "Failed to invoke reboot(): %m"); r = -errno; error: - log_error("Critical error while doing system shutdown: %s", strerror(-r)); + log_emergency_errno(r, "Critical error while doing system shutdown: %m"); freeze(); } diff --git a/src/core/slice.c b/src/core/slice.c index 057feefa0f..a31e629370 100644 --- a/src/core/slice.c +++ b/src/core/slice.c @@ -113,7 +113,7 @@ static int slice_verify(Slice *s) { a = (char*) SPECIAL_ROOT_SLICE; if (!unit_has_name(UNIT_DEREF(UNIT(s)->slice), a)) { - log_error_unit(UNIT(s)->id, + log_unit_error(UNIT(s)->id, "%s located outside its parent slice. Refusing.", UNIT(s)->id); return -EINVAL; } diff --git a/src/core/smack-setup.c b/src/core/smack-setup.c index d0fd1809f9..59f6832bc2 100644 --- a/src/core/smack-setup.c +++ b/src/core/smack-setup.c @@ -56,7 +56,7 @@ static int write_rules(const char* dstpath, const char* srcdir) { dst = fopen(dstpath, "we"); if (!dst) { if (errno != ENOENT) - log_warning("Failed to open %s: %m", dstpath); + log_warning_errno(errno, "Failed to open %s: %m", dstpath); return -errno; /* negative error */ } @@ -64,7 +64,7 @@ static int write_rules(const char* dstpath, const char* srcdir) { dir = opendir(srcdir); if (!dir) { if (errno != ENOENT) - log_warning("Failed to opendir %s: %m", srcdir); + log_warning_errno(errno, "Failed to opendir %s: %m", srcdir); return errno; /* positive on purpose */ } @@ -79,7 +79,7 @@ static int write_rules(const char* dstpath, const char* srcdir) { if (fd < 0) { if (r == 0) r = -errno; - log_warning("Failed to open %s: %m", entry->d_name); + log_warning_errno(errno, "Failed to open %s: %m", entry->d_name); continue; } @@ -88,13 +88,13 @@ static int write_rules(const char* dstpath, const char* srcdir) { if (r == 0) r = -errno; safe_close(fd); - log_error("Failed to open %s: %m", entry->d_name); + log_error_errno(errno, "Failed to open %s: %m", entry->d_name); continue; } /* load2 write rules in the kernel require a line buffered stream */ FOREACH_LINE(buf, policy, - log_error("Failed to read line from %s: %m", + log_error_errno(errno, "Failed to read line from %s: %m", entry->d_name)) { if (!fputs(buf, dst)) { if (r == 0) @@ -105,7 +105,7 @@ static int write_rules(const char* dstpath, const char* srcdir) { if (fflush(dst)) { if (r == 0) r = -errno; - log_error("Failed to flush writes to %s: %m", dstpath); + log_error_errno(errno, "Failed to flush writes to %s: %m", dstpath); break; } } diff --git a/src/core/snapshot.c b/src/core/snapshot.c index 5eed615a15..068a077f15 100644 --- a/src/core/snapshot.c +++ b/src/core/snapshot.c @@ -25,7 +25,7 @@ #include "snapshot.h" #include "unit-name.h" #include "dbus-snapshot.h" -#include "bus-errors.h" +#include "bus-common-errors.h" static const UnitActiveState state_translation_table[_SNAPSHOT_STATE_MAX] = { [SNAPSHOT_DEAD] = UNIT_INACTIVE, @@ -51,7 +51,7 @@ static void snapshot_set_state(Snapshot *s, SnapshotState state) { s->state = state; if (state != old_state) - log_debug_unit(UNIT(s)->id, + log_unit_debug(UNIT(s)->id, "%s changed %s -> %s", UNIT(s)->id, snapshot_state_to_string(old_state), @@ -155,7 +155,7 @@ static int snapshot_deserialize_item(Unit *u, const char *key, const char *value state = snapshot_state_from_string(value); if (state < 0) - log_debug_unit(u->id, "Failed to parse state value %s", value); + log_unit_debug(u->id, "Failed to parse state value %s", value); else s->deserialized_state = state; @@ -163,7 +163,7 @@ static int snapshot_deserialize_item(Unit *u, const char *key, const char *value r = parse_boolean(value); if (r < 0) - log_debug_unit(u->id, "Failed to parse cleanup value %s", value); + log_unit_debug(u->id, "Failed to parse cleanup value %s", value); else s->cleanup = r; @@ -173,7 +173,7 @@ static int snapshot_deserialize_item(Unit *u, const char *key, const char *value if (r < 0) return r; } else - log_debug_unit(u->id, "Unknown serialization key '%s'", key); + log_unit_debug(u->id, "Unknown serialization key '%s'", key); return 0; } @@ -208,7 +208,7 @@ int snapshot_create(Manager *m, const char *name, bool cleanup, sd_bus_error *e, return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s lacks snapshot suffix.", name); if (manager_get_unit(m, name)) - sd_bus_error_setf(e, BUS_ERROR_UNIT_EXISTS, "Snapshot %s exists already.", name); + return sd_bus_error_setf(e, BUS_ERROR_UNIT_EXISTS, "Snapshot %s exists already.", name); } else { @@ -258,7 +258,7 @@ int snapshot_create(Manager *m, const char *name, bool cleanup, sd_bus_error *e, SNAPSHOT(u)->cleanup = cleanup; *_s = SNAPSHOT(u); - log_info_unit(u->id, "Created snapshot %s.", u->id); + log_unit_info(u->id, "Created snapshot %s.", u->id); return 0; @@ -272,7 +272,7 @@ fail: void snapshot_remove(Snapshot *s) { assert(s); - log_info_unit(UNIT(s)->id, "Removing snapshot %s.", UNIT(s)->id); + log_unit_info(UNIT(s)->id, "Removing snapshot %s.", UNIT(s)->id); unit_add_to_cleanup_queue(UNIT(s)); } diff --git a/src/core/socket.c b/src/core/socket.c index 6ba8338d8b..8fa55e0b03 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -391,33 +391,33 @@ static int socket_verify(Socket *s) { return 0; if (!s->ports) { - log_error_unit(UNIT(s)->id, "%s lacks Listen setting. Refusing.", UNIT(s)->id); + log_unit_error(UNIT(s)->id, "%s lacks Listen setting. Refusing.", UNIT(s)->id); return -EINVAL; } if (s->accept && have_non_accept_socket(s)) { - log_error_unit(UNIT(s)->id, "%s configured for accepting sockets, but sockets are non-accepting. Refusing.", + log_unit_error(UNIT(s)->id, "%s configured for accepting sockets, but sockets are non-accepting. Refusing.", UNIT(s)->id); return -EINVAL; } if (s->accept && s->max_connections <= 0) { - log_error_unit(UNIT(s)->id, "%s's MaxConnection setting too small. Refusing.", UNIT(s)->id); + log_unit_error(UNIT(s)->id, "%s's MaxConnection setting too small. Refusing.", UNIT(s)->id); return -EINVAL; } if (s->accept && UNIT_DEREF(s->service)) { - log_error_unit(UNIT(s)->id, "Explicit service configuration for accepting sockets not supported on %s. Refusing.", UNIT(s)->id); + log_unit_error(UNIT(s)->id, "Explicit service configuration for accepting sockets not supported on %s. Refusing.", UNIT(s)->id); return -EINVAL; } if (s->exec_context.pam_name && s->kill_context.kill_mode != KILL_CONTROL_GROUP) { - log_error_unit(UNIT(s)->id, "%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(s)->id); + log_unit_error(UNIT(s)->id, "%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(s)->id); return -EINVAL; } if (!strv_isempty(s->symlinks) && !socket_find_symlink_target(s)) { - log_error_unit(UNIT(s)->id, "%s has symlinks set but none or more than one node in the file system. Refusing.", UNIT(s)->id); + log_unit_error(UNIT(s)->id, "%s has symlinks set but none or more than one node in the file system. Refusing.", UNIT(s)->id); return -EINVAL; } @@ -815,60 +815,60 @@ static void socket_apply_socket_options(Socket *s, int fd) { if (s->keep_alive) { int b = s->keep_alive; if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &b, sizeof(b)) < 0) - log_warning_unit(UNIT(s)->id, "SO_KEEPALIVE failed: %m"); + log_unit_warning(UNIT(s)->id, "SO_KEEPALIVE failed: %m"); } if (s->keep_alive_time) { int value = s->keep_alive_time / USEC_PER_SEC; if (setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, &value, sizeof(value)) < 0) - log_warning_unit(UNIT(s)->id, "TCP_KEEPIDLE failed: %m"); + log_unit_warning(UNIT(s)->id, "TCP_KEEPIDLE failed: %m"); } if (s->keep_alive_interval) { int value = s->keep_alive_interval / USEC_PER_SEC; if (setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &value, sizeof(value)) < 0) - log_warning_unit(UNIT(s)->id, "TCP_KEEPINTVL failed: %m"); + log_unit_warning(UNIT(s)->id, "TCP_KEEPINTVL failed: %m"); } if (s->keep_alive_cnt) { int value = s->keep_alive_cnt; if (setsockopt(fd, SOL_SOCKET, TCP_KEEPCNT, &value, sizeof(value)) < 0) - log_warning_unit(UNIT(s)->id, "TCP_KEEPCNT failed: %m"); + log_unit_warning(UNIT(s)->id, "TCP_KEEPCNT failed: %m"); } if (s->defer_accept) { int value = s->defer_accept / USEC_PER_SEC; if (setsockopt(fd, SOL_TCP, TCP_DEFER_ACCEPT, &value, sizeof(value)) < 0) - log_warning_unit(UNIT(s)->id, "TCP_DEFER_ACCEPT failed: %m"); + log_unit_warning(UNIT(s)->id, "TCP_DEFER_ACCEPT failed: %m"); } if (s->no_delay) { int b = s->no_delay; if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &b, sizeof(b)) < 0) - log_warning_unit(UNIT(s)->id, "TCP_NODELAY failed: %m"); + log_unit_warning(UNIT(s)->id, "TCP_NODELAY failed: %m"); } if (s->broadcast) { int one = 1; if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof(one)) < 0) - log_warning_unit(UNIT(s)->id, "SO_BROADCAST failed: %m"); + log_unit_warning(UNIT(s)->id, "SO_BROADCAST failed: %m"); } if (s->pass_cred) { int one = 1; if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) - log_warning_unit(UNIT(s)->id, "SO_PASSCRED failed: %m"); + log_unit_warning(UNIT(s)->id, "SO_PASSCRED failed: %m"); } if (s->pass_sec) { int one = 1; if (setsockopt(fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one)) < 0) - log_warning_unit(UNIT(s)->id, "SO_PASSSEC failed: %m"); + log_unit_warning(UNIT(s)->id, "SO_PASSSEC failed: %m"); } if (s->priority >= 0) if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, &s->priority, sizeof(s->priority)) < 0) - log_warning_unit(UNIT(s)->id, "SO_PRIORITY failed: %m"); + log_unit_warning(UNIT(s)->id, "SO_PRIORITY failed: %m"); if (s->receive_buffer > 0) { int value = (int) s->receive_buffer; @@ -877,23 +877,23 @@ static void socket_apply_socket_options(Socket *s, int fd) { if (setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &value, sizeof(value)) < 0) if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)) < 0) - log_warning_unit(UNIT(s)->id, "SO_RCVBUF failed: %m"); + log_unit_warning(UNIT(s)->id, "SO_RCVBUF failed: %m"); } if (s->send_buffer > 0) { int value = (int) s->send_buffer; if (setsockopt(fd, SOL_SOCKET, SO_SNDBUFFORCE, &value, sizeof(value)) < 0) if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)) < 0) - log_warning_unit(UNIT(s)->id, "SO_SNDBUF failed: %m"); + log_unit_warning(UNIT(s)->id, "SO_SNDBUF failed: %m"); } if (s->mark >= 0) if (setsockopt(fd, SOL_SOCKET, SO_MARK, &s->mark, sizeof(s->mark)) < 0) - log_warning_unit(UNIT(s)->id, "SO_MARK failed: %m"); + log_unit_warning(UNIT(s)->id, "SO_MARK failed: %m"); if (s->ip_tos >= 0) if (setsockopt(fd, IPPROTO_IP, IP_TOS, &s->ip_tos, sizeof(s->ip_tos)) < 0) - log_warning_unit(UNIT(s)->id, "IP_TOS failed: %m"); + log_unit_warning(UNIT(s)->id, "IP_TOS failed: %m"); if (s->ip_ttl >= 0) { int x; @@ -908,30 +908,30 @@ static void socket_apply_socket_options(Socket *s, int fd) { } if (r < 0 && x < 0) - log_warning_unit(UNIT(s)->id, + log_unit_warning(UNIT(s)->id, "IP_TTL/IPV6_UNICAST_HOPS failed: %m"); } if (s->tcp_congestion) if (setsockopt(fd, SOL_TCP, TCP_CONGESTION, s->tcp_congestion, strlen(s->tcp_congestion)+1) < 0) - log_warning_unit(UNIT(s)->id, "TCP_CONGESTION failed: %m"); + log_unit_warning(UNIT(s)->id, "TCP_CONGESTION failed: %m"); if (s->reuse_port) { int b = s->reuse_port; if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &b, sizeof(b)) < 0) - log_warning_unit(UNIT(s)->id, "SO_REUSEPORT failed: %m"); + log_unit_warning(UNIT(s)->id, "SO_REUSEPORT failed: %m"); } if (s->smack_ip_in) { r = mac_smack_apply_ip_in_fd(fd, s->smack_ip_in); if (r < 0) - log_error_unit(UNIT(s)->id, "mac_smack_apply_ip_in_fd: %s", strerror(-r)); + log_unit_error_errno(UNIT(s)->id, r, "mac_smack_apply_ip_in_fd: %m"); } if (s->smack_ip_out) { r = mac_smack_apply_ip_out_fd(fd, s->smack_ip_out); if (r < 0) - log_error_unit(UNIT(s)->id, "mac_smack_apply_ip_out_fd: %s", strerror(-r)); + log_unit_error_errno(UNIT(s)->id, r, "mac_smack_apply_ip_out_fd: %m"); } } @@ -943,12 +943,12 @@ static void socket_apply_fifo_options(Socket *s, int fd) { if (s->pipe_size > 0) if (fcntl(fd, F_SETPIPE_SZ, s->pipe_size) < 0) - log_warning_unit(UNIT(s)->id, "F_SETPIPE_SZ: %m"); + log_unit_warning(UNIT(s)->id, "F_SETPIPE_SZ: %m"); if (s->smack) { r = mac_smack_apply_fd(fd, s->smack); if (r < 0) - log_error_unit(UNIT(s)->id, "mac_smack_apply_fd: %s", strerror(-r)); + log_unit_error_errno(UNIT(s)->id, r, "mac_smack_apply_fd: %m"); } } @@ -1249,7 +1249,7 @@ static void socket_unwatch_fds(Socket *s) { r = sd_event_source_set_enabled(p->event_source, SD_EVENT_OFF); if (r < 0) - log_debug_unit(UNIT(s)->id, "Failed to disable event source."); + log_unit_debug(UNIT(s)->id, "Failed to disable event source."); } } @@ -1269,7 +1269,7 @@ static int socket_watch_fds(Socket *s) { r = sd_event_add_io(UNIT(s)->manager->event, &p->event_source, p->fd, EPOLLIN, socket_dispatch_io, p); if (r < 0) { - log_warning_unit(UNIT(s)->id, "Failed to watch listening fds: %s", strerror(-r)); + log_unit_warning_errno(UNIT(s)->id, r, "Failed to watch listening fds: %m"); goto fail; } } @@ -1319,7 +1319,7 @@ static void socket_set_state(Socket *s, SocketState state) { socket_close_fds(s); if (state != old_state) - log_debug_unit(UNIT(s)->id, "%s changed %s -> %s", + log_unit_debug(UNIT(s)->id, "%s changed %s -> %s", UNIT(s)->id, socket_state_to_string(old_state), socket_state_to_string(state)); unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true); @@ -1414,6 +1414,7 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn; exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported; exec_params.cgroup_path = UNIT(s)->cgroup_path; + exec_params.cgroup_delegate = s->cgroup_context.delegate; exec_params.runtime_prefix = manager_get_runtime_prefix(UNIT(s)->manager); exec_params.unit_id = UNIT(s)->id; @@ -1455,8 +1456,8 @@ static int socket_chown(Socket *s, pid_t *_pid) { if (pid == 0) { SocketPort *p; - uid_t uid = (uid_t) -1; - gid_t gid = (gid_t) -1; + uid_t uid = UID_INVALID; + gid_t gid = GID_INVALID; int ret; default_signals(SIGNALS_CRASH_HANDLER, SIGNALS_IGNORE, -1); @@ -1505,7 +1506,7 @@ static int socket_chown(Socket *s, pid_t *_pid) { fail_child: log_open(); - log_error("Failed to chown socket at step %s: %s", exit_status_to_string(ret, EXIT_STATUS_SYSTEMD), strerror(-r)); + log_error_errno(r, "Failed to chown socket at step %s: %m", exit_status_to_string(ret, EXIT_STATUS_SYSTEMD)); _exit(ret); } @@ -1561,7 +1562,7 @@ static void socket_enter_stop_post(Socket *s, SocketResult f) { return; fail: - log_warning_unit(UNIT(s)->id, + log_unit_warning(UNIT(s)->id, "%s failed to run 'stop-post' task: %s", UNIT(s)->id, strerror(-r)); socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_RESOURCES); @@ -1604,7 +1605,7 @@ static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) { return; fail: - log_warning_unit(UNIT(s)->id, "%s failed to kill processes: %s", UNIT(s)->id, strerror(-r)); + log_unit_warning_errno(UNIT(s)->id, r, "%s failed to kill processes: %m", UNIT(s)->id); if (state == SOCKET_STOP_PRE_SIGTERM || state == SOCKET_STOP_PRE_SIGKILL) socket_enter_stop_post(s, SOCKET_FAILURE_RESOURCES); @@ -1635,7 +1636,7 @@ static void socket_enter_stop_pre(Socket *s, SocketResult f) { return; fail: - log_warning_unit(UNIT(s)->id, "%s failed to run 'stop-pre' task: %s", UNIT(s)->id, strerror(-r)); + log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run 'stop-pre' task: %m", UNIT(s)->id); socket_enter_stop_post(s, SOCKET_FAILURE_RESOURCES); } @@ -1645,7 +1646,7 @@ static void socket_enter_listening(Socket *s) { r = socket_watch_fds(s); if (r < 0) { - log_warning_unit(UNIT(s)->id, "%s failed to watch sockets: %s", UNIT(s)->id, strerror(-r)); + log_unit_warning_errno(UNIT(s)->id, r, "%s failed to watch sockets: %m", UNIT(s)->id); goto fail; } @@ -1667,7 +1668,7 @@ static void socket_enter_start_post(Socket *s) { if (s->control_command) { r = socket_spawn(s, s->control_command, &s->control_pid); if (r < 0) { - log_warning_unit(UNIT(s)->id, "%s failed to run 'start-post' task: %s", UNIT(s)->id, strerror(-r)); + log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run 'start-post' task: %m", UNIT(s)->id); goto fail; } @@ -1688,7 +1689,7 @@ static void socket_enter_start_chown(Socket *s) { r = socket_open_fds(s); if (r < 0) { - log_warning_unit(UNIT(s)->id, "%s failed to listen on sockets: %s", UNIT(s)->id, strerror(-r)); + log_unit_warning_errno(UNIT(s)->id, r, "%s failed to listen on sockets: %m", UNIT(s)->id); goto fail; } @@ -1700,7 +1701,7 @@ static void socket_enter_start_chown(Socket *s) { r = socket_chown(s, &s->control_pid); if (r < 0) { - log_warning_unit(UNIT(s)->id, "%s failed to fork 'start-chown' task: %s", UNIT(s)->id, strerror(-r)); + log_unit_warning_errno(UNIT(s)->id, r, "%s failed to fork 'start-chown' task: %m", UNIT(s)->id); goto fail; } @@ -1725,7 +1726,7 @@ static void socket_enter_start_pre(Socket *s) { if (s->control_command) { r = socket_spawn(s, s->control_command, &s->control_pid); if (r < 0) { - log_warning_unit(UNIT(s)->id, "%s failed to run 'start-pre' task: %s", UNIT(s)->id, strerror(-r)); + log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run 'start-pre' task: %m", UNIT(s)->id); goto fail; } @@ -1749,7 +1750,7 @@ static void socket_enter_running(Socket *s, int cfd) { * shut down anyway */ if (unit_stop_pending(UNIT(s))) { - log_debug_unit(UNIT(s)->id, "Suppressing connection request on %s since unit stop is scheduled.", UNIT(s)->id); + log_unit_debug(UNIT(s)->id, "Suppressing connection request on %s since unit stop is scheduled.", UNIT(s)->id); if (cfd >= 0) safe_close(cfd); @@ -1759,14 +1760,14 @@ static void socket_enter_running(Socket *s, int cfd) { r = socket_open_fds(s); if (r < 0) { - log_warning_unit(UNIT(s)->id, "%s failed to listen on sockets: %s", UNIT(s)->id, strerror(-r)); + log_unit_warning_errno(UNIT(s)->id, r, "%s failed to listen on sockets: %m", UNIT(s)->id); socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); return; } r = socket_watch_fds(s); if (r < 0) { - log_warning_unit(UNIT(s)->id, "%s failed to watch sockets: %s", UNIT(s)->id, strerror(-r)); + log_unit_warning_errno(UNIT(s)->id, r, "%s failed to watch sockets: %m", UNIT(s)->id); socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); } } @@ -1789,7 +1790,7 @@ static void socket_enter_running(Socket *s, int cfd) { if (!pending) { if (!UNIT_ISSET(s->service)) { - log_error_unit(UNIT(s)->id, "%s: service to activate vanished, refusing activation.", UNIT(s)->id); + log_unit_error(UNIT(s)->id, "%s: service to activate vanished, refusing activation.", UNIT(s)->id); r = -ENOENT; goto fail; } @@ -1805,7 +1806,7 @@ static void socket_enter_running(Socket *s, int cfd) { Service *service; if (s->n_connections >= s->max_connections) { - log_warning_unit(UNIT(s)->id, "%s: Too many incoming connections (%u)", UNIT(s)->id, s->n_connections); + log_unit_warning(UNIT(s)->id, "%s: Too many incoming connections (%u)", UNIT(s)->id, s->n_connections); safe_close(cfd); return; } @@ -1867,7 +1868,7 @@ static void socket_enter_running(Socket *s, int cfd) { return; fail: - log_warning_unit(UNIT(s)->id, "%s failed to queue service startup job (Maybe the service file is missing or not a %s unit?): %s", + log_unit_warning(UNIT(s)->id, "%s failed to queue service startup job (Maybe the service file is missing or not a %s unit?): %s", UNIT(s)->id, cfd >= 0 ? "template" : "non-template", bus_error_message(&error, r)); @@ -1893,7 +1894,7 @@ static void socket_run_next(Socket *s) { return; fail: - log_warning_unit(UNIT(s)->id, "%s failed to run next task: %s", UNIT(s)->id, strerror(-r)); + log_unit_warning_errno(UNIT(s)->id, r, "%s failed to run next task: %m", UNIT(s)->id); if (s->state == SOCKET_START_POST) socket_enter_stop_pre(s, SOCKET_FAILURE_RESOURCES); @@ -1933,7 +1934,7 @@ static int socket_start(Unit *u) { service = SERVICE(UNIT_DEREF(s->service)); if (UNIT(service)->load_state != UNIT_LOADED) { - log_error_unit(u->id, "Socket service %s not loaded, refusing.", UNIT(service)->id); + log_unit_error(u->id, "Socket service %s not loaded, refusing.", UNIT(service)->id); return -ENOENT; } @@ -1942,7 +1943,7 @@ static int socket_start(Unit *u) { if (service->state != SERVICE_DEAD && service->state != SERVICE_FAILED && service->state != SERVICE_AUTO_RESTART) { - log_error_unit(u->id, "Socket service %s already active, refusing.", UNIT(service)->id); + log_unit_error(u->id, "Socket service %s already active, refusing.", UNIT(service)->id); return -EBUSY; } } @@ -2052,7 +2053,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, state = socket_state_from_string(value); if (state < 0) - log_debug_unit(u->id, "Failed to parse state value %s", value); + log_unit_debug(u->id, "Failed to parse state value %s", value); else s->deserialized_state = state; } else if (streq(key, "result")) { @@ -2060,7 +2061,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, f = socket_result_from_string(value); if (f < 0) - log_debug_unit(u->id, "Failed to parse result value %s", value); + log_unit_debug(u->id, "Failed to parse result value %s", value); else if (f != SOCKET_SUCCESS) s->result = f; @@ -2068,14 +2069,14 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, unsigned k; if (safe_atou(value, &k) < 0) - log_debug_unit(u->id, "Failed to parse n-accepted value %s", value); + log_unit_debug(u->id, "Failed to parse n-accepted value %s", value); else s->n_accepted += k; } else if (streq(key, "control-pid")) { pid_t pid; if (parse_pid(value, &pid) < 0) - log_debug_unit(u->id, "Failed to parse control-pid value %s", value); + log_unit_debug(u->id, "Failed to parse control-pid value %s", value); else s->control_pid = pid; } else if (streq(key, "control-command")) { @@ -2083,7 +2084,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, id = socket_exec_command_from_string(value); if (id < 0) - log_debug_unit(u->id, "Failed to parse exec-command value %s", value); + log_unit_debug(u->id, "Failed to parse exec-command value %s", value); else { s->control_command_id = id; s->control_command = s->exec_command[id]; @@ -2093,7 +2094,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, SocketPort *p; if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) - log_debug_unit(u->id, "Failed to parse fifo value %s", value); + log_unit_debug(u->id, "Failed to parse fifo value %s", value); else { LIST_FOREACH(port, p, s->ports) @@ -2112,7 +2113,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, SocketPort *p; if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) - log_debug_unit(u->id, "Failed to parse special value %s", value); + log_unit_debug(u->id, "Failed to parse special value %s", value); else { LIST_FOREACH(port, p, s->ports) @@ -2131,7 +2132,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, SocketPort *p; if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) - log_debug_unit(u->id, "Failed to parse mqueue value %s", value); + log_unit_debug(u->id, "Failed to parse mqueue value %s", value); else { LIST_FOREACH(port, p, s->ports) @@ -2150,7 +2151,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, SocketPort *p; if (sscanf(value, "%i %i %n", &fd, &type, &skip) < 2 || fd < 0 || type < 0 || !fdset_contains(fds, fd)) - log_debug_unit(u->id, "Failed to parse socket value %s", value); + log_unit_debug(u->id, "Failed to parse socket value %s", value); else { LIST_FOREACH(port, p, s->ports) @@ -2168,7 +2169,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, SocketPort *p; if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) - log_debug_unit(u->id, "Failed to parse socket value %s", value); + log_unit_debug(u->id, "Failed to parse socket value %s", value); else { LIST_FOREACH(port, p, s->ports) @@ -2181,7 +2182,7 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, } } } else - log_debug_unit(UNIT(s)->id, "Unknown serialization key '%s'", key); + log_unit_debug(UNIT(s)->id, "Unknown serialization key '%s'", key); return 0; } @@ -2285,15 +2286,15 @@ static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, if (p->socket->state != SOCKET_LISTENING) return 0; - log_debug_unit(UNIT(p->socket)->id, "Incoming traffic on %s", UNIT(p->socket)->id); + log_unit_debug(UNIT(p->socket)->id, "Incoming traffic on %s", UNIT(p->socket)->id); if (revents != EPOLLIN) { if (revents & EPOLLHUP) - log_error_unit(UNIT(p->socket)->id, "%s: Got POLLHUP on a listening socket. The service probably invoked shutdown() on it, and should better not do that.", + log_unit_error(UNIT(p->socket)->id, "%s: Got POLLHUP on a listening socket. The service probably invoked shutdown() on it, and should better not do that.", UNIT(p->socket)->id); else - log_error_unit(UNIT(p->socket)->id, "%s: Got unexpected poll event (0x%x) on socket.", + log_unit_error(UNIT(p->socket)->id, "%s: Got unexpected poll event (0x%x) on socket.", UNIT(p->socket)->id, revents); goto fail; @@ -2311,7 +2312,7 @@ static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, if (errno == EINTR) continue; - log_error_unit(UNIT(p->socket)->id, + log_unit_error(UNIT(p->socket)->id, "Failed to accept socket: %m"); goto fail; } @@ -2360,8 +2361,8 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { f = SOCKET_SUCCESS; } - log_full_unit(f == SOCKET_SUCCESS ? LOG_DEBUG : LOG_NOTICE, - u->id, + log_unit_full(u->id, + f == SOCKET_SUCCESS ? LOG_DEBUG : LOG_NOTICE, "%s control process exited, code=%s status=%i", u->id, sigchld_code_to_string(code), status); @@ -2372,7 +2373,7 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { s->control_command->command_next && f == SOCKET_SUCCESS) { - log_debug_unit(u->id, + log_unit_debug(u->id, "%s running next command for state %s", u->id, socket_state_to_string(s->state)); socket_run_next(s); @@ -2383,7 +2384,7 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { /* No further commands for this step, so let's figure * out what to do next */ - log_debug_unit(u->id, + log_unit_debug(u->id, "%s got final SIGCHLD for state %s", u->id, socket_state_to_string(s->state)); @@ -2440,53 +2441,53 @@ static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *use switch (s->state) { case SOCKET_START_PRE: - log_warning_unit(UNIT(s)->id, "%s starting timed out. Terminating.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s starting timed out. Terminating.", UNIT(s)->id); socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_TIMEOUT); break; case SOCKET_START_CHOWN: case SOCKET_START_POST: - log_warning_unit(UNIT(s)->id, "%s starting timed out. Stopping.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s starting timed out. Stopping.", UNIT(s)->id); socket_enter_stop_pre(s, SOCKET_FAILURE_TIMEOUT); break; case SOCKET_STOP_PRE: - log_warning_unit(UNIT(s)->id, "%s stopping timed out. Terminating.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s stopping timed out. Terminating.", UNIT(s)->id); socket_enter_signal(s, SOCKET_STOP_PRE_SIGTERM, SOCKET_FAILURE_TIMEOUT); break; case SOCKET_STOP_PRE_SIGTERM: if (s->kill_context.send_sigkill) { - log_warning_unit(UNIT(s)->id, "%s stopping timed out. Killing.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s stopping timed out. Killing.", UNIT(s)->id); socket_enter_signal(s, SOCKET_STOP_PRE_SIGKILL, SOCKET_FAILURE_TIMEOUT); } else { - log_warning_unit(UNIT(s)->id, "%s stopping timed out. Skipping SIGKILL. Ignoring.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s stopping timed out. Skipping SIGKILL. Ignoring.", UNIT(s)->id); socket_enter_stop_post(s, SOCKET_FAILURE_TIMEOUT); } break; case SOCKET_STOP_PRE_SIGKILL: - log_warning_unit(UNIT(s)->id, "%s still around after SIGKILL. Ignoring.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s still around after SIGKILL. Ignoring.", UNIT(s)->id); socket_enter_stop_post(s, SOCKET_FAILURE_TIMEOUT); break; case SOCKET_STOP_POST: - log_warning_unit(UNIT(s)->id, "%s stopping timed out (2). Terminating.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s stopping timed out (2). Terminating.", UNIT(s)->id); socket_enter_signal(s, SOCKET_FINAL_SIGTERM, SOCKET_FAILURE_TIMEOUT); break; case SOCKET_FINAL_SIGTERM: if (s->kill_context.send_sigkill) { - log_warning_unit(UNIT(s)->id, "%s stopping timed out (2). Killing.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s stopping timed out (2). Killing.", UNIT(s)->id); socket_enter_signal(s, SOCKET_FINAL_SIGKILL, SOCKET_FAILURE_TIMEOUT); } else { - log_warning_unit(UNIT(s)->id, "%s stopping timed out (2). Skipping SIGKILL. Ignoring.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s stopping timed out (2). Skipping SIGKILL. Ignoring.", UNIT(s)->id); socket_enter_dead(s, SOCKET_FAILURE_TIMEOUT); } break; case SOCKET_FINAL_SIGKILL: - log_warning_unit(UNIT(s)->id, "%s still around after SIGKILL (2). Entering failed mode.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s still around after SIGKILL (2). Entering failed mode.", UNIT(s)->id); socket_enter_dead(s, SOCKET_FAILURE_TIMEOUT); break; @@ -2555,7 +2556,7 @@ static void socket_notify_service_dead(Socket *s, bool failed_permanent) { * services. */ if (s->state == SOCKET_RUNNING) { - log_debug_unit(UNIT(s)->id, "%s got notified about service death (failed permanently: %s)", UNIT(s)->id, yes_no(failed_permanent)); + log_unit_debug(UNIT(s)->id, "%s got notified about service death (failed permanently: %s)", UNIT(s)->id, yes_no(failed_permanent)); if (failed_permanent) socket_enter_stop_pre(s, SOCKET_FAILURE_SERVICE_FAILED_PERMANENT); else @@ -2574,7 +2575,7 @@ void socket_connection_unref(Socket *s) { assert(s->n_connections > 0); s->n_connections--; - log_debug_unit(UNIT(s)->id, "%s: One connection closed, %u left.", UNIT(s)->id, s->n_connections); + log_unit_debug(UNIT(s)->id, "%s: One connection closed, %u left.", UNIT(s)->id, s->n_connections); } static void socket_trigger_notify(Unit *u, Unit *other) { diff --git a/src/core/swap.c b/src/core/swap.c index 1add722bf1..a6a23554c9 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -35,7 +35,7 @@ #include "unit-name.h" #include "dbus-swap.h" #include "special.h" -#include "bus-errors.h" +#include "bus-common-errors.h" #include "exit-status.h" #include "def.h" #include "path-util.h" @@ -209,8 +209,6 @@ static int swap_add_device_links(Swap *s) { } static int swap_add_default_dependencies(Swap *s) { - int r; - assert(s); if (UNIT(s)->manager->running_as != SYSTEMD_SYSTEM) @@ -219,19 +217,7 @@ static int swap_add_default_dependencies(Swap *s) { if (detect_container(NULL) > 0) return 0; - r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true); - if (r < 0) - return r; - - if (!s->from_fragment) - /* The swap unit can either be for an alternative device name, in which - * case we don't need to add the dependency on swap.target because this unit - * is following a different unit which will have this dependency added, - * or it can be derived from /proc/swaps, in which case it was started - * manually, and should not become a dependency of swap.target. */ - return 0; - - return unit_add_two_dependencies_by_name_inverse(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SWAP_TARGET, NULL, true); + return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true); } static int swap_verify(Swap *s) { @@ -247,12 +233,12 @@ static int swap_verify(Swap *s) { b = unit_has_name(UNIT(s), e); if (!b) { - log_error_unit(UNIT(s)->id, "%s: Value of \"What\" and unit name do not match, not loading.", UNIT(s)->id); + log_unit_error(UNIT(s)->id, "%s: Value of \"What\" and unit name do not match, not loading.", UNIT(s)->id); return -EINVAL; } if (s->exec_context.pam_name && s->kill_context.kill_mode != KILL_CONTROL_GROUP) { - log_error_unit(UNIT(s)->id, "%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing to load.", UNIT(s)->id); + log_unit_error(UNIT(s)->id, "%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing to load.", UNIT(s)->id); return -EINVAL; } @@ -424,7 +410,7 @@ static int swap_add_one( return 0; fail: - log_warning_unit(e, "Failed to load swap unit: %s", strerror(-r)); + log_unit_warning_errno(e, r, "Failed to load swap unit: %m"); if (delete && u) unit_free(u); @@ -506,7 +492,7 @@ static void swap_set_state(Swap *s, SwapState state) { } if (state != old_state) - log_debug_unit(UNIT(s)->id, + log_unit_debug(UNIT(s)->id, "%s changed %s -> %s", UNIT(s)->id, swap_state_to_string(old_state), @@ -627,6 +613,7 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) { exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn; exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported; exec_params.cgroup_path = UNIT(s)->cgroup_path; + exec_params.cgroup_delegate = s->cgroup_context.delegate; exec_params.runtime_prefix = manager_get_runtime_prefix(UNIT(s)->manager); exec_params.unit_id = UNIT(s)->id; @@ -711,7 +698,7 @@ static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) { return; fail: - log_warning_unit(UNIT(s)->id, + log_unit_warning(UNIT(s)->id, "%s failed to kill processes: %s", UNIT(s)->id, strerror(-r)); swap_enter_dead(s, SWAP_FAILURE_RESOURCES); @@ -840,7 +827,7 @@ static void swap_enter_activating(Swap *s) { return; fail: - log_warning_unit(UNIT(s)->id, + log_unit_warning(UNIT(s)->id, "%s failed to run 'swapon' task: %s", UNIT(s)->id, strerror(-r)); swap_enter_dead(s, SWAP_FAILURE_RESOURCES); @@ -872,7 +859,7 @@ static void swap_enter_deactivating(Swap *s) { return; fail: - log_warning_unit(UNIT(s)->id, + log_unit_warning(UNIT(s)->id, "%s failed to run 'swapoff' task: %s", UNIT(s)->id, strerror(-r)); swap_enter_active(s, SWAP_FAILURE_RESOURCES); @@ -959,7 +946,7 @@ static int swap_deserialize_item(Unit *u, const char *key, const char *value, FD state = swap_state_from_string(value); if (state < 0) - log_debug_unit(u->id, "Failed to parse state value %s", value); + log_unit_debug(u->id, "Failed to parse state value %s", value); else s->deserialized_state = state; } else if (streq(key, "result")) { @@ -967,14 +954,14 @@ static int swap_deserialize_item(Unit *u, const char *key, const char *value, FD f = swap_result_from_string(value); if (f < 0) - log_debug_unit(u->id, "Failed to parse result value %s", value); + log_unit_debug(u->id, "Failed to parse result value %s", value); else if (f != SWAP_SUCCESS) s->result = f; } else if (streq(key, "control-pid")) { pid_t pid; if (parse_pid(value, &pid) < 0) - log_debug_unit(u->id, "Failed to parse control-pid value %s", value); + log_unit_debug(u->id, "Failed to parse control-pid value %s", value); else s->control_pid = pid; @@ -983,13 +970,13 @@ static int swap_deserialize_item(Unit *u, const char *key, const char *value, FD id = swap_exec_command_from_string(value); if (id < 0) - log_debug_unit(u->id, "Failed to parse exec-command value %s", value); + log_unit_debug(u->id, "Failed to parse exec-command value %s", value); else { s->control_command_id = id; s->control_command = s->exec_command + id; } } else - log_debug_unit(u->id, "Unknown serialization key '%s'", key); + log_unit_debug(u->id, "Unknown serialization key '%s'", key); return 0; } @@ -1047,8 +1034,8 @@ static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) { s->control_command_id = _SWAP_EXEC_COMMAND_INVALID; } - log_full_unit(f == SWAP_SUCCESS ? LOG_DEBUG : LOG_NOTICE, - u->id, + log_unit_full(u->id, + f == SWAP_SUCCESS ? LOG_DEBUG : LOG_NOTICE, "%s swap process exited, code=%s status=%i", u->id, sigchld_code_to_string(code), status); @@ -1090,38 +1077,38 @@ static int swap_dispatch_timer(sd_event_source *source, usec_t usec, void *userd case SWAP_ACTIVATING: case SWAP_ACTIVATING_DONE: - log_warning_unit(UNIT(s)->id, "%s activation timed out. Stopping.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s activation timed out. Stopping.", UNIT(s)->id); swap_enter_signal(s, SWAP_ACTIVATING_SIGTERM, SWAP_FAILURE_TIMEOUT); break; case SWAP_DEACTIVATING: - log_warning_unit(UNIT(s)->id, "%s deactivation timed out. Stopping.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s deactivation timed out. Stopping.", UNIT(s)->id); swap_enter_signal(s, SWAP_DEACTIVATING_SIGTERM, SWAP_FAILURE_TIMEOUT); break; case SWAP_ACTIVATING_SIGTERM: if (s->kill_context.send_sigkill) { - log_warning_unit(UNIT(s)->id, "%s activation timed out. Killing.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s activation timed out. Killing.", UNIT(s)->id); swap_enter_signal(s, SWAP_ACTIVATING_SIGKILL, SWAP_FAILURE_TIMEOUT); } else { - log_warning_unit(UNIT(s)->id, "%s activation timed out. Skipping SIGKILL. Ignoring.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s activation timed out. Skipping SIGKILL. Ignoring.", UNIT(s)->id); swap_enter_dead(s, SWAP_FAILURE_TIMEOUT); } break; case SWAP_DEACTIVATING_SIGTERM: if (s->kill_context.send_sigkill) { - log_warning_unit(UNIT(s)->id, "%s deactivation timed out. Killing.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s deactivation timed out. Killing.", UNIT(s)->id); swap_enter_signal(s, SWAP_DEACTIVATING_SIGKILL, SWAP_FAILURE_TIMEOUT); } else { - log_warning_unit(UNIT(s)->id, "%s deactivation timed out. Skipping SIGKILL. Ignoring.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s deactivation timed out. Skipping SIGKILL. Ignoring.", UNIT(s)->id); swap_enter_dead(s, SWAP_FAILURE_TIMEOUT); } break; case SWAP_ACTIVATING_SIGKILL: case SWAP_DEACTIVATING_SIGKILL: - log_warning_unit(UNIT(s)->id, "%s swap process still around after SIGKILL. Ignoring.", UNIT(s)->id); + log_unit_warning(UNIT(s)->id, "%s swap process still around after SIGKILL. Ignoring.", UNIT(s)->id); swap_enter_dead(s, SWAP_FAILURE_TIMEOUT); break; @@ -1183,7 +1170,7 @@ static int swap_dispatch_io(sd_event_source *source, int fd, uint32_t revents, v r = swap_load_proc_swaps(m, true); if (r < 0) { - log_error("Failed to reread /proc/swaps: %s", strerror(-r)); + log_error_errno(r, "Failed to reread /proc/swaps: %m"); /* Reset flags, just in case, for late calls */ LIST_FOREACH(units_by_type, u, m->units_by_type[UNIT_SWAP]) { diff --git a/src/core/system.conf b/src/core/system.conf index 65a35a0689..a3727200df 100644 --- a/src/core/system.conf +++ b/src/core/system.conf @@ -5,6 +5,9 @@ # the Free Software Foundation; either version 2.1 of the License, or # (at your option) any later version. # +# You can override the directives in this file by creating files in +# /etc/systemd/system.conf.d/*.conf. +# # See systemd-system.conf(5) for details [Manager] diff --git a/src/core/timer.c b/src/core/timer.c index a3713e2140..309852aae6 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -86,7 +86,7 @@ static int timer_verify(Timer *t) { return 0; if (!t->values) { - log_error_unit(UNIT(t)->id, "%s lacks value setting. Refusing.", UNIT(t)->id); + log_unit_error(UNIT(t)->id, "%s lacks value setting. Refusing.", UNIT(t)->id); return -EINVAL; } @@ -147,10 +147,8 @@ static int timer_setup_persistent(Timer *t) { _cleanup_free_ char *h = NULL; r = get_home_dir(&h); - if (r < 0) { - log_error("Failed to determine home directory: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to determine home directory: %m"); t->stamp_path = strjoin(h, "/.local/share/systemd/timers/stamp-", UNIT(t)->id, NULL); } @@ -260,7 +258,7 @@ static void timer_set_state(Timer *t, TimerState state) { } if (state != old_state) - log_debug_unit(UNIT(t)->id, + log_unit_debug(UNIT(t)->id, "%s changed %s -> %s", UNIT(t)->id, timer_state_to_string(old_state), timer_state_to_string(state)); @@ -419,7 +417,7 @@ static void timer_enter_waiting(Timer *t, bool initial) { } if (!found_monotonic && !found_realtime) { - log_debug_unit(UNIT(t)->id, "%s: Timer is elapsed.", UNIT(t)->id); + log_unit_debug(UNIT(t)->id, "%s: Timer is elapsed.", UNIT(t)->id); timer_set_state(t, TIMER_ELAPSED); return; } @@ -427,7 +425,7 @@ static void timer_enter_waiting(Timer *t, bool initial) { if (found_monotonic) { char buf[FORMAT_TIMESPAN_MAX]; - log_debug_unit(UNIT(t)->id, "%s: Monotonic timer elapses in %s.", + log_unit_debug(UNIT(t)->id, "%s: Monotonic timer elapses in %s.", UNIT(t)->id, format_timespan(buf, sizeof(buf), t->next_elapse_monotonic_or_boottime > ts_monotonic ? t->next_elapse_monotonic_or_boottime - ts_monotonic : 0, 0)); @@ -456,7 +454,7 @@ static void timer_enter_waiting(Timer *t, bool initial) { if (found_realtime) { char buf[FORMAT_TIMESTAMP_MAX]; - log_debug_unit(UNIT(t)->id, "%s: Realtime timer elapses at %s.", UNIT(t)->id, format_timestamp(buf, sizeof(buf), t->next_elapse_realtime)); + log_unit_debug(UNIT(t)->id, "%s: Realtime timer elapses at %s.", UNIT(t)->id, format_timestamp(buf, sizeof(buf), t->next_elapse_realtime)); if (t->realtime_event_source) { r = sd_event_source_set_time(t->realtime_event_source, t->next_elapse_realtime); @@ -485,7 +483,7 @@ static void timer_enter_waiting(Timer *t, bool initial) { return; fail: - log_warning_unit(UNIT(t)->id, "%s failed to enter waiting state: %s", UNIT(t)->id, strerror(-r)); + log_unit_warning_errno(UNIT(t)->id, r, "%s failed to enter waiting state: %m", UNIT(t)->id); timer_enter_dead(t, TIMER_FAILURE_RESOURCES); } @@ -507,13 +505,13 @@ static void timer_enter_running(Timer *t) { dual_timestamp_get(&t->last_trigger); if (t->stamp_path) - touch_file(t->stamp_path, true, t->last_trigger.realtime, (uid_t) -1, (gid_t) -1, 0); + touch_file(t->stamp_path, true, t->last_trigger.realtime, UID_INVALID, GID_INVALID, 0); timer_set_state(t, TIMER_RUNNING); return; fail: - log_warning_unit(UNIT(t)->id, + log_unit_warning(UNIT(t)->id, "%s failed to queue unit startup job: %s", UNIT(t)->id, bus_error_message(&error, r)); timer_enter_dead(t, TIMER_FAILURE_RESOURCES); @@ -521,6 +519,7 @@ fail: static int timer_start(Unit *u) { Timer *t = TIMER(u); + TimerValue *v; assert(t); assert(t->state == TIMER_DEAD || t->state == TIMER_FAILED); @@ -530,6 +529,11 @@ static int timer_start(Unit *u) { t->last_trigger = DUAL_TIMESTAMP_NULL; + /* Reenable all timers that depend on unit activation time */ + LIST_FOREACH(value, v, t->values) + if (v->base == TIMER_ACTIVE) + v->disabled = false; + if (t->stamp_path) { struct stat st; @@ -539,7 +543,7 @@ static int timer_start(Unit *u) { /* The timer has never run before, * make sure a stamp file exists. */ - touch_file(t->stamp_path, true, (usec_t) -1, (uid_t) -1, (gid_t) -1, 0); + touch_file(t->stamp_path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0); } t->result = TIMER_SUCCESS; @@ -590,7 +594,7 @@ static int timer_deserialize_item(Unit *u, const char *key, const char *value, F state = timer_state_from_string(value); if (state < 0) - log_debug_unit(u->id, "Failed to parse state value %s", value); + log_unit_debug(u->id, "Failed to parse state value %s", value); else t->deserialized_state = state; } else if (streq(key, "result")) { @@ -598,23 +602,23 @@ static int timer_deserialize_item(Unit *u, const char *key, const char *value, F f = timer_result_from_string(value); if (f < 0) - log_debug_unit(u->id, "Failed to parse result value %s", value); + log_unit_debug(u->id, "Failed to parse result value %s", value); else if (f != TIMER_SUCCESS) t->result = f; } else if (streq(key, "last-trigger-realtime")) { r = safe_atou64(value, &t->last_trigger.realtime); if (r < 0) - log_debug_unit(u->id, "Failed to parse last-trigger-realtime value %s", value); + log_unit_debug(u->id, "Failed to parse last-trigger-realtime value %s", value); } else if (streq(key, "last-trigger-monotonic")) { r = safe_atou64(value, &t->last_trigger.monotonic); if (r < 0) - log_debug_unit(u->id, "Failed to parse last-trigger-monotonic value %s", value); + log_unit_debug(u->id, "Failed to parse last-trigger-monotonic value %s", value); } else - log_debug_unit(u->id, "Unknown serialization key '%s'", key); + log_unit_debug(u->id, "Unknown serialization key '%s'", key); return 0; } @@ -639,7 +643,7 @@ static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata) { if (t->state != TIMER_WAITING) return 0; - log_debug_unit(UNIT(t)->id, "Timer elapsed on %s", UNIT(t)->id); + log_unit_debug(UNIT(t)->id, "Timer elapsed on %s", UNIT(t)->id); timer_enter_running(t); return 0; } @@ -672,7 +676,7 @@ static void timer_trigger_notify(Unit *u, Unit *other) { case TIMER_RUNNING: if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(other))) { - log_debug_unit(UNIT(t)->id, "%s got notified about unit deactivation.", UNIT(t)->id); + log_unit_debug(UNIT(t)->id, "%s got notified about unit deactivation.", UNIT(t)->id); timer_enter_waiting(t, false); } break; @@ -705,7 +709,7 @@ static void timer_time_change(Unit *u) { if (t->state != TIMER_WAITING) return; - log_debug_unit(u->id, "%s: time change, recalculating next elapse.", u->id); + log_unit_debug(u->id, "%s: time change, recalculating next elapse.", u->id); timer_enter_waiting(t, false); } @@ -744,6 +748,7 @@ const UnitVTable timer_vtable = { "Unit\0" "Timer\0" "Install\0", + .private_section = "Timer", .init = timer_init, .done = timer_done, @@ -769,4 +774,7 @@ const UnitVTable timer_vtable = { .bus_interface = "org.freedesktop.systemd1.Timer", .bus_vtable = bus_timer_vtable, + .bus_set_property = bus_timer_set_property, + + .can_transient = true, }; diff --git a/src/core/transaction.c b/src/core/transaction.c index dbb4133fe3..b0b3d6bd60 100644 --- a/src/core/transaction.c +++ b/src/core/transaction.c @@ -22,7 +22,7 @@ #include <unistd.h> #include <fcntl.h> -#include "bus-errors.h" +#include "bus-common-errors.h" #include "bus-util.h" #include "bus-error.h" #include "transaction.h" @@ -189,11 +189,11 @@ static int delete_one_unmergeable_job(Transaction *tr, Job *j) { * another unit in which case we * rather remove the start. */ - log_debug_unit(j->unit->id, + log_unit_debug(j->unit->id, "Looking at job %s/%s conflicted_by=%s", j->unit->id, job_type_to_string(j->type), yes_no(j->type == JOB_STOP && job_is_conflicted_by(j))); - log_debug_unit(k->unit->id, + log_unit_debug(k->unit->id, "Looking at job %s/%s conflicted_by=%s", k->unit->id, job_type_to_string(k->type), yes_no(k->type == JOB_STOP && job_is_conflicted_by(k))); @@ -222,7 +222,7 @@ static int delete_one_unmergeable_job(Transaction *tr, Job *j) { return -ENOEXEC; /* Ok, we can drop one, so let's do so. */ - log_debug_unit(d->unit->id, + log_unit_debug(d->unit->id, "Fixing conflicting jobs %s/%s,%s/%s by deleting job %s/%s", j->unit->id, job_type_to_string(j->type), k->unit->id, job_type_to_string(k->type), @@ -264,13 +264,12 @@ static int transaction_merge_jobs(Transaction *tr, sd_bus_error *e) { return -EAGAIN; /* We couldn't merge anything. Failure */ - sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_JOBS_CONFLICTING, - "Transaction contains conflicting jobs '%s' and '%s' for %s. " - "Probably contradicting requirement dependencies configured.", - job_type_to_string(t), - job_type_to_string(k->type), - k->unit->id); - return r; + return sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_JOBS_CONFLICTING, + "Transaction contains conflicting jobs '%s' and '%s' for %s. " + "Probably contradicting requirement dependencies configured.", + job_type_to_string(t), + job_type_to_string(k->type), + k->unit->id); } } @@ -369,7 +368,7 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi * job to remove. We use the marker to find our way * back, since smart how we are we stored our way back * in there. */ - log_warning_unit(j->unit->id, + log_unit_warning(j->unit->id, "Found ordering cycle on %s/%s", j->unit->id, job_type_to_string(j->type)); @@ -377,9 +376,9 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi for (k = from; k; k = ((k->generation == generation && k->marker != k) ? k->marker : NULL)) { /* logging for j not k here here to provide consistent narrative */ - log_info_unit(j->unit->id, - "Found dependency on %s/%s", - k->unit->id, job_type_to_string(k->type)); + log_unit_warning(j->unit->id, + "Found dependency on %s/%s", + k->unit->id, job_type_to_string(k->type)); if (!delete && hashmap_get(tr->jobs, k->unit) && !unit_matters_to_anchor(k->unit, k)) { @@ -397,10 +396,10 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi if (delete) { /* logging for j not k here here to provide consistent narrative */ - log_warning_unit(j->unit->id, + log_unit_warning(j->unit->id, "Breaking ordering cycle by deleting job %s/%s", delete->unit->id, job_type_to_string(delete->type)); - log_error_unit(delete->unit->id, + log_unit_error(delete->unit->id, "Job %s/%s deleted to break ordering cycle starting with %s/%s", delete->unit->id, job_type_to_string(delete->type), j->unit->id, job_type_to_string(j->type)); @@ -412,9 +411,8 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi log_error("Unable to break cycle"); - sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, - "Transaction order is cyclic. See system logs for details."); - return -ENOEXEC; + return sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, + "Transaction order is cyclic. See system logs for details."); } /* Make the marker point to where we come from, so that we can @@ -513,12 +511,9 @@ static int transaction_is_destructive(Transaction *tr, JobMode mode, sd_bus_erro assert(!j->transaction_next); if (j->unit->job && (mode == JOB_FAIL || j->unit->job->irreversible) && - !job_type_is_superset(j->type, j->unit->job->type)) { - - sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, - "Transaction is destructive."); - return -EEXIST; - } + job_type_is_conflicting(j->unit->job->type, j->type)) + return sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, + "Transaction is destructive."); } return 0; @@ -557,17 +552,17 @@ rescan: continue; if (stops_running_service) - log_debug_unit(j->unit->id, + log_unit_debug(j->unit->id, "%s/%s would stop a running service.", j->unit->id, job_type_to_string(j->type)); if (changes_existing_job) - log_debug_unit(j->unit->id, + log_unit_debug(j->unit->id, "%s/%s would change existing job.", j->unit->id, job_type_to_string(j->type)); /* Ok, let's get rid of this */ - log_debug_unit(j->unit->id, + log_unit_debug(j->unit->id, "Deleting %s/%s to minimize impact.", j->unit->id, job_type_to_string(j->type)); @@ -727,10 +722,8 @@ int transaction_activate(Transaction *tr, Manager *m, JobMode mode, sd_bus_error /* Tenth step: apply changes */ r = transaction_apply(tr, m, mode); - if (r < 0) { - log_warning("Failed to apply transaction: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_warning_errno(r, "Failed to apply transaction: %m"); assert(hashmap_isempty(tr->jobs)); @@ -824,7 +817,7 @@ static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependen job_dependency_free(j->object_list); if (other && delete_dependencies) { - log_debug_unit(other->unit->id, + log_unit_debug(other->unit->id, "Deleting job %s/%s as dependency of job %s/%s", other->unit->id, job_type_to_string(other->type), j->unit->id, job_type_to_string(j->type)); @@ -860,50 +853,39 @@ int transaction_add_job_and_dependencies( /* by ? by->unit->id : "NA", */ /* by ? job_type_to_string(by->type) : "NA"); */ - if (unit->load_state != UNIT_LOADED && - unit->load_state != UNIT_ERROR && - unit->load_state != UNIT_NOT_FOUND && - unit->load_state != UNIT_MASKED) { - sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED, - "Unit %s is not loaded properly.", unit->id); - return -EINVAL; - } + if (!IN_SET(unit->load_state, UNIT_LOADED, UNIT_ERROR, UNIT_NOT_FOUND, UNIT_MASKED)) + return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED, + "Unit %s is not loaded properly.", unit->id); if (type != JOB_STOP && unit->load_state == UNIT_ERROR) { if (unit->load_error == -ENOENT || unit->manager->test_run) - sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED, - "Unit %s failed to load: %s.", - unit->id, - strerror(-unit->load_error)); + return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED, + "Unit %s failed to load: %s.", + unit->id, + strerror(-unit->load_error)); else - sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED, - "Unit %s failed to load: %s. " - "See system logs and 'systemctl status %s' for details.", - unit->id, - strerror(-unit->load_error), - unit->id); - return -EINVAL; + return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED, + "Unit %s failed to load: %s. " + "See system logs and 'systemctl status %s' for details.", + unit->id, + strerror(-unit->load_error), + unit->id); } - if (type != JOB_STOP && unit->load_state == UNIT_NOT_FOUND) { - sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED, - "Unit %s failed to load: %s.", - unit->id, strerror(-unit->load_error)); - return -EINVAL; - } + if (type != JOB_STOP && unit->load_state == UNIT_NOT_FOUND) + return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED, + "Unit %s failed to load: %s.", + unit->id, strerror(-unit->load_error)); - if (type != JOB_STOP && unit->load_state == UNIT_MASKED) { - sd_bus_error_setf(e, BUS_ERROR_UNIT_MASKED, - "Unit %s is masked.", unit->id); - return -EADDRNOTAVAIL; - } + if (type != JOB_STOP && unit->load_state == UNIT_MASKED) + return sd_bus_error_setf(e, BUS_ERROR_UNIT_MASKED, + "Unit %s is masked.", unit->id); + + if (!unit_job_is_applicable(unit, type)) + return sd_bus_error_setf(e, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE, + "Job type %s is not applicable for unit %s.", + job_type_to_string(type), unit->id); - if (!unit_job_is_applicable(unit, type)) { - sd_bus_error_setf(e, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE, - "Job type %s is not applicable for unit %s.", - job_type_to_string(type), unit->id); - return -EBADR; - } /* First add the job. */ ret = transaction_add_one_job(tr, type, unit, override, &is_new); @@ -931,7 +913,7 @@ int transaction_add_job_and_dependencies( SET_FOREACH(dep, following, i) { r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, override, false, false, ignore_order, e); if (r < 0) { - log_warning_unit(dep->id, + log_unit_warning(dep->id, "Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error_message(e, r)); @@ -970,7 +952,8 @@ int transaction_add_job_and_dependencies( SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES_OVERRIDABLE], i) { r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, !override, override, false, false, ignore_order, e); if (r < 0) { - log_full_unit(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, dep->id, + log_unit_full(dep->id, + r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, "Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error_message(e, r)); @@ -982,7 +965,8 @@ int transaction_add_job_and_dependencies( SET_FOREACH(dep, ret->unit->dependencies[UNIT_WANTS], i) { r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, false, ignore_order, e); if (r < 0) { - log_full_unit(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, dep->id, + log_unit_full(dep->id, + r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, "Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error_message(e, r)); @@ -1005,7 +989,8 @@ int transaction_add_job_and_dependencies( SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE_OVERRIDABLE], i) { r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, !override, override, false, false, ignore_order, e); if (r < 0) { - log_full_unit(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, dep->id, + log_unit_full(dep->id, + r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, "Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error_message(e, r)); @@ -1028,7 +1013,7 @@ int transaction_add_job_and_dependencies( SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTED_BY], i) { r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, override, false, false, ignore_order, e); if (r < 0) { - log_warning_unit(dep->id, + log_unit_warning(dep->id, "Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error_message(e, r)); @@ -1081,7 +1066,7 @@ int transaction_add_job_and_dependencies( SET_FOREACH(dep, ret->unit->dependencies[UNIT_PROPAGATES_RELOAD_TO], i) { r = transaction_add_job_and_dependencies(tr, JOB_RELOAD, dep, ret, false, override, false, false, ignore_order, e); if (r < 0) { - log_warning_unit(dep->id, + log_unit_warning(dep->id, "Cannot add dependency reload job for unit %s, ignoring: %s", dep->id, bus_error_message(e, r)); @@ -1128,7 +1113,7 @@ int transaction_add_isolate_jobs(Transaction *tr, Manager *m) { r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, true, false, false, false, false, NULL); if (r < 0) - log_warning_unit(u->id, + log_unit_warning(u->id, "Cannot add isolate job for unit %s, ignoring: %s", u->id, strerror(-r)); } diff --git a/src/core/umount.c b/src/core/umount.c index cffa45327b..e38851dc12 100644 --- a/src/core/umount.c +++ b/src/core/umount.c @@ -401,7 +401,7 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_e mount_point_free(head, m); } else if (log_error) { - log_warning("Could not unmount %s: %m", m->path); + log_warning_errno(errno, "Could not unmount %s: %m", m->path); n_failed++; } } @@ -423,7 +423,7 @@ static int swap_points_list_off(MountPoint **head, bool *changed) { mount_point_free(head, m); } else { - log_warning("Could not deactivate swap %s: %m", m->path); + log_warning_errno(errno, "Could not deactivate swap %s: %m", m->path); n_failed++; } } @@ -460,7 +460,7 @@ static int loopback_points_list_detach(MountPoint **head, bool *changed) { mount_point_free(head, m); } else { - log_warning("Could not detach loopback %s: %m", m->path); + log_warning_errno(errno, "Could not detach loopback %s: %m", m->path); n_failed++; } } @@ -495,7 +495,7 @@ static int dm_points_list_detach(MountPoint **head, bool *changed) { mount_point_free(head, m); } else { - log_warning("Could not detach DM %s: %m", m->path); + log_warning_errno(errno, "Could not detach DM %s: %m", m->path); n_failed++; } } diff --git a/src/core/unit.c b/src/core/unit.c index 84f210a312..fe0dfb2083 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -47,7 +47,7 @@ #include "mkdir.h" #include "label.h" #include "fileio-label.h" -#include "bus-errors.h" +#include "bus-common-errors.h" #include "dbus.h" #include "execute.h" #include "virt.h" @@ -92,6 +92,7 @@ Unit *unit_new(Manager *m, size_t size) { u->deserialized_job = _JOB_TYPE_INVALID; u->default_dependencies = true; u->unit_file_state = _UNIT_FILE_STATE_INVALID; + u->unit_file_preset = -1; u->on_failure_job_mode = JOB_REPLACE; return u; @@ -527,6 +528,7 @@ void unit_free(Unit *u) { unit_unwatch_all_pids(u); condition_free_list(u->conditions); + condition_free_list(u->asserts); unit_ref_unset(&u->slice); @@ -929,7 +931,8 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { if (u->job_timeout_reboot_arg) fprintf(f, "%s\tJob Timeout Reboot Argument: %s\n", prefix, u->job_timeout_reboot_arg); - condition_dump_list(u->conditions, f, prefix); + condition_dump_list(u->conditions, f, prefix, condition_type_to_string); + condition_dump_list(u->asserts, f, prefix, assert_type_to_string); if (dual_timestamp_is_set(&u->condition_timestamp)) fprintf(f, @@ -938,6 +941,13 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { prefix, strna(format_timestamp(timestamp1, sizeof(timestamp1), u->condition_timestamp.realtime)), prefix, yes_no(u->condition_result)); + if (dual_timestamp_is_set(&u->assert_timestamp)) + fprintf(f, + "%s\tAssert Timestamp: %s\n" + "%s\tAssert Result: %s\n", + prefix, strna(format_timestamp(timestamp1, sizeof(timestamp1), u->assert_timestamp.realtime)), + prefix, yes_no(u->assert_result)); + for (d = 0; d < _UNIT_DEPENDENCY_MAX; d++) { Unit *other; @@ -1209,7 +1219,7 @@ int unit_load(Unit *u) { goto fail; if (u->on_failure_job_mode == JOB_ISOLATE && set_size(u->dependencies[UNIT_ON_FAILURE]) > 1) { - log_error_unit(u->id, "More than one OnFailure= dependencies specified for %s but OnFailureJobMode=isolate set. Refusing.", u->id); + log_unit_error(u->id, "More than one OnFailure= dependencies specified for %s but OnFailureJobMode=isolate set. Refusing.", u->id); r = -EINVAL; goto fail; } @@ -1230,21 +1240,77 @@ fail: unit_add_to_dbus_queue(u); unit_add_to_gc_queue(u); - log_debug_unit(u->id, "Failed to load configuration for %s: %s", + log_unit_debug(u->id, "Failed to load configuration for %s: %s", u->id, strerror(-r)); return r; } +static bool unit_condition_test_list(Unit *u, Condition *first, const char *(*to_string)(ConditionType t)) { + Condition *c; + int triggered = -1; + + assert(u); + assert(to_string); + + /* If the condition list is empty, then it is true */ + if (!first) + return true; + + /* Otherwise, if all of the non-trigger conditions apply and + * if any of the trigger conditions apply (unless there are + * none) we return true */ + LIST_FOREACH(conditions, c, first) { + int r; + + r = condition_test(c); + if (r < 0) + log_unit_warning(u->id, + "Couldn't determine result for %s=%s%s%s for %s, assuming failed: %s", + to_string(c->type), + c->trigger ? "|" : "", + c->negate ? "!" : "", + c->parameter, + u->id, + strerror(-r)); + else + log_unit_debug(u->id, + "%s=%s%s%s %s for %s.", + to_string(c->type), + c->trigger ? "|" : "", + c->negate ? "!" : "", + c->parameter, + condition_result_to_string(c->result), + u->id); + + if (!c->trigger && r <= 0) + return false; + + if (c->trigger && triggered <= 0) + triggered = r > 0; + } + + return triggered != 0; +} + static bool unit_condition_test(Unit *u) { assert(u); dual_timestamp_get(&u->condition_timestamp); - u->condition_result = condition_test_list(u->id, u->conditions); + u->condition_result = unit_condition_test_list(u, u->conditions, condition_type_to_string); return u->condition_result; } +static bool unit_assert_test(Unit *u) { + assert(u); + + dual_timestamp_get(&u->assert_timestamp); + u->assert_result = unit_condition_test_list(u, u->asserts, assert_type_to_string); + + return u->assert_result; +} + _pure_ static const char* unit_get_status_message_format(Unit *u, JobType t) { const UnitStatusMessageFormats *format_table; @@ -1329,10 +1395,10 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) { t == JOB_STOP ? SD_MESSAGE_UNIT_STOPPING : SD_MESSAGE_UNIT_RELOADING; - log_struct_unit(LOG_INFO, - u->id, - MESSAGE_ID(mid), - "MESSAGE=%s", buf, + log_unit_struct(u->id, + LOG_INFO, + LOG_MESSAGE_ID(mid), + LOG_MESSAGE("%s", buf), NULL); } @@ -1341,6 +1407,7 @@ static void unit_status_log_starting_stopping_reloading(Unit *u, JobType t) { * -EALREADY: Unit is already started. * -EAGAIN: An operation is already in progress. Retry later. * -ECANCELED: Too many requests for now. + * -EPROTO: Assert failed */ int unit_start(Unit *u) { UnitActiveState state; @@ -1365,15 +1432,21 @@ int unit_start(Unit *u) { * but we don't want to recheck the condition in that case. */ if (state != UNIT_ACTIVATING && !unit_condition_test(u)) { - log_debug_unit(u->id, "Starting of %s requested but condition failed. Ignoring.", u->id); + log_unit_debug(u->id, "Starting of %s requested but condition failed. Not starting unit.", u->id); return -EALREADY; } + /* If the asserts failed, fail the entire job */ + if (state != UNIT_ACTIVATING && + !unit_assert_test(u)) { + log_unit_debug(u->id, "Starting of %s requested but asserts failed.", u->id); + return -EPROTO; + } + /* Forward to the main object, if we aren't it. */ following = unit_following(u); if (following) { - log_debug_unit(u->id, "Redirecting start request from %s to %s.", - u->id, following->id); + log_unit_debug(u->id, "Redirecting start request from %s to %s.", u->id, following->id); return unit_start(following); } @@ -1424,7 +1497,7 @@ int unit_stop(Unit *u) { return -EALREADY; if ((following = unit_following(u))) { - log_debug_unit(u->id, "Redirecting stop request from %s to %s.", + log_unit_debug(u->id, "Redirecting stop request from %s to %s.", u->id, following->id); return unit_stop(following); } @@ -1462,14 +1535,14 @@ int unit_reload(Unit *u) { return -EALREADY; if (state != UNIT_ACTIVE) { - log_warning_unit(u->id, "Unit %s cannot be reloaded because it is inactive.", + log_unit_warning(u->id, "Unit %s cannot be reloaded because it is inactive.", u->id); return -ENOEXEC; } following = unit_following(u); if (following) { - log_debug_unit(u->id, "Redirecting reload request from %s to %s.", + log_unit_debug(u->id, "Redirecting reload request from %s to %s.", u->id, following->id); return unit_reload(following); } @@ -1523,7 +1596,7 @@ static void unit_check_unneeded(Unit *u) { if (unit_active_or_pending(other)) return; - log_info_unit(u->id, "Unit %s is not needed anymore. Stopping.", u->id); + log_unit_info(u->id, "Unit %s is not needed anymore. Stopping.", u->id); /* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */ manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL); @@ -1555,7 +1628,7 @@ static void unit_check_binds_to(Unit *u) { if (!stop) return; - log_info_unit(u->id, "Unit %s is bound to inactive service. Stopping, too.", u->id); + log_unit_info(u->id, "Unit %s is bound to inactive service. Stopping, too.", u->id); /* A unit we need to run is gone. Sniff. Let's stop this. */ manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL); @@ -1647,14 +1720,14 @@ void unit_start_on_failure(Unit *u) { if (set_size(u->dependencies[UNIT_ON_FAILURE]) <= 0) return; - log_info_unit(u->id, "Triggering OnFailure= dependencies of %s.", u->id); + log_unit_info(u->id, "Triggering OnFailure= dependencies of %s.", u->id); SET_FOREACH(other, u->dependencies[UNIT_ON_FAILURE], i) { int r; r = manager_add_job(u->manager, JOB_START, other, u->on_failure_job_mode, true, NULL, NULL); if (r < 0) - log_error_unit(u->id, "Failed to enqueue OnFailure= job: %s", strerror(-r)); + log_unit_error_errno(u->id, r, "Failed to enqueue OnFailure= job: %m"); } } @@ -1710,7 +1783,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su /* Make sure the cgroup is always removed when we become inactive */ if (UNIT_IS_INACTIVE_OR_FAILED(ns)) - unit_destroy_cgroup(u); + unit_destroy_cgroup_if_empty(u); /* Note that this doesn't apply to RemainAfterExit services exiting * successfully, since there's no change of state in that case. Which is @@ -1818,7 +1891,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su check_unneeded_dependencies(u); if (ns != os && ns == UNIT_FAILED) { - log_notice_unit(u->id, "Unit %s entered failed state.", u->id); + log_unit_notice(u->id, "Unit %s entered failed state.", u->id); unit_start_on_failure(u); } } @@ -2080,10 +2153,10 @@ static int maybe_warn_about_dependency(const char *id, const char *other, UnitDe case UNIT_TRIGGERS: case UNIT_TRIGGERED_BY: if (streq_ptr(id, other)) - log_warning_unit(id, "Dependency %s=%s dropped from unit %s", + log_unit_warning(id, "Dependency %s=%s dropped from unit %s", unit_dependency_to_string(dependency), id, other); else - log_warning_unit(id, "Dependency %s=%s dropped from unit %s merged into %s", + log_unit_warning(id, "Dependency %s=%s dropped from unit %s merged into %s", unit_dependency_to_string(dependency), id, strna(other), id); return -EINVAL; @@ -2502,10 +2575,14 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) { dual_timestamp_serialize(f, "active-exit-timestamp", &u->active_exit_timestamp); dual_timestamp_serialize(f, "inactive-enter-timestamp", &u->inactive_enter_timestamp); dual_timestamp_serialize(f, "condition-timestamp", &u->condition_timestamp); + dual_timestamp_serialize(f, "assert-timestamp", &u->assert_timestamp); if (dual_timestamp_is_set(&u->condition_timestamp)) unit_serialize_item(u, f, "condition-result", yes_no(u->condition_result)); + if (dual_timestamp_is_set(&u->assert_timestamp)) + unit_serialize_item(u, f, "assert-result", yes_no(u->assert_result)); + unit_serialize_item(u, f, "transient", yes_no(u->transient)); if (u->cgroup_path) @@ -2645,6 +2722,9 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { } else if (streq(l, "condition-timestamp")) { dual_timestamp_deserialize(v, &u->condition_timestamp); continue; + } else if (streq(l, "assert-timestamp")) { + dual_timestamp_deserialize(v, &u->assert_timestamp); + continue; } else if (streq(l, "condition-result")) { int b; @@ -2656,6 +2736,17 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) { continue; + } else if (streq(l, "assert-result")) { + int b; + + b = parse_boolean(v); + if (b < 0) + log_debug("Failed to parse assert result value %s", v); + else + u->assert_result = b; + + continue; + } else if (streq(l, "transient")) { int b; @@ -2939,18 +3030,16 @@ int unit_kill_common( if (who == KILL_MAIN && main_pid <= 0) { if (main_pid < 0) - sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no main processes", unit_type_to_string(u->type)); + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no main processes", unit_type_to_string(u->type)); else - sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill"); - return -ESRCH; + return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill"); } if (who == KILL_CONTROL && control_pid <= 0) { if (control_pid < 0) - sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no control processes", unit_type_to_string(u->type)); + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no control processes", unit_type_to_string(u->type)); else - sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill"); - return -ESRCH; + return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill"); } if (who == KILL_CONTROL || who == KILL_ALL) @@ -3002,6 +3091,17 @@ UnitFileState unit_get_unit_file_state(Unit *u) { return u->unit_file_state; } +int unit_get_unit_file_preset(Unit *u) { + assert(u); + + if (u->unit_file_preset < 0 && u->fragment_path) + u->unit_file_preset = unit_file_query_preset( + u->manager->running_as == SYSTEMD_SYSTEM ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, + NULL, basename(u->fragment_path)); + + return u->unit_file_preset; +} + Unit* unit_ref_set(UnitRef *ref, Unit *u) { assert(ref); assert(u); @@ -3172,7 +3272,7 @@ static int unit_drop_in_file(Unit *u, int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data) { - _cleanup_free_ char *dir = NULL; + _cleanup_free_ char *dir = NULL, *p = NULL, *q = NULL; int r; assert(u); @@ -3184,7 +3284,24 @@ int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, co if (r < 0) return r; - return write_drop_in(dir, u->id, 50, name, data); + r = write_drop_in(dir, u->id, 50, name, data); + if (r < 0) + return r; + + r = drop_in_file(dir, u->id, 50, name, &p, &q); + if (r < 0) + return r; + + r = strv_extend(&u->dropin_paths, q); + if (r < 0) + return r; + + strv_sort(u->dropin_paths); + strv_uniq(u->dropin_paths); + + u->dropin_mtime = now(CLOCK_REALTIME); + + return 0; } int unit_write_drop_in_format(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *format, ...) { @@ -3347,7 +3464,7 @@ int unit_kill_context( _cleanup_free_ char *comm = NULL; get_process_comm(main_pid, &comm); - log_warning_unit(u->id, "Failed to kill main process " PID_FMT " (%s): %s", main_pid, strna(comm), strerror(-r)); + log_unit_warning_errno(u->id, r, "Failed to kill main process " PID_FMT " (%s): %m", main_pid, strna(comm)); } else { if (!main_pid_alien) wait_for_exit = true; @@ -3364,7 +3481,7 @@ int unit_kill_context( _cleanup_free_ char *comm = NULL; get_process_comm(control_pid, &comm); - log_warning_unit(u->id, "Failed to kill control process " PID_FMT " (%s): %s", control_pid, strna(comm), strerror(-r)); + log_unit_warning_errno(u->id, r, "Failed to kill control process " PID_FMT " (%s): %m", control_pid, strna(comm)); } else { wait_for_exit = true; @@ -3384,7 +3501,7 @@ int unit_kill_context( r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, sig, true, true, false, pid_set); if (r < 0) { if (r != -EAGAIN && r != -ESRCH && r != -ENOENT) - log_warning_unit(u->id, "Failed to kill control group: %s", strerror(-r)); + log_unit_warning_errno(u->id, r, "Failed to kill control group: %m"); } else if (r > 0) { /* FIXME: For now, we will not wait for the diff --git a/src/core/unit.h b/src/core/unit.h index 081ab18f10..0e45229d7b 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -129,8 +129,10 @@ struct Unit { /* Conditions to check */ LIST_HEAD(Condition, conditions); + LIST_HEAD(Condition, asserts); dual_timestamp condition_timestamp; + dual_timestamp assert_timestamp; dual_timestamp inactive_exit_timestamp; dual_timestamp active_enter_timestamp; @@ -177,8 +179,9 @@ struct Unit { /* Error code when we didn't manage to load the unit (negative) */ int load_error; - /* Cached unit file state */ + /* Cached unit file state and preset */ UnitFileState unit_file_state; + int unit_file_preset; /* Counterparts in the cgroup filesystem */ char *cgroup_path; @@ -212,6 +215,7 @@ struct Unit { /* Did the last condition check succeed? */ bool condition_result; + bool assert_result; /* Is this a transient unit? */ bool transient; @@ -557,6 +561,7 @@ void unit_start_on_failure(Unit *u); void unit_trigger_notify(Unit *u); UnitFileState unit_get_unit_file_state(Unit *u); +int unit_get_unit_file_preset(Unit *u); Unit* unit_ref_set(UnitRef *ref, Unit *u); void unit_ref_unset(UnitRef *ref); @@ -593,11 +598,20 @@ UnitActiveState unit_active_state_from_string(const char *s) _pure_; /* Macros which append UNIT= or USER_UNIT= to the message */ -#define log_full_unit(level, unit, ...) log_meta_object(level, __FILE__, __LINE__, __func__, getpid() == 1 ? "UNIT=" : "USER_UNIT=", unit, __VA_ARGS__) -#define log_debug_unit(unit, ...) log_full_unit(LOG_DEBUG, unit, __VA_ARGS__) -#define log_info_unit(unit, ...) log_full_unit(LOG_INFO, unit, __VA_ARGS__) -#define log_notice_unit(unit, ...) log_full_unit(LOG_NOTICE, unit, __VA_ARGS__) -#define log_warning_unit(unit, ...) log_full_unit(LOG_WARNING, unit, __VA_ARGS__) -#define log_error_unit(unit, ...) log_full_unit(LOG_ERR, unit, __VA_ARGS__) +#define log_unit_full_errno(unit, level, error, ...) log_object_internal(level, error, __FILE__, __LINE__, __func__, getpid() == 1 ? "UNIT=" : "USER_UNIT=", unit, __VA_ARGS__) +#define log_unit_full(unit, level, ...) log_unit_full_errno(unit, level, 0, __VA_ARGS__) -#define log_struct_unit(level, unit, ...) log_struct(level, getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s", unit, __VA_ARGS__) +#define log_unit_debug(unit, ...) log_unit_full(unit, LOG_DEBUG, __VA_ARGS__) +#define log_unit_info(unit, ...) log_unit_full(unit, LOG_INFO, __VA_ARGS__) +#define log_unit_notice(unit, ...) log_unit_full(unit, LOG_NOTICE, __VA_ARGS__) +#define log_unit_warning(unit, ...) log_unit_full(unit, LOG_WARNING, __VA_ARGS__) +#define log_unit_error(unit, ...) log_unit_full(unit, LOG_ERR, __VA_ARGS__) + +#define log_unit_debug_errno(unit, error, ...) log_unit_full_errno(unit, LOG_DEBUG, error, __VA_ARGS__) +#define log_unit_info_errno(unit, error, ...) log_unit_full_errno(unit, LOG_INFO, error, __VA_ARGS__) +#define log_unit_notice_errno(unit, error, ...) log_unit_full_errno(unit, LOG_NOTICE, error, __VA_ARGS__) +#define log_unit_warning_errno(unit, error, ...) log_unit_full_errno(unit, LOG_WARNING, error, __VA_ARGS__) +#define log_unit_error_errno(unit, error, ...) log_unit_full_errno(unit, LOG_ERR, error, __VA_ARGS__) + +#define log_unit_struct(unit, level, ...) log_struct(level, getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s", unit, __VA_ARGS__) +#define log_unit_struct_errno(unit, level, error, ...) log_struct_errno(level, error, getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s", unit, __VA_ARGS__) diff --git a/src/core/user.conf b/src/core/user.conf index 8c7ecde727..87c8164378 100644 --- a/src/core/user.conf +++ b/src/core/user.conf @@ -5,6 +5,9 @@ # the Free Software Foundation; either version 2.1 of the License, or # (at your option) any later version. # +# You can override the directives in this file by creating files in +# /etc/systemd/user.conf.d/*.conf. +# # See systemd-user.conf(5) for details [Manager] diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c index 05ceff443f..3a866f36fc 100644 --- a/src/cryptsetup/cryptsetup-generator.c +++ b/src/cryptsetup/cryptsetup-generator.c @@ -19,26 +19,36 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <string.h> #include <errno.h> +#include <string.h> #include <unistd.h> +#include "dropin.h" +#include "fileio.h" +#include "generator.h" +#include "hashmap.h" #include "log.h" -#include "util.h" -#include "unit-name.h" #include "mkdir.h" -#include "strv.h" -#include "fileio.h" #include "path-util.h" -#include "dropin.h" -#include "generator.h" +#include "strv.h" +#include "unit-name.h" +#include "util.h" + +typedef struct crypto_device { + char *uuid; + char *keyfile; + char *name; + char *options; + bool create; +} crypto_device; static const char *arg_dest = "/tmp"; static bool arg_enabled = true; static bool arg_read_crypttab = true; -static char **arg_disks = NULL; -static char **arg_options = NULL; -static char *arg_keyfile = NULL; +static bool arg_whitelist = false; +static Hashmap *arg_disks = NULL; +static char *arg_default_options = NULL; +static char *arg_default_keyfile = NULL; static bool has_option(const char *haystack, const char *needle) { const char *f = haystack; @@ -116,10 +126,8 @@ static int create_disk( return log_oom(); f = fopen(p, "wxe"); - if (!f) { - log_error("Failed to create unit file %s: %m", p); - return -errno; - } + if (!f) + return log_error_errno(errno, "Failed to create unit file %s: %m", p); fputs( "# Automatically generated by systemd-cryptsetup-generator\n\n" @@ -200,10 +208,8 @@ static int create_disk( name); fflush(f); - if (ferror(f)) { - log_error("Failed to write file %s: %m", p); - return -errno; - } + if (ferror(f)) + return log_error_errno(errno, "Failed to write file %s: %m", p); from = strappenda("../", n); @@ -214,10 +220,8 @@ static int create_disk( return log_oom(); mkdir_parents_label(to, 0755); - if (symlink(from, to) < 0) { - log_error("Failed to create symlink %s: %m", to); - return -errno; - } + if (symlink(from, to) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", to); free(to); if (!nofail) @@ -228,10 +232,8 @@ static int create_disk( return log_oom(); mkdir_parents_label(to, 0755); - if (symlink(from, to) < 0) { - log_error("Failed to create symlink %s: %m", to); - return -errno; - } + if (symlink(from, to) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", to); } free(to); @@ -240,10 +242,8 @@ static int create_disk( return log_oom(); mkdir_parents_label(to, 0755); - if (symlink(from, to) < 0) { - log_error("Failed to create symlink %s: %m", to); - return -errno; - } + if (symlink(from, to) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", to); if (!noauto && !nofail) { _cleanup_free_ char *dmname; @@ -254,17 +254,63 @@ static int create_disk( r = write_drop_in(arg_dest, dmname, 90, "device-timeout", "# Automatically generated by systemd-cryptsetup-generator \n\n" "[Unit]\nJobTimeoutSec=0"); + if (r < 0) + return log_error_errno(r, "Failed to write device drop-in: %m"); + } + + return 0; +} + +static void free_arg_disks(void) { + crypto_device *d; + + while ((d = hashmap_steal_first(arg_disks))) { + free(d->uuid); + free(d->keyfile); + free(d->name); + free(d->options); + free(d); + } + + hashmap_free(arg_disks); +} + +static crypto_device *get_crypto_device(const char *uuid) { + int r; + crypto_device *d; + + assert(uuid); + + d = hashmap_get(arg_disks, uuid); + if (!d) { + d = new0(struct crypto_device, 1); + if (!d) + return NULL; + + d->create = false; + d->keyfile = d->options = d->name = NULL; + + d->uuid = strdup(uuid); + if (!d->uuid) { + free(d); + return NULL; + } + + r = hashmap_put(arg_disks, d->uuid, d); if (r < 0) { - log_error("Failed to write device drop-in: %s", strerror(-r)); - return r; + free(d->uuid); + free(d); + return NULL; } } - return 0; + return d; } static int parse_proc_cmdline_item(const char *key, const char *value) { int r; + crypto_device *d; + _cleanup_free_ char *uuid = NULL, *uuid_value = NULL; if (STR_IN_SET(key, "luks", "rd.luks") && value) { @@ -284,232 +330,214 @@ static int parse_proc_cmdline_item(const char *key, const char *value) { } else if (STR_IN_SET(key, "luks.uuid", "rd.luks.uuid") && value) { - if (strv_extend(&arg_disks, value) < 0) + d = get_crypto_device(startswith(value, "luks-") ? value+5 : value); + if (!d) return log_oom(); + d->create = arg_whitelist = true; + } else if (STR_IN_SET(key, "luks.options", "rd.luks.options") && value) { - if (strv_extend(&arg_options, value) < 0) + r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value); + if (r == 2) { + d = get_crypto_device(uuid); + if (!d) + return log_oom(); + + free(d->options); + d->options = uuid_value; + uuid_value = NULL; + } else if (free_and_strdup(&arg_default_options, value) < 0) return log_oom(); } else if (STR_IN_SET(key, "luks.key", "rd.luks.key") && value) { - free(arg_keyfile); - arg_keyfile = strdup(value); - if (!arg_keyfile) + r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value); + if (r == 2) { + d = get_crypto_device(uuid); + if (!d) + return log_oom(); + + free(d->keyfile); + d->keyfile = uuid_value; + uuid_value = NULL; + } else if (free_and_strdup(&arg_default_keyfile, value)) return log_oom(); + } else if (STR_IN_SET(key, "luks.name", "rd.luks.name") && value) { + + r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value); + if (r == 2) { + d = get_crypto_device(uuid); + if (!d) + return log_oom(); + + d->create = arg_whitelist = true; + + free(d->name); + d->name = uuid_value; + uuid_value = NULL; + } else + log_warning("Failed to parse luks name switch %s. Ignoring.", value); + } return 0; } -int main(int argc, char *argv[]) { - _cleanup_strv_free_ char **disks_done = NULL; +static int add_crypttab_devices(void) { + struct stat st; + unsigned crypttab_line = 0; _cleanup_fclose_ FILE *f = NULL; - unsigned n = 0; - int r = EXIT_FAILURE, r2 = EXIT_FAILURE; - char **i; - if (argc > 1 && argc != 4) { - log_error("This program takes three or no arguments."); - return EXIT_FAILURE; + if (!arg_read_crypttab) + return 0; + + f = fopen("/etc/crypttab", "re"); + if (!f) { + if (errno != ENOENT) + log_error_errno(errno, "Failed to open /etc/crypttab: %m"); + return 0; } - if (argc > 1) - arg_dest = argv[1]; + if (fstat(fileno(f), &st) < 0) { + log_error_errno(errno, "Failed to stat /etc/crypttab: %m"); + return 0; + } - log_set_target(LOG_TARGET_SAFE); - log_parse_environment(); - log_open(); + /* If we readd support for specifying passphrases + * directly in crypttab we should upgrade the warning + * below, though possibly only if a passphrase is + * specified directly. */ + if (st.st_mode & 0005) + log_debug("/etc/crypttab is world-readable. This is usually not a good idea."); - umask(0022); + for (;;) { + int r, k; + char line[LINE_MAX], *l, *uuid; + crypto_device *d = NULL; + _cleanup_free_ char *name = NULL, *device = NULL, *keyfile = NULL, *options = NULL; - if (parse_proc_cmdline(parse_proc_cmdline_item) < 0) - goto cleanup; + if (!fgets(line, sizeof(line), f)) + break; - if (!arg_enabled) { - r = r2 = EXIT_SUCCESS; - goto cleanup; - } + crypttab_line++; - strv_uniq(arg_disks); + l = strstrip(line); + if (*l == '#' || *l == 0) + continue; - if (arg_read_crypttab) { - struct stat st; + k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &keyfile, &options); + if (k < 2 || k > 4) { + log_error("Failed to parse /etc/crypttab:%u, ignoring.", crypttab_line); + continue; + } - f = fopen("/etc/crypttab", "re"); - if (!f) { - if (errno == ENOENT) - r = EXIT_SUCCESS; - else - log_error("Failed to open /etc/crypttab: %m"); + uuid = startswith(device, "UUID="); + if (!uuid) + uuid = path_startswith(device, "/dev/disk/by-uuid/"); + if (!uuid) + uuid = startswith(name, "luks-"); + if (uuid) + d = hashmap_get(arg_disks, uuid); - goto next; + if (arg_whitelist && !d) { + log_info("Not creating device '%s' because it was not specified on the kernel command line.", name); + continue; } - if (fstat(fileno(f), &st) < 0) { - log_error("Failed to stat /etc/crypttab: %m"); - goto next; - } + r = create_disk(name, device, keyfile, (d && d->options) ? d->options : options); + if (r < 0) + return r; - /* If we readd support for specifying passphrases - * directly in crypttabe we should upgrade the warning - * below, though possibly only if a passphrase is - * specified directly. */ - if (st.st_mode & 0005) - log_debug("/etc/crypttab is world-readable. This is usually not a good idea."); + if (d) + d->create = false; + } - for (;;) { - char line[LINE_MAX], *l; - _cleanup_free_ char *name = NULL, *device = NULL, *password = NULL, *options = NULL; - int k; + return 0; +} - if (!fgets(line, sizeof(line), f)) - break; +static int add_proc_cmdline_devices(void) { + int r; + Iterator i; + crypto_device *d; - n++; + HASHMAP_FOREACH(d, arg_disks, i) { + const char *options; + _cleanup_free_ char *device = NULL; - l = strstrip(line); - if (*l == '#' || *l == 0) - continue; + if (!d->create) + continue; - k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &password, &options); - if (k < 2 || k > 4) { - log_error("Failed to parse /etc/crypttab:%u, ignoring.", n); - continue; - } + if (!d->name) { + d->name = strappend("luks-", d->uuid); + if (!d->name) + return log_oom(); + } - /* - If options are specified on the kernel commandline, let them override - the ones from crypttab. - */ - STRV_FOREACH(i, arg_options) { - _cleanup_free_ char *proc_uuid = NULL, *proc_options = NULL; - const char *p = *i; - - k = sscanf(p, "%m[0-9a-fA-F-]=%ms", &proc_uuid, &proc_options); - if (k == 2 && streq(proc_uuid, device + 5)) { - free(options); - options = strdup(p); - if (!options) { - log_oom(); - goto cleanup; - } - } - } + device = strappend("UUID=", d->uuid); + if (!device) + return log_oom(); - if (arg_disks) { - /* - If luks UUIDs are specified on the kernel command line, use them as a filter - for /etc/crypttab and only generate units for those. - */ - STRV_FOREACH(i, arg_disks) { - _cleanup_free_ char *proc_device = NULL, *proc_name = NULL; - const char *p = *i; - - if (startswith(p, "luks-")) - p += 5; - - proc_name = strappend("luks-", p); - proc_device = strappend("UUID=", p); - - if (!proc_name || !proc_device) { - log_oom(); - goto cleanup; - } - - if (streq(proc_device, device) || streq(proc_name, name)) { - if (create_disk(name, device, password, options) < 0) - goto cleanup; - - if (strv_extend(&disks_done, p) < 0) { - log_oom(); - goto cleanup; - } - } - } - } else if (create_disk(name, device, password, options) < 0) - goto cleanup; + if (d->options) + options = d->options; + else if (arg_default_options) + options = arg_default_options; + else + options = "timeout=0"; - } + r = create_disk(d->name, device, d->keyfile ?: arg_default_keyfile, options); + if (r < 0) + return r; } - r = EXIT_SUCCESS; - -next: - STRV_FOREACH(i, arg_disks) { - /* - Generate units for those UUIDs, which were specified - on the kernel command line and not yet written. - */ + return 0; +} - _cleanup_free_ char *name = NULL, *device = NULL, *options = NULL; - const char *p = *i; +int main(int argc, char *argv[]) { + int r = EXIT_FAILURE; - if (startswith(p, "luks-")) - p += 5; + if (argc > 1 && argc != 4) { + log_error("This program takes three or no arguments."); + return EXIT_FAILURE; + } - if (strv_contains(disks_done, p)) - continue; + if (argc > 1) + arg_dest = argv[1]; - name = strappend("luks-", p); - device = strappend("UUID=", p); + log_set_target(LOG_TARGET_SAFE); + log_parse_environment(); + log_open(); - if (!name || !device) { - log_oom(); - goto cleanup; - } + umask(0022); - if (arg_options) { - /* - If options are specified on the kernel commandline, use them. - */ - char **j; - - STRV_FOREACH(j, arg_options) { - _cleanup_free_ char *proc_uuid = NULL, *proc_options = NULL; - const char *s = *j; - int k; - - k = sscanf(s, "%m[0-9a-fA-F-]=%ms", &proc_uuid, &proc_options); - if (k == 2) { - if (streq(proc_uuid, device + 5)) { - free(options); - options = proc_options; - proc_options = NULL; - } - } else if (!options) { - /* - Fall back to options without a specified UUID - */ - options = strdup(s); - if (!options) { - log_oom(); - goto cleanup; - }; - } - } - } + arg_disks = hashmap_new(&string_hash_ops); + if (!arg_disks) + goto cleanup; - if (!options) { - options = strdup("timeout=0"); - if (!options) { - log_oom(); - goto cleanup; - } - } + r = parse_proc_cmdline(parse_proc_cmdline_item); + if (r < 0) { + log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); + r = EXIT_FAILURE; + } - if (create_disk(name, device, arg_keyfile, options) < 0) - goto cleanup; + if (!arg_enabled) { + r = EXIT_SUCCESS; + goto cleanup; } - r2 = EXIT_SUCCESS; + if (add_crypttab_devices() < 0) + goto cleanup; + + if (add_proc_cmdline_devices() < 0) + goto cleanup; + + r = EXIT_SUCCESS; cleanup: - strv_free(arg_disks); - strv_free(arg_options); - free(arg_keyfile); + free_arg_disks(); + free(arg_default_options); + free(arg_default_keyfile); - return r != EXIT_SUCCESS ? r : r2; + return r; } diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index 94570eb82d..21b1260f1b 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -280,10 +280,8 @@ static int get_password(const char *name, usec_t until, bool accept_cached, char id = strappenda("cryptsetup:", escaped_name); r = ask_password_auto(text, "drive-harddisk", id, until, accept_cached, passwords); - if (r < 0) { - log_error("Failed to query password: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to query password: %m"); if (arg_verify) { _cleanup_strv_free_ char **passwords2 = NULL; @@ -296,10 +294,8 @@ static int get_password(const char *name, usec_t until, bool accept_cached, char id = strappenda("cryptsetup-verification:", escaped_name); r = ask_password_auto(text, "drive-harddisk", id, until, false, &passwords2); - if (r < 0) { - log_error("Failed to query verification password: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to query verification password: %m"); assert(strv_length(passwords2) == 1); @@ -355,7 +351,7 @@ static int attach_tcrypt(struct crypt_device *cd, if (key_file) { r = read_one_line_file(key_file, &passphrase); if (r < 0) { - log_error("Failed to read password file '%s': %s", key_file, strerror(-r)); + log_error_errno(r, "Failed to read password file '%s': %m", key_file); return -EAGAIN; } @@ -400,7 +396,9 @@ static int attach_luks_or_plain(struct crypt_device *cd, /* plain isn't a real hash type. it just means "use no hash" */ if (!streq(arg_hash, "plain")) params.hash = arg_hash; - } else + } else if (!key_file) + /* for CRYPT_PLAIN, the behaviour of cryptsetup + * package is to not hash when a key file is provided */ params.hash = "ripemd160"; if (arg_cipher) { @@ -436,10 +434,8 @@ static int attach_luks_or_plain(struct crypt_device *cd, pass_volume_key = (params.hash == NULL); } - if (r < 0) { - log_error("Loading of cryptographic parameters failed: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Loading of cryptographic parameters failed: %m"); log_info("Set cipher %s, mode %s, key size %i bits for device %s.", crypt_get_cipher(cd), @@ -452,7 +448,7 @@ static int attach_luks_or_plain(struct crypt_device *cd, key_file, arg_keyfile_size, arg_keyfile_offset, flags); if (r < 0) { - log_error("Failed to activate with key file '%s': %s", key_file, strerror(-r)); + log_error_errno(r, "Failed to activate with key file '%s': %m", key_file); return -EAGAIN; } } else { @@ -565,7 +561,7 @@ int main(int argc, char *argv[]) { k = crypt_init(&cd, argv[3]); if (k) { - log_error("crypt_init() failed: %s", strerror(-k)); + log_error_errno(k, "crypt_init() failed: %m"); goto finish; } @@ -621,7 +617,7 @@ int main(int argc, char *argv[]) { key_file = NULL; continue; } else if (k != -EPERM) { - log_error("Failed to activate: %s", strerror(-k)); + log_error_errno(k, "Failed to activate: %m"); goto finish; } @@ -639,7 +635,7 @@ int main(int argc, char *argv[]) { k = crypt_init_by_name(&cd, argv[2]); if (k) { - log_error("crypt_init() failed: %s", strerror(-k)); + log_error_errno(k, "crypt_init() failed: %m"); goto finish; } @@ -647,7 +643,7 @@ int main(int argc, char *argv[]) { k = crypt_deactivate(cd, argv[2]); if (k < 0) { - log_error("Failed to deactivate: %s", strerror(-k)); + log_error_errno(k, "Failed to deactivate: %m"); goto finish; } diff --git a/src/dbus1-generator/dbus1-generator.c b/src/dbus1-generator/dbus1-generator.c index 3c4522b589..10a33efee5 100644 --- a/src/dbus1-generator/dbus1-generator.c +++ b/src/dbus1-generator/dbus1-generator.c @@ -58,10 +58,8 @@ static int create_dbus_files( return log_oom(); f = fopen(a, "wxe"); - if (!f) { - log_error("Failed to create %s: %m", a); - return -errno; - } + if (!f) + return log_error_errno(errno, "Failed to create %s: %m", a); fprintf(f, "# Automatically generated by systemd-dbus1-generator\n\n" @@ -86,7 +84,7 @@ static int create_dbus_files( fprintf(f, "Environment=DBUS_STARTER_BUS_TYPE=%s\n", type); if (streq(type, "system")) - fprintf(f, "Environment=DBUS_STARTER_ADDRESS=" DEFAULT_SYSTEM_BUS_PATH "\n"); + fprintf(f, "Environment=DBUS_STARTER_ADDRESS=" DEFAULT_SYSTEM_BUS_ADDRESS "\n"); else if (streq(type, "session")) { char *run; @@ -96,16 +94,14 @@ static int create_dbus_files( return -EINVAL; } - fprintf(f, "Environment=DBUS_STARTER_ADDRESS="KERNEL_USER_BUS_FMT ";" UNIX_USER_BUS_FMT "\n", + fprintf(f, "Environment=DBUS_STARTER_ADDRESS="KERNEL_USER_BUS_ADDRESS_FMT ";" UNIX_USER_BUS_ADDRESS_FMT "\n", getuid(), run); } } r = fflush_and_check(f); - if (r < 0) { - log_error("Failed to write %s: %s", a, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to write %s: %m", a); fclose(f); f = NULL; @@ -118,10 +114,8 @@ static int create_dbus_files( return log_oom(); f = fopen(b, "wxe"); - if (!f) { - log_error("Failed to create %s: %m", b); - return -errno; - } + if (!f) + return log_error_errno(errno, "Failed to create %s: %m", b); fprintf(f, "# Automatically generated by systemd-dbus1-generator\n\n" @@ -139,20 +133,16 @@ static int create_dbus_files( service); r = fflush_and_check(f); - if (r < 0) { - log_error("Failed to write %s: %s", b, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to write %s: %m", b); lnk = strjoin(arg_dest_late, "/" SPECIAL_BUSNAMES_TARGET ".wants/", name, ".busname", NULL); if (!lnk) return log_oom(); mkdir_parents_label(lnk, 0755); - if (symlink(b, lnk)) { - log_error("Failed to create symlink %s: %m", lnk); - return -errno; - } + if (symlink(b, lnk)) + return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); return 0; } @@ -233,7 +223,7 @@ static int parse_dbus_fragments(const char *path, const char *type) { if (errno == -ENOENT) return 0; - log_error("Failed to enumerate D-Bus activated services: %m"); + log_error_errno(errno, "Failed to enumerate D-Bus activated services: %m"); return -errno; } @@ -252,7 +242,7 @@ static int parse_dbus_fragments(const char *path, const char *type) { return r; fail: - log_error("Failed to read D-Bus services directory: %m"); + log_error_errno(errno, "Failed to read D-Bus services directory: %m"); return -errno; } @@ -263,10 +253,8 @@ static int link_busnames_target(const char *units) { t = strappenda(arg_dest, "/" SPECIAL_BASIC_TARGET ".wants/" SPECIAL_BUSNAMES_TARGET); mkdir_parents_label(t, 0755); - if (symlink(f, t) < 0) { - log_error("Failed to create symlink %s: %m", t); - return -errno; - } + if (symlink(f, t) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", t); return 0; } @@ -277,24 +265,18 @@ static int link_compatibility(const char *units) { f = strappenda(units, "/systemd-bus-proxyd.socket"); t = strappenda(arg_dest, "/" SPECIAL_DBUS_SOCKET); mkdir_parents_label(t, 0755); - if (symlink(f, t) < 0) { - log_error("Failed to create symlink %s: %m", t); - return -errno; - } + if (symlink(f, t) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", t); f = strappenda(units, "/systemd-bus-proxyd.socket"); t = strappenda(arg_dest, "/" SPECIAL_SOCKETS_TARGET ".wants/systemd-bus-proxyd.socket"); mkdir_parents_label(t, 0755); - if (symlink(f, t) < 0) { - log_error("Failed to create symlink %s: %m", t); - return -errno; - } + if (symlink(f, t) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", t); t = strappenda(arg_dest, "/" SPECIAL_DBUS_SERVICE); - if (symlink("/dev/null", t) < 0) { - log_error("Failed to mask %s: %m", t); - return -errno; - } + if (symlink("/dev/null", t) < 0) + return log_error_errno(errno, "Failed to mask %s: %m", t); return 0; } @@ -319,7 +301,7 @@ int main(int argc, char *argv[]) { umask(0022); - if (access("/dev/kdbus/control", F_OK) < 0) + if (access("/sys/fs/kdbus/control", F_OK) < 0) return 0; r = cg_pid_get_owner_uid(0, NULL); @@ -331,10 +313,8 @@ int main(int argc, char *argv[]) { path = "/usr/share/dbus-1/system-services"; type = "system"; units = SYSTEM_DATA_UNIT_PATH; - } else { - log_error("Failed to determine whether we are running as user or system instance: %s", strerror(-r)); - return r; - } + } else + return log_error_errno(r, "Failed to determine whether we are running as user or system instance: %m"); r = parse_dbus_fragments(path, type); diff --git a/src/debug-generator/debug-generator.c b/src/debug-generator/debug-generator.c index fd7c29d28f..1b9019325c 100644 --- a/src/debug-generator/debug-generator.c +++ b/src/debug-generator/debug-generator.c @@ -96,7 +96,7 @@ static int generate_mask_symlinks(void) { return log_oom(); if (symlink("/dev/null", p) < 0) { - log_error("Failed to create mask symlink %s: %m", p); + log_error_errno(errno, "Failed to create mask symlink %s: %m", p); r = -errno; } } @@ -125,7 +125,7 @@ static int generate_wants_symlinks(void) { mkdir_parents_label(p, 0755); if (symlink(f, p) < 0) { - log_error("Failed to create wants symlink %s: %m", p); + log_error_errno(errno, "Failed to create wants symlink %s: %m", p); r = -errno; } } @@ -150,8 +150,9 @@ int main(int argc, char *argv[]) { umask(0022); - if (parse_proc_cmdline(parse_proc_cmdline_item) < 0) - return EXIT_FAILURE; + r = parse_proc_cmdline(parse_proc_cmdline_item); + if (r < 0) + log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); if (arg_debug_shell) { r = strv_extend(&arg_wants, "debug-shell.service"); diff --git a/src/delta/delta.c b/src/delta/delta.c index 25c4a0bcfc..a3a121fb0c 100644 --- a/src/delta/delta.c +++ b/src/delta/delta.c @@ -185,16 +185,15 @@ static int found_override(const char *top, const char *bottom) { fflush(stdout); pid = fork(); - if (pid < 0) { - log_error("Failed to fork off diff: %m"); - return -errno; - } else if (pid == 0) { + if (pid < 0) + return log_error_errno(errno, "Failed to fork off diff: %m"); + else if (pid == 0) { execlp("diff", "diff", "-us", "--", bottom, top, NULL); - log_error("Failed to execute diff: %m"); + log_error_errno(errno, "Failed to execute diff: %m"); _exit(1); } - wait_for_terminate_and_warn("diff", pid); + wait_for_terminate_and_warn("diff", pid, false); putchar('\n'); return k; @@ -226,10 +225,8 @@ static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const *c = 0; r = get_files_in_directory(path, &list); - if (r < 0){ - log_error("Failed to enumerate %s: %s", path, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to enumerate %s: %m", path); STRV_FOREACH(file, list) { Hashmap *h; @@ -307,7 +304,7 @@ static int enumerate_dir(Hashmap *top, Hashmap *bottom, Hashmap *drops, const ch if (errno == ENOENT) return 0; - log_error("Failed to open %s: %m", path); + log_error_errno(errno, "Failed to open %s: %m", path); return -errno; } @@ -487,7 +484,7 @@ static int parse_flags(const char *flag_str, int flags) { const char *word, *state; size_t l; - FOREACH_WORD(word, l, flag_str, state) { + FOREACH_WORD_SEPARATOR(word, l, flag_str, ",", state) { if (strneq("masked", word, l)) flags |= SHOW_MASKED; else if (strneq ("equivalent", word, l)) diff --git a/src/detect-virt/detect-virt.c b/src/detect-virt/detect-virt.c index ff5bee56f9..9a924117db 100644 --- a/src/detect-virt/detect-virt.c +++ b/src/detect-virt/detect-virt.c @@ -131,7 +131,7 @@ int main(int argc, char *argv[]) { v = detect_virtualization(&id); if (v < 0) { - log_error("Failed to check for virtualization: %s", strerror(-v)); + log_error_errno(v, "Failed to check for virtualization: %m"); return EXIT_FAILURE; } @@ -142,7 +142,7 @@ int main(int argc, char *argv[]) { case ONLY_CONTAINER: r = detect_container(&id); if (r < 0) { - log_error("Failed to check for container: %s", strerror(-r)); + log_error_errno(r, "Failed to check for container: %m"); return EXIT_FAILURE; } @@ -152,7 +152,7 @@ int main(int argc, char *argv[]) { case ONLY_VM: r = detect_vm(&id); if (r < 0) { - log_error("Failed to check for vm: %s", strerror(-r)); + log_error_errno(r, "Failed to check for vm: %m"); return EXIT_FAILURE; } diff --git a/src/efi-boot-generator/efi-boot-generator.c b/src/efi-boot-generator/efi-boot-generator.c index d4d778036f..99a819fe37 100644 --- a/src/efi-boot-generator/efi-boot-generator.c +++ b/src/efi-boot-generator/efi-boot-generator.c @@ -80,14 +80,14 @@ int main(int argc, char *argv[]) { log_debug("EFI loader partition unknown, exiting."); return EXIT_SUCCESS; } else if (r < 0) { - log_error("Failed to read ESP partition UUID: %s", strerror(-r)); + log_error_errno(r, "Failed to read ESP partition UUID: %m"); return EXIT_FAILURE; } name = strappenda(arg_dest, "/boot.mount"); f = fopen(name, "wxe"); if (!f) { - log_error("Failed to create mount unit file %s: %m", name); + log_error_errno(errno, "Failed to create mount unit file %s: %m", name); return EXIT_FAILURE; } @@ -120,7 +120,7 @@ int main(int argc, char *argv[]) { fflush(f); if (ferror(f)) { - log_error("Failed to write mount unit file: %m"); + log_error_errno(errno, "Failed to write mount unit file: %m"); return EXIT_FAILURE; } @@ -128,7 +128,7 @@ int main(int argc, char *argv[]) { fclose(f); f = fopen(name, "wxe"); if (!f) { - log_error("Failed to create automount unit file %s: %m", name); + log_error_errno(errno, "Failed to create automount unit file %s: %m", name); return EXIT_FAILURE; } @@ -140,7 +140,7 @@ int main(int argc, char *argv[]) { fflush(f); if (ferror(f)) { - log_error("Failed to write automount unit file: %m"); + log_error_errno(errno, "Failed to write automount unit file: %m"); return EXIT_FAILURE; } @@ -148,7 +148,7 @@ int main(int argc, char *argv[]) { mkdir_parents(name, 0755); if (symlink("../boot.automount", name) < 0) { - log_error("Failed to create symlink %s: %m", name); + log_error_errno(errno, "Failed to create symlink %s: %m", name); return EXIT_FAILURE; } diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index 93273d903c..d087ef35e9 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -98,7 +98,7 @@ static void print_welcome(void) { } if (r < 0 && r != -ENOENT) - log_warning("Failed to read os-release file: %s", strerror(-r)); + log_warning_errno(r, "Failed to read os-release file: %m"); printf("\nWelcome to your new installation of %s!\nPlease configure a few basic system settings:\n\n", isempty(pretty_name) ? "Linux" : pretty_name); @@ -166,10 +166,8 @@ static int prompt_loop(const char *text, char **l, bool (*is_valid)(const char * unsigned u; r = ask_string(&p, "%s %s (empty to skip): ", draw_special_char(DRAW_TRIANGULAR_BULLET), text); - if (r < 0) { - log_error("Failed to query user: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to query user: %m"); if (isempty(p)) { log_warning("No data entered, skipping."); @@ -219,10 +217,8 @@ static int prompt_locale(void) { return 0; r = get_locales(&locales); - if (r < 0) { - log_error("Cannot query locales list: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Cannot query locales list: %m"); print_welcome(); @@ -262,10 +258,8 @@ static int process_locale(void) { mkdir_parents(etc_localeconf, 0755); r = copy_file("/etc/locale.conf", etc_localeconf, 0, 0644); if (r != -ENOENT) { - if (r < 0) { - log_error("Failed to copy %s: %s", etc_localeconf, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to copy %s: %m", etc_localeconf); log_info("%s copied.", etc_localeconf); return 0; @@ -288,10 +282,8 @@ static int process_locale(void) { mkdir_parents(etc_localeconf, 0755); r = write_env_file(etc_localeconf, locales); - if (r < 0) { - log_error("Failed to write %s: %s", etc_localeconf, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to write %s: %m", etc_localeconf); log_info("%s written.", etc_localeconf); return 0; @@ -308,10 +300,8 @@ static int prompt_timezone(void) { return 0; r = get_timezones(&zones); - if (r < 0) { - log_error("Cannot query timezone list: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Cannot query timezone list: %m"); print_welcome(); @@ -342,16 +332,12 @@ static int process_timezone(void) { r = readlink_malloc("/etc/localtime", &p); if (r != -ENOENT) { - if (r < 0) { - log_error("Failed to read host timezone: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to read host timezone: %m"); mkdir_parents(etc_localtime, 0755); - if (symlink(p, etc_localtime) < 0) { - log_error("Failed to create %s symlink: %m", etc_localtime); - return -errno; - } + if (symlink(p, etc_localtime) < 0) + return log_error_errno(errno, "Failed to create %s symlink: %m", etc_localtime); log_info("%s copied.", etc_localtime); return 0; @@ -368,10 +354,8 @@ static int process_timezone(void) { e = strappenda("../usr/share/zoneinfo/", arg_timezone); mkdir_parents(etc_localtime, 0755); - if (symlink(e, etc_localtime) < 0) { - log_error("Failed to create %s symlink: %m", etc_localtime); - return -errno; - } + if (symlink(e, etc_localtime) < 0) + return log_error_errno(errno, "Failed to create %s symlink: %m", etc_localtime); log_info("%s written", etc_localtime); return 0; @@ -393,10 +377,8 @@ static int prompt_hostname(void) { _cleanup_free_ char *h = NULL; r = ask_string(&h, "%s Please enter hostname for new system (empty to skip): ", draw_special_char(DRAW_TRIANGULAR_BULLET)); - if (r < 0) { - log_error("Failed to query hostname: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to query hostname: %m"); if (isempty(h)) { log_warning("No hostname entered, skipping."); @@ -433,10 +415,8 @@ static int process_hostname(void) { mkdir_parents(etc_hostname, 0755); r = write_string_file(etc_hostname, arg_hostname); - if (r < 0) { - log_error("Failed to write %s: %s", etc_hostname, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to write %s: %m", etc_hostname); log_info("%s written.", etc_hostname); return 0; @@ -456,10 +436,8 @@ static int process_machine_id(void) { mkdir_parents(etc_machine_id, 0755); r = write_string_file(etc_machine_id, sd_id128_to_string(arg_machine_id, id)); - if (r < 0) { - log_error("Failed to write machine id: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to write machine id: %m"); log_info("%s written.", etc_machine_id); return 0; @@ -489,10 +467,8 @@ static int prompt_root_password(void) { _cleanup_free_ char *a = NULL, *b = NULL; r = ask_password_tty(msg1, 0, false, NULL, &a); - if (r < 0) { - log_error("Failed to query root password: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to query root password: %m"); if (isempty(a)) { log_warning("No password entered, skipping."); @@ -501,7 +477,7 @@ static int prompt_root_password(void) { r = ask_password_tty(msg2, 0, false, NULL, &b); if (r < 0) { - log_error("Failed to query root password: %s", strerror(-r)); + log_error_errno(r, "Failed to query root password: %m"); clear_string(a); return r; } @@ -586,15 +562,13 @@ static int process_root_password(void) { if (!errno) errno = EIO; - log_error("Failed to find shadow entry for root: %m"); + log_error_errno(errno, "Failed to find shadow entry for root: %m"); return -errno; } r = write_root_shadow(etc_shadow, p); - if (r < 0) { - log_error("Failed to write %s: %s", etc_shadow, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to write %s: %m", etc_shadow); log_info("%s copied.", etc_shadow); return 0; @@ -609,10 +583,8 @@ static int process_root_password(void) { return 0; r = dev_urandom(raw, 16); - if (r < 0) { - log_error("Failed to get salt: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get salt: %m"); /* We only bother with SHA512 hashed passwords, the rest is legacy, and we don't do legacy. */ assert_cc(sizeof(table) == 64 + 1); @@ -628,17 +600,15 @@ static int process_root_password(void) { if (!errno) errno = -EINVAL; - log_error("Failed to encrypt password: %m"); + log_error_errno(errno, "Failed to encrypt password: %m"); return -errno; } item.sp_lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY); r = write_root_shadow(etc_shadow, &item); - if (r < 0) { - log_error("Failed to write %s: %s", etc_shadow, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to write %s: %m", etc_shadow); log_info("%s written.", etc_shadow); return 0; @@ -803,10 +773,8 @@ static int parse_argv(int argc, char *argv[]) { arg_root_password = NULL; r = read_one_line_file(optarg, &arg_root_password); - if (r < 0) { - log_error("Failed to read %s: %s", optarg, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to read %s: %m", optarg); break; @@ -870,10 +838,8 @@ static int parse_argv(int argc, char *argv[]) { case ARG_SETUP_MACHINE_ID: r = sd_id128_randomize(&arg_machine_id); - if (r < 0) { - log_error("Failed to generate randomized machine ID: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to generate randomized machine ID: %m"); break; diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c index 70a591883e..20b7940948 100644 --- a/src/fsck/fsck.c +++ b/src/fsck/fsck.c @@ -35,7 +35,7 @@ #include "special.h" #include "bus-util.h" #include "bus-error.h" -#include "bus-errors.h" +#include "bus-common-errors.h" #include "fileio.h" #include "udev-util.h" #include "path-util.h" @@ -54,7 +54,7 @@ static void start_target(const char *target) { r = bus_open_system_systemd(&bus); if (r < 0) { - log_error("Failed to get D-Bus connection: %s", strerror(-r)); + log_error_errno(r, "Failed to get D-Bus connection: %m"); return; } @@ -236,7 +236,10 @@ int main(int argc, char *argv[]) { umask(0022); - parse_proc_cmdline(parse_proc_cmdline_item); + q = parse_proc_cmdline(parse_proc_cmdline_item); + if (q < 0) + log_warning_errno(q, "Failed to parse kernel command line, ignoring: %m"); + test_files(); if (!arg_force && arg_skip) @@ -253,7 +256,7 @@ int main(int argc, char *argv[]) { root_directory = false; if (stat(device, &st) < 0) { - log_error("Failed to stat '%s': %m", device); + log_error_errno(errno, "Failed to stat '%s': %m", device); return EXIT_FAILURE; } @@ -268,7 +271,7 @@ int main(int argc, char *argv[]) { /* Find root device */ if (stat("/", &st) < 0) { - log_error("Failed to stat() the root directory: %m"); + log_error_errno(errno, "Failed to stat() the root directory: %m"); return EXIT_FAILURE; } @@ -306,12 +309,12 @@ int main(int argc, char *argv[]) { log_info("fsck.%s doesn't exist, not checking file system on %s", type, device); return EXIT_SUCCESS; } else if (r < 0) - log_warning("fsck.%s cannot be used for %s: %s", type, device, strerror(-r)); + log_warning_errno(r, "fsck.%s cannot be used for %s: %m", type, device); } if (arg_show_progress) if (pipe(progress_pipe) < 0) { - log_error("pipe(): %m"); + log_error_errno(errno, "pipe(): %m"); return EXIT_FAILURE; } @@ -343,7 +346,7 @@ int main(int argc, char *argv[]) { pid = fork(); if (pid < 0) { - log_error("fork(): %m"); + log_error_errno(errno, "fork(): %m"); goto finish; } else if (pid == 0) { /* Child */ @@ -362,7 +365,7 @@ int main(int argc, char *argv[]) { q = wait_for_terminate(pid, &status); if (q < 0) { - log_error("waitid(): %s", strerror(-q)); + log_error_errno(q, "waitid(): %m"); goto finish; } diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index e257c121e5..1f34594928 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -74,11 +74,14 @@ static int mount_find_pri(struct mntent *me, int *ret) { return 1; } -static int add_swap(const char *what, struct mntent *me) { +static int add_swap( + const char *what, + struct mntent *me, + bool noauto, + bool nofail) { + _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL; _cleanup_fclose_ FILE *f = NULL; - - bool noauto; int r, pri = -1; assert(what); @@ -95,8 +98,6 @@ static int add_swap(const char *what, struct mntent *me) { return r; } - noauto = !!hasmntopt(me, "noauto"); - name = unit_name_from_path(what, ".swap"); if (!name) return log_oom(); @@ -110,7 +111,7 @@ static int add_swap(const char *what, struct mntent *me) { if (errno == EEXIST) log_error("Failed to create swap unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit); else - log_error("Failed to create unit file %s: %m", unit); + log_error_errno(errno, "Failed to create unit file %s: %m", unit); return -errno; } @@ -132,10 +133,8 @@ static int add_swap(const char *what, struct mntent *me) { fprintf(f, "Options=%s\n", me->mnt_opts); r = fflush_and_check(f); - if (r < 0) { - log_error("Failed to write unit file %s: %s", unit, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to write unit file %s: %m", unit); /* use what as where, to have a nicer error message */ r = generator_write_timeouts(arg_dest, what, what, me->mnt_opts, NULL); @@ -143,15 +142,14 @@ static int add_swap(const char *what, struct mntent *me) { return r; if (!noauto) { - lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL); + lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET, + nofail ? ".wants/" : ".requires/", name, NULL); if (!lnk) return log_oom(); mkdir_parents_label(lnk, 0755); - if (symlink(unit, lnk) < 0) { - log_error("Failed to create symlink %s: %m", lnk); - return -errno; - } + if (symlink(unit, lnk) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); } return 0; @@ -229,7 +227,7 @@ static int add_mount( if (errno == EEXIST) log_error("Failed to create mount unit file %s, as it already exists. Duplicate entry in /etc/fstab?", unit); else - log_error("Failed to create unit file %s: %m", unit); + log_error_errno(errno, "Failed to create unit file %s: %m", unit); return -errno; } @@ -268,10 +266,8 @@ static int add_mount( fprintf(f, "Options=%s\n", filtered); fflush(f); - if (ferror(f)) { - log_error("Failed to write unit file %s: %m", unit); - return -errno; - } + if (ferror(f)) + return log_error_errno(errno, "Failed to write unit file %s: %m", unit); if (!noauto && post) { lnk = strjoin(arg_dest, "/", post, nofail || automount ? ".wants/" : ".requires/", name, NULL); @@ -279,10 +275,8 @@ static int add_mount( return log_oom(); mkdir_parents_label(lnk, 0755); - if (symlink(unit, lnk) < 0) { - log_error("Failed to create symlink %s: %m", lnk); - return -errno; - } + if (symlink(unit, lnk) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); } if (automount) { @@ -296,10 +290,8 @@ static int add_mount( fclose(f); f = fopen(automount_unit, "wxe"); - if (!f) { - log_error("Failed to create unit file %s: %m", automount_unit); - return -errno; - } + if (!f) + return log_error_errno(errno, "Failed to create unit file %s: %m", automount_unit); fprintf(f, "# Automatically generated by systemd-fstab-generator\n\n" @@ -319,10 +311,8 @@ static int add_mount( where); fflush(f); - if (ferror(f)) { - log_error("Failed to write unit file %s: %m", automount_unit); - return -errno; - } + if (ferror(f)) + return log_error_errno(errno, "Failed to write unit file %s: %m", automount_unit); free(lnk); lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL); @@ -330,10 +320,8 @@ static int add_mount( return log_oom(); mkdir_parents_label(lnk, 0755); - if (symlink(automount_unit, lnk) < 0) { - log_error("Failed to create symlink %s: %m", lnk); - return -errno; - } + if (symlink(automount_unit, lnk) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); } return 0; @@ -351,12 +339,13 @@ static int parse_fstab(bool initrd) { if (errno == ENOENT) return 0; - log_error("Failed to open %s: %m", fstab_path); + log_error_errno(errno, "Failed to open %s: %m", fstab_path); return -errno; } while ((me = getmntent(f))) { _cleanup_free_ char *where = NULL, *what = NULL; + bool noauto, nofail; int k; if (initrd && !mount_in_initrd(me)) @@ -378,16 +367,18 @@ static int parse_fstab(bool initrd) { if (is_path(where)) path_kill_slashes(where); - log_debug("Found entry what=%s where=%s type=%s", what, where, me->mnt_type); + noauto = !!hasmntopt(me, "noauto"); + nofail = !!hasmntopt(me, "nofail"); + log_debug("Found entry what=%s where=%s type=%s nofail=%s noauto=%s", + what, where, me->mnt_type, + yes_no(noauto), yes_no(nofail)); if (streq(me->mnt_type, "swap")) - k = add_swap(what, me); + k = add_swap(what, me, noauto, nofail); else { - bool noauto, nofail, automount; + bool automount; const char *post; - noauto = !!hasmntopt(me, "noauto"); - nofail = !!hasmntopt(me, "nofail"); automount = hasmntopt(me, "comment=systemd.automount") || hasmntopt(me, "x-systemd.automount"); @@ -425,7 +416,7 @@ static int add_root_mount(void) { const char *opts; if (isempty(arg_root_what)) { - log_debug("Could not find a root= entry on the kernel commandline."); + log_debug("Could not find a root= entry on the kernel command line."); return 0; } @@ -485,7 +476,7 @@ static int add_usr_mount(void) { return log_oom(); } - if (!arg_usr_what || !arg_usr_options) + if (!arg_usr_what) return 0; what = fstab_node_to_udev_node(arg_usr_what); @@ -494,7 +485,13 @@ static int add_usr_mount(void) { return -1; } - opts = arg_usr_options; + if (!arg_usr_options) + opts = arg_root_rw > 0 ? "rw" : "ro"; + else if (!mount_test_option(arg_usr_options, "ro") && + !mount_test_option(arg_usr_options, "rw")) + opts = strappenda(arg_usr_options, ",", arg_root_rw > 0 ? "rw" : "ro"); + else + opts = arg_usr_options; log_debug("Found entry what=%s where=/sysroot/usr type=%s", what, strna(arg_usr_fstype)); return add_mount(what, @@ -593,8 +590,9 @@ int main(int argc, char *argv[]) { umask(0022); - if (parse_proc_cmdline(parse_proc_cmdline_item) < 0) - return EXIT_FAILURE; + r = parse_proc_cmdline(parse_proc_cmdline_item); + if (r < 0) + log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); /* Always honour root= and usr= in the kernel command line if we are in an initrd */ if (in_initrd()) { @@ -625,6 +623,12 @@ int main(int argc, char *argv[]) { } free(arg_root_what); + free(arg_root_fstype); + free(arg_root_options); + + free(arg_usr_what); + free(arg_usr_fstype); + free(arg_usr_options); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/getty-generator/getty-generator.c b/src/getty-generator/getty-generator.c index 06ca9b9662..931651e161 100644 --- a/src/getty-generator/getty-generator.c +++ b/src/getty-generator/getty-generator.c @@ -52,7 +52,7 @@ static int add_symlink(const char *fservice, const char *tservice) { /* In case console=hvc0 is passed this will very likely result in EEXIST */ return 0; else { - log_error("Failed to create symlink %s: %m", to); + log_error_errno(errno, "Failed to create symlink %s: %m", to); return -errno; } } diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index 539e2e64b5..32e9b78a1d 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -68,10 +68,8 @@ static int add_swap(const char *path) { return log_oom(); f = fopen(unit, "wxe"); - if (!f) { - log_error("Failed to create unit file %s: %m", unit); - return -errno; - } + if (!f) + return log_error_errno(errno, "Failed to create unit file %s: %m", unit); fprintf(f, "# Automatically generated by systemd-gpt-auto-generator\n\n" @@ -83,20 +81,16 @@ static int add_swap(const char *path) { path); fflush(f); - if (ferror(f)) { - log_error("Failed to write unit file %s: %m", unit); - return -errno; - } + if (ferror(f)) + return log_error_errno(errno, "Failed to write unit file %s: %m", unit); lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL); if (!lnk) return log_oom(); mkdir_parents_label(lnk, 0755); - if (symlink(unit, lnk) < 0) { - log_error("Failed to create symlink %s: %m", lnk); - return -errno; - } + if (symlink(unit, lnk) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); return 0; } @@ -128,10 +122,8 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, char **devi return log_oom(); f = fopen(p, "wxe"); - if (!f) { - log_error("Failed to create unit file %s: %m", p); - return -errno; - } + if (!f) + return log_error_errno(errno, "Failed to create unit file %s: %m", p); fprintf(f, "# Automatically generated by systemd-gpt-auto-generator\n\n" @@ -155,10 +147,8 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, char **devi id); fflush(f); - if (ferror(f)) { - log_error("Failed to write file %s: %m", p); - return -errno; - } + if (ferror(f)) + return log_error_errno(errno, "Failed to write file %s: %m", p); from = strappenda("../", n); @@ -167,10 +157,8 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, char **devi return log_oom(); mkdir_parents_label(to, 0755); - if (symlink(from, to) < 0) { - log_error("Failed to create symlink %s: %m", to); - return -errno; - } + if (symlink(from, to) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", to); free(to); to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL); @@ -178,10 +166,8 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, char **devi return log_oom(); mkdir_parents_label(to, 0755); - if (symlink(from, to) < 0) { - log_error("Failed to create symlink %s: %m", to); - return -errno; - } + if (symlink(from, to) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", to); free(to); to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL); @@ -189,10 +175,8 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, char **devi return log_oom(); mkdir_parents_label(to, 0755); - if (symlink(from, to) < 0) { - log_error("Failed to create symlink %s: %m", to); - return -errno; - } + if (symlink(from, to) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", to); free(p); p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf", NULL); @@ -204,10 +188,8 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, char **devi "# Automatically generated by systemd-gpt-auto-generator\n\n" "[Unit]\n" "JobTimeoutSec=0\n"); /* the binary handles timeouts anyway */ - if (r < 0) { - log_error("Failed to write device drop-in: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to write device drop-in: %m"); ret = strappend("/dev/mapper/", id); if (!ret) @@ -256,10 +238,8 @@ static int add_mount( return log_oom(); f = fopen(p, "wxe"); - if (!f) { - log_error("Failed to create unit file %s: %m", unit); - return -errno; - } + if (!f) + return log_error_errno(errno, "Failed to create unit file %s: %m", unit); fprintf(f, "# Automatically generated by systemd-gpt-auto-generator\n\n" @@ -288,10 +268,8 @@ static int add_mount( fprintf(f, "Options=%s\n", rw ? "rw" : "ro"); fflush(f); - if (ferror(f)) { - log_error("Failed to write unit file %s: %m", p); - return -errno; - } + if (ferror(f)) + return log_error_errno(errno, "Failed to write unit file %s: %m", p); if (post) { lnk = strjoin(arg_dest, "/", post, ".requires/", unit, NULL); @@ -299,10 +277,8 @@ static int add_mount( return log_oom(); mkdir_parents_label(lnk, 0755); - if (symlink(p, lnk) < 0) { - log_error("Failed to create symlink %s: %m", lnk); - return -errno; - } + if (symlink(p, lnk) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); } return 0; @@ -339,7 +315,7 @@ static int probe_and_add_mount( if (!b) { if (errno == 0) return log_oom(); - log_error("Failed to allocate prober: %m"); + log_error_errno(errno, "Failed to allocate prober: %m"); return -errno; } @@ -353,7 +329,7 @@ static int probe_and_add_mount( else if (r != 0) { if (errno == 0) errno = EIO; - log_error("Failed to probe %s: %m", what); + log_error_errno(errno, "Failed to probe %s: %m", what); return -errno; } @@ -431,7 +407,7 @@ static int enumerate_partitions(dev_t devnum) { if (errno == 0) return log_oom(); - log_error("Failed allocate prober: %m"); + log_error_errno(errno, "Failed allocate prober: %m"); return -errno; } @@ -445,7 +421,7 @@ static int enumerate_partitions(dev_t devnum) { else if (r != 0) { if (errno == 0) errno = EIO; - log_error("Failed to probe %s: %m", node); + log_error_errno(errno, "Failed to probe %s: %m", node); return -errno; } @@ -454,7 +430,7 @@ static int enumerate_partitions(dev_t devnum) { if (r != 0) { if (errno == 0) errno = EIO; - log_error("Failed to determine partition table type of %s: %m", node); + log_error_errno(errno, "Failed to determine partition table type of %s: %m", node); return -errno; } @@ -470,7 +446,7 @@ static int enumerate_partitions(dev_t devnum) { if (errno == 0) return log_oom(); - log_error("Failed to list partitions of %s: %m", node); + log_error_errno(errno, "Failed to list partitions of %s: %m", node); return -errno; } @@ -487,10 +463,8 @@ static int enumerate_partitions(dev_t devnum) { return log_oom(); r = udev_enumerate_scan_devices(e); - if (r < 0) { - log_error("Failed to enumerate partitions on %s: %s", node, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to enumerate partitions on %s: %m", node); first = udev_enumerate_get_list_entry(e); udev_list_entry_foreach(item, first) { @@ -680,8 +654,8 @@ static int parse_proc_cmdline_item(const char *key, const char *value) { r = parse_boolean(value); if (r < 0) log_warning("Failed to parse gpt-auto switch %s. Ignoring.", value); - - arg_enabled = r; + else + arg_enabled = r; } else if (streq(key, "root") && value) { @@ -712,10 +686,8 @@ static int add_root_mount(void) { if (r == -ENOENT) { log_debug("EFI loader partition unknown, exiting."); return 0; - } else if (r < 0) { - log_error("Failed to read ESP partition UUID: %s", strerror(-r)); - return r; - } + } else if (r < 0) + return log_error_errno(r, "Failed to read ESP partition UUID: %m"); /* OK, we have an ESP partition, this is fantastic, so let's * wait for a root device to show up. A udev rule will create @@ -739,10 +711,9 @@ static int add_mounts(void) { int r; r = get_block_device("/", &devno); - if (r < 0) { - log_error("Failed to determine block device of root file system: %s", strerror(-r)); - return r; - } else if (r == 0) { + if (r < 0) + return log_error_errno(r, "Failed to determine block device of root file system: %m"); + else if (r == 0) { log_debug("Root file system not on a (single) block device."); return 0; } @@ -772,8 +743,9 @@ int main(int argc, char *argv[]) { return EXIT_SUCCESS; } - if (parse_proc_cmdline(parse_proc_cmdline_item) < 0) - return EXIT_FAILURE; + r = parse_proc_cmdline(parse_proc_cmdline_item); + if (r < 0) + log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); if (!arg_enabled) { log_debug("Disabled, exiting."); diff --git a/src/hibernate-resume/hibernate-resume-generator.c b/src/hibernate-resume/hibernate-resume-generator.c index f40721662e..0207346b28 100644 --- a/src/hibernate-resume/hibernate-resume-generator.c +++ b/src/hibernate-resume/hibernate-resume-generator.c @@ -45,6 +45,9 @@ static int parse_proc_cmdline_item(const char *key, const char *value) { static int process_resume(void) { _cleanup_free_ char *name = NULL, *lnk = NULL; + if (!arg_resume_dev) + return 0; + name = unit_name_from_path_instance("systemd-hibernate-resume", arg_resume_dev, ".service"); if (!name) return log_oom(); @@ -54,10 +57,8 @@ static int process_resume(void) { return log_oom(); mkdir_parents_label(lnk, 0755); - if (symlink(SYSTEM_DATA_UNIT_PATH "/systemd-hibernate-resume@.service", lnk) < 0) { - log_error("Failed to create symlink %s: %m", lnk); - return -errno; - } + if (symlink(SYSTEM_DATA_UNIT_PATH "/systemd-hibernate-resume@.service", lnk) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); return 0; } @@ -83,12 +84,11 @@ int main(int argc, char *argv[]) { if (!in_initrd()) return EXIT_SUCCESS; - if (parse_proc_cmdline(parse_proc_cmdline_item) < 0) - return EXIT_FAILURE; - - if (arg_resume_dev != NULL) - r = process_resume(); + r = parse_proc_cmdline(parse_proc_cmdline_item); + if (r < 0) + log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); + r = process_resume(); free(arg_resume_dev); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; diff --git a/src/hibernate-resume/hibernate-resume.c b/src/hibernate-resume/hibernate-resume.c index b234a0b49a..f28eabbe37 100644 --- a/src/hibernate-resume/hibernate-resume.c +++ b/src/hibernate-resume/hibernate-resume.c @@ -53,7 +53,7 @@ int main(int argc, char *argv[]) { device = argv[1]; if (stat(device, &st) < 0) { - log_error("Failed to stat '%s': %m", device); + log_error_errno(errno, "Failed to stat '%s': %m", device); return EXIT_FAILURE; } @@ -69,7 +69,7 @@ int main(int argc, char *argv[]) { r = write_string_file("/sys/power/resume", major_minor); if (r < 0) { - log_error("Failed to write '%s' to /sys/power/resume: %s", major_minor, strerror(-r)); + log_error_errno(r, "Failed to write '%s' to /sys/power/resume: %m", major_minor); return EXIT_FAILURE; } diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c index e48736920f..b3ce8510ba 100644 --- a/src/hostname/hostnamectl.c +++ b/src/hostname/hostnamectl.c @@ -529,12 +529,12 @@ int main(int argc, char *argv[]) { r = bus_open_transport(arg_transport, arg_host, false, &bus); if (r < 0) { - log_error("Failed to create bus connection: %s", strerror(-r)); + log_error_errno(r, "Failed to create bus connection: %m"); goto finish; } r = hostnamectl_main(bus, argc, argv); finish: - return r < 0 ? EXIT_FAILURE : r; + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index a449610bb8..0b38cde169 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -138,7 +138,8 @@ static bool valid_chassis(const char *chassis) { "server\0" "tablet\0" "handset\0" - "watch\0", + "watch\0" + "embedded\0", chassis); } @@ -441,7 +442,7 @@ static int method_set_hostname(sd_bus *bus, sd_bus_message *m, void *userdata, s r = context_update_kernel_hostname(c); if (r < 0) { - log_error("Failed to set host name: %s", strerror(-r)); + log_error_errno(r, "Failed to set host name: %m"); return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %s", strerror(-r)); } @@ -493,13 +494,13 @@ static int method_set_static_hostname(sd_bus *bus, sd_bus_message *m, void *user r = context_update_kernel_hostname(c); if (r < 0) { - log_error("Failed to set host name: %s", strerror(-r)); + log_error_errno(r, "Failed to set host name: %m"); return sd_bus_error_set_errnof(error, r, "Failed to set hostname: %s", strerror(-r)); } r = context_write_data_static_hostname(c); if (r < 0) { - log_error("Failed to write static host name: %s", strerror(-r)); + log_error_errno(r, "Failed to write static host name: %m"); return sd_bus_error_set_errnof(error, r, "Failed to set static hostname: %s", strerror(-r)); } @@ -572,7 +573,7 @@ static int set_machine_info(Context *c, sd_bus *bus, sd_bus_message *m, int prop r = context_write_data_machine_info(c); if (r < 0) { - log_error("Failed to write machine info: %s", strerror(-r)); + log_error_errno(r, "Failed to write machine info: %m"); return sd_bus_error_set_errnof(error, r, "Failed to write machine info: %s", strerror(-r)); } @@ -644,28 +645,20 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { assert(_bus); r = sd_bus_default_system(&bus); - if (r < 0) { - log_error("Failed to get system bus connection: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get system bus connection: %m"); r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/hostname1", "org.freedesktop.hostname1", hostname_vtable, c); - if (r < 0) { - log_error("Failed to register object: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to register object: %m"); r = sd_bus_request_name(bus, "org.freedesktop.hostname1", 0); - if (r < 0) { - log_error("Failed to register name: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to register name: %m"); r = sd_bus_attach_event(bus, event, 0); - if (r < 0) { - log_error("Failed to attach bus to event loop: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to attach bus to event loop: %m"); *_bus = bus; bus = NULL; @@ -700,7 +693,7 @@ int main(int argc, char *argv[]) { r = sd_event_default(&event); if (r < 0) { - log_error("Failed to allocate event loop: %s", strerror(-r)); + log_error_errno(r, "Failed to allocate event loop: %m"); goto finish; } @@ -712,13 +705,13 @@ int main(int argc, char *argv[]) { r = context_read_data(&context); if (r < 0) { - log_error("Failed to read hostname and machine information: %s", strerror(-r)); + log_error_errno(r, "Failed to read hostname and machine information: %m"); goto finish; } r = bus_event_loop_with_idle(event, bus, "org.freedesktop.hostname1", DEFAULT_EXIT_USEC, NULL, NULL); if (r < 0) { - log_error("Failed to run event loop: %s", strerror(-r)); + log_error_errno(r, "Failed to run event loop: %m"); goto finish; } diff --git a/src/initctl/initctl.c b/src/initctl/initctl.c index f1c2b8dfb4..d7cd4ba29f 100644 --- a/src/initctl/initctl.c +++ b/src/initctl/initctl.c @@ -162,7 +162,7 @@ static void request_process(Server *s, const struct init_request *req) { case 'u': case 'U': if (kill(1, SIGTERM) < 0) - log_error("kill() failed: %m"); + log_error_errno(errno, "kill() failed: %m"); /* The bus connection will be * terminated if PID 1 is reexecuted, @@ -175,7 +175,7 @@ static void request_process(Server *s, const struct init_request *req) { case 'q': case 'Q': if (kill(1, SIGHUP) < 0) - log_error("kill() failed: %m"); + log_error_errno(errno, "kill() failed: %m"); break; default: @@ -217,7 +217,7 @@ static int fifo_process(Fifo *f) { if (errno == EAGAIN) return 0; - log_warning("Failed to read from fifo: %m"); + log_warning_errno(errno, "Failed to read from fifo: %m"); return -errno; } @@ -277,7 +277,7 @@ static int server_init(Server *s, unsigned n_sockets) { s->epoll_fd = epoll_create1(EPOLL_CLOEXEC); if (s->epoll_fd < 0) { r = -errno; - log_error("Failed to create epoll object: %m"); + log_error_errno(errno, "Failed to create epoll object: %m"); goto fail; } @@ -290,8 +290,7 @@ static int server_init(Server *s, unsigned n_sockets) { r = sd_is_fifo(fd, NULL); if (r < 0) { - log_error("Failed to determine file descriptor type: %s", - strerror(-r)); + log_error_errno(r, "Failed to determine file descriptor type: %m"); goto fail; } @@ -304,7 +303,7 @@ static int server_init(Server *s, unsigned n_sockets) { f = new0(Fifo, 1); if (!f) { r = -ENOMEM; - log_error("Failed to create fifo object: %m"); + log_error_errno(errno, "Failed to create fifo object: %m"); goto fail; } @@ -316,7 +315,7 @@ static int server_init(Server *s, unsigned n_sockets) { if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) { r = -errno; fifo_free(f); - log_error("Failed to add fifo fd to epoll object: %m"); + log_error_errno(errno, "Failed to add fifo fd to epoll object: %m"); goto fail; } @@ -328,7 +327,7 @@ static int server_init(Server *s, unsigned n_sockets) { r = bus_open_system_systemd(&s->bus); if (r < 0) { - log_error("Failed to get D-Bus connection: %s", strerror(-r)); + log_error_errno(r, "Failed to get D-Bus connection: %m"); r = -EIO; goto fail; } @@ -355,7 +354,7 @@ static int process_event(Server *s, struct epoll_event *ev) { f = (Fifo*) ev->data.ptr; r = fifo_process(f); if (r < 0) { - log_info("Got error on fifo: %s", strerror(-r)); + log_info_errno(r, "Got error on fifo: %m"); fifo_free(f); return r; } @@ -385,7 +384,7 @@ int main(int argc, char *argv[]) { n = sd_listen_fds(true); if (n < 0) { - log_error("Failed to read listening file descriptors from environment: %s", strerror(-r)); + log_error_errno(r, "Failed to read listening file descriptors from environment: %m"); return EXIT_FAILURE; } @@ -414,7 +413,7 @@ int main(int argc, char *argv[]) { if (errno == EINTR) continue; - log_error("epoll_wait() failed: %m"); + log_error_errno(errno, "epoll_wait() failed: %m"); goto fail; } diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c index db81fe3ca3..7a99430a63 100644 --- a/src/journal-remote/journal-gatewayd.c +++ b/src/journal-remote/journal-gatewayd.c @@ -155,14 +155,14 @@ static ssize_t request_reader_entries( r = sd_journal_next(m->journal); if (r < 0) { - log_error("Failed to advance journal pointer: %s", strerror(-r)); + log_error_errno(r, "Failed to advance journal pointer: %m"); return MHD_CONTENT_READER_END_WITH_ERROR; } else if (r == 0) { if (m->follow) { r = sd_journal_wait(m->journal, (uint64_t) -1); if (r < 0) { - log_error("Couldn't wait for journal event: %s", strerror(-r)); + log_error_errno(r, "Couldn't wait for journal event: %m"); return MHD_CONTENT_READER_END_WITH_ERROR; } @@ -177,7 +177,7 @@ static ssize_t request_reader_entries( r = sd_journal_test_cursor(m->journal, m->cursor); if (r < 0) { - log_error("Failed to test cursor: %s", strerror(-r)); + log_error_errno(r, "Failed to test cursor: %m"); return MHD_CONTENT_READER_END_WITH_ERROR; } @@ -198,20 +198,20 @@ static ssize_t request_reader_entries( else { m->tmp = tmpfile(); if (!m->tmp) { - log_error("Failed to create temporary file: %m"); + log_error_errno(errno, "Failed to create temporary file: %m"); return MHD_CONTENT_READER_END_WITH_ERROR; } } r = output_journal(m->tmp, m->journal, m->mode, 0, OUTPUT_FULL_WIDTH, NULL); if (r < 0) { - log_error("Failed to serialize item: %s", strerror(-r)); + log_error_errno(r, "Failed to serialize item: %m"); return MHD_CONTENT_READER_END_WITH_ERROR; } sz = ftello(m->tmp); if (sz == (off_t) -1) { - log_error("Failed to retrieve file position: %m"); + log_error_errno(errno, "Failed to retrieve file position: %m"); return MHD_CONTENT_READER_END_WITH_ERROR; } @@ -219,7 +219,7 @@ static ssize_t request_reader_entries( } if (fseeko(m->tmp, pos, SEEK_SET) < 0) { - log_error("Failed to seek to position: %m"); + log_error_errno(errno, "Failed to seek to position: %m"); return MHD_CONTENT_READER_END_WITH_ERROR; } @@ -394,7 +394,7 @@ static int request_parse_arguments_iterator( r = sd_id128_get_boot(&bid); if (r < 0) { - log_error("Failed to get boot ID: %s", strerror(-r)); + log_error_errno(r, "Failed to get boot ID: %m"); return MHD_NO; } @@ -543,7 +543,7 @@ static ssize_t request_reader_fields( r = sd_journal_enumerate_unique(m->journal, &d, &l); if (r < 0) { - log_error("Failed to advance field index: %s", strerror(-r)); + log_error_errno(r, "Failed to advance field index: %m"); return MHD_CONTENT_READER_END_WITH_ERROR; } else if (r == 0) return MHD_CONTENT_READER_END_OF_STREAM; @@ -559,20 +559,20 @@ static ssize_t request_reader_fields( else { m->tmp = tmpfile(); if (!m->tmp) { - log_error("Failed to create temporary file: %m"); + log_error_errno(errno, "Failed to create temporary file: %m"); return MHD_CONTENT_READER_END_WITH_ERROR; } } r = output_field(m->tmp, m->mode, d, l); if (r < 0) { - log_error("Failed to serialize item: %s", strerror(-r)); + log_error_errno(r, "Failed to serialize item: %m"); return MHD_CONTENT_READER_END_WITH_ERROR; } sz = ftello(m->tmp); if (sz == (off_t) -1) { - log_error("Failed to retrieve file position: %m"); + log_error_errno(errno, "Failed to retrieve file position: %m"); return MHD_CONTENT_READER_END_WITH_ERROR; } @@ -580,7 +580,7 @@ static ssize_t request_reader_fields( } if (fseeko(m->tmp, pos, SEEK_SET) < 0) { - log_error("Failed to seek to position: %m"); + log_error_errno(errno, "Failed to seek to position: %m"); return MHD_CONTENT_READER_END_WITH_ERROR; } @@ -909,10 +909,8 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } r = read_full_file(optarg, &key_pem, NULL); - if (r < 0) { - log_error("Failed to read key file: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to read key file: %m"); assert(key_pem); break; @@ -922,10 +920,8 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } r = read_full_file(optarg, &cert_pem, NULL); - if (r < 0) { - log_error("Failed to read certificate file: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to read certificate file: %m"); assert(cert_pem); break; @@ -936,10 +932,8 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } r = read_full_file(optarg, &trust_pem, NULL); - if (r < 0) { - log_error("Failed to read CA certificate file: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to read CA certificate file: %m"); assert(trust_pem); break; #else @@ -992,7 +986,7 @@ int main(int argc, char *argv[]) { n = sd_listen_fds(1); if (n < 0) { - log_error("Failed to determine passed sockets: %s", strerror(-n)); + log_error_errno(n, "Failed to determine passed sockets: %m"); goto finish; } else if (n > 1) { log_error("Can't listen on more than one socket."); diff --git a/src/journal-remote/journal-remote-parse.c b/src/journal-remote/journal-remote-parse.c index 7dd8878ca6..76407f711b 100644 --- a/src/journal-remote/journal-remote-parse.c +++ b/src/journal-remote/journal-remote-parse.c @@ -125,7 +125,7 @@ static int get_line(RemoteSource *source, char **line, size_t *size) { source->size - source->filled); if (n < 0) { if (errno != EAGAIN && errno != EWOULDBLOCK) - log_error("read(%d, ..., %zd): %m", source->fd, + log_error_errno(errno, "read(%d, ..., %zd): %m", source->fd, source->size - source->filled); return -errno; } else if (n == 0) @@ -186,7 +186,7 @@ static int fill_fixed_size(RemoteSource *source, void **data, size_t size) { source->size - source->filled); if (n < 0) { if (errno != EAGAIN && errno != EWOULDBLOCK) - log_error("read(%d, ..., %zd): %m", source->fd, + log_error_errno(errno, "read(%d, ..., %zd): %m", source->fd, source->size - source->filled); return -errno; } else if (n == 0) @@ -451,8 +451,8 @@ int process_source(RemoteSource *source, bool compress, bool seal) { r = writer_write(source->writer, &source->iovw, &source->ts, compress, seal); if (r < 0) - log_error("Failed to write entry of %zu bytes: %s", - iovw_size(&source->iovw), strerror(-r)); + log_error_errno(r, "Failed to write entry of %zu bytes: %m", + iovw_size(&source->iovw)); else r = 1; diff --git a/src/journal-remote/journal-remote-write.c b/src/journal-remote/journal-remote-write.c index 0139f851a5..df30049397 100644 --- a/src/journal-remote/journal-remote-write.c +++ b/src/journal-remote/journal-remote-write.c @@ -59,11 +59,9 @@ static int do_rotate(JournalFile **f, bool compress, bool seal) { int r = journal_file_rotate(f, compress, seal); if (r < 0) { if (*f) - log_error("Failed to rotate %s: %s", (*f)->path, - strerror(-r)); + log_error_errno(r, "Failed to rotate %s: %m", (*f)->path); else - log_error("Failed to create rotated journal: %s", - strerror(-r)); + log_error_errno(r, "Failed to create rotated journal: %m"); } return r; @@ -153,7 +151,7 @@ int writer_write(Writer *w, return 1; } - log_debug("%s: Write failed, rotating: %s", w->journal->path, strerror(-r)); + log_debug_errno(r, "%s: Write failed, rotating: %m", w->journal->path); r = do_rotate(&w->journal, compress, seal); if (r < 0) return r; diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c index dc7120bbd5..6ec5ad2e77 100644 --- a/src/journal-remote/journal-remote.c +++ b/src/journal-remote/journal-remote.c @@ -84,17 +84,15 @@ static int spawn_child(const char* child, char** argv) { pid_t parent_pid, child_pid; int r; - if (pipe(fd) < 0) { - log_error("Failed to create pager pipe: %m"); - return -errno; - } + if (pipe(fd) < 0) + return log_error_errno(errno, "Failed to create pager pipe: %m"); parent_pid = getpid(); child_pid = fork(); if (child_pid < 0) { r = -errno; - log_error("Failed to fork: %m"); + log_error_errno(errno, "Failed to fork: %m"); safe_close_pair(fd); return r; } @@ -103,7 +101,7 @@ static int spawn_child(const char* child, char** argv) { if (child_pid == 0) { r = dup2(fd[1], STDOUT_FILENO); if (r < 0) { - log_error("Failed to dup pipe to stdout: %m"); + log_error_errno(errno, "Failed to dup pipe to stdout: %m"); _exit(EXIT_FAILURE); } @@ -119,13 +117,13 @@ static int spawn_child(const char* child, char** argv) { _exit(EXIT_SUCCESS); execvp(child, argv); - log_error("Failed to exec child %s: %m", child); + log_error_errno(errno, "Failed to exec child %s: %m", child); _exit(EXIT_FAILURE); } r = close(fd[1]); if (r < 0) - log_warning("Failed to close write end of pipe: %m"); + log_warning_errno(errno, "Failed to close write end of pipe: %m"); return fd[0]; } @@ -140,7 +138,7 @@ static int spawn_curl(const char* url) { r = spawn_child("curl", argv); if (r < 0) - log_error("Failed to spawn curl: %m"); + log_error_errno(errno, "Failed to spawn curl: %m"); return r; } @@ -149,21 +147,17 @@ static int spawn_getter(const char *getter, const char *url) { _cleanup_strv_free_ char **words = NULL; assert(getter); - r = strv_split_quoted(&words, getter); - if (r < 0) { - log_error("Failed to split getter option: %s", strerror(-r)); - return r; - } + r = strv_split_quoted(&words, getter, false); + if (r < 0) + return log_error_errno(r, "Failed to split getter option: %m"); r = strv_extend(&words, url); - if (r < 0) { - log_error("Failed to create command line: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to create command line: %m"); r = spawn_child(words[0], words); if (r < 0) - log_error("Failed to spawn getter %s: %m", getter); + log_error_errno(errno, "Failed to spawn getter %s: %m", getter); return r; } @@ -210,8 +204,8 @@ static int open_output(Writer *w, const char* host) { w->mmap, NULL, &w->journal); if (r < 0) - log_error("Failed to open output journal %s: %s", - output, strerror(-r)); + log_error_errno(r, "Failed to open output journal %s: %m", + output); else log_info("Opened output file %s", w->journal->path); return r; @@ -320,11 +314,9 @@ static int get_source_for_fd(RemoteServer *s, return log_oom(); r = get_writer(s, name, &writer); - if (r < 0) { - log_warning("Failed to get writer for source %s: %s", - name, strerror(-r)); - return r; - } + if (r < 0) + return log_warning_errno(r, "Failed to get writer for source %s: %m", + name); if (s->sources[fd] == NULL) { s->sources[fd] = source_new(fd, false, name, writer); @@ -376,8 +368,8 @@ static int add_source(RemoteServer *s, int fd, char* name, bool own_name) { r = get_source_for_fd(s, fd, name, &source); if (r < 0) { - log_error("Failed to create source for fd:%d (%s): %s", - fd, name, strerror(-r)); + log_error_errno(r, "Failed to create source for fd:%d (%s): %m", + fd, name); free(name); return r; } @@ -393,14 +385,14 @@ static int add_source(RemoteServer *s, int fd, char* name, bool own_name) { sd_event_source_set_enabled(source->event, SD_EVENT_ON); } if (r < 0) { - log_error("Failed to register event source for fd:%d: %s", - fd, strerror(-r)); + log_error_errno(r, "Failed to register event source for fd:%d: %m", + fd); goto error; } - r = sd_event_source_set_name(source->event, name); + r = sd_event_source_set_description(source->event, name); if (r < 0) { - log_error("Failed to set source name for fd:%d: %s", fd, strerror(-r)); + log_error_errno(r, "Failed to set source name for fd:%d: %m", fd); goto error; } @@ -426,7 +418,7 @@ static int add_raw_socket(RemoteServer *s, int fd) { snprintf(name, sizeof(name), "raw-socket-%d", fd); - r = sd_event_source_set_name(s->listen_event, name); + r = sd_event_source_set_description(s->listen_event, name); if (r < 0) return r; @@ -459,11 +451,9 @@ static int request_meta(void **connection_cls, int fd, char *hostname) { return 0; r = get_writer(server, hostname, &writer); - if (r < 0) { - log_warning("Failed to get writer for source %s: %s", - hostname, strerror(-r)); - return r; - } + if (r < 0) + return log_warning_errno(r, "Failed to get writer for source %s: %m", + hostname); source = source_new(fd, true, hostname, writer); if (!source) { @@ -661,10 +651,8 @@ static int setup_microhttpd_server(RemoteServer *s, assert(fd >= 0); r = fd_nonblock(fd, true); - if (r < 0) { - log_error("Failed to make fd:%d nonblocking: %s", fd, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to make fd:%d nonblocking: %m", fd); if (key) { assert(cert); @@ -720,13 +708,13 @@ static int setup_microhttpd_server(RemoteServer *s, epoll_fd, EPOLLIN, dispatch_http_event, d); if (r < 0) { - log_error("Failed to add event callback: %s", strerror(-r)); + log_error_errno(r, "Failed to add event callback: %m"); goto error; } - r = sd_event_source_set_name(d->event, "epoll-fd"); + r = sd_event_source_set_description(d->event, "epoll-fd"); if (r < 0) { - log_error("Failed to set source name: %s", strerror(-r)); + log_error_errno(r, "Failed to set source name: %m"); goto error; } @@ -738,7 +726,7 @@ static int setup_microhttpd_server(RemoteServer *s, r = hashmap_put(s->daemons, &d->fd, d); if (r < 0) { - log_error("Failed to add daemon to hashmap: %s", strerror(-r)); + log_error_errno(r, "Failed to add daemon to hashmap: %m"); goto error; } @@ -803,18 +791,10 @@ static int setup_signals(RemoteServer *s) { if (r < 0) return r; - r = sd_event_source_set_name(s->sigterm_event, "sigterm"); - if (r < 0) - return r; - r = sd_event_add_signal(s->events, &s->sigint_event, SIGINT, NULL, s); if (r < 0) return r; - r = sd_event_source_set_name(s->sigint_event, "sigint"); - if (r < 0) - return r; - return 0; } @@ -848,10 +828,8 @@ static int remoteserver_init(RemoteServer *s, } r = sd_event_default(&s->events); - if (r < 0) { - log_error("Failed to allocate event loop: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to allocate event loop: %m"); setup_signals(s); @@ -863,11 +841,9 @@ static int remoteserver_init(RemoteServer *s, return r; n = sd_listen_fds(true); - if (n < 0) { - log_error("Failed to read listening file descriptors from environment: %s", - strerror(-n)); - return n; - } else + if (n < 0) + return log_error_errno(n, "Failed to read listening file descriptors from environment: %m"); + else log_info("Received %d descriptors", n); if (MAX(http_socket, https_socket) >= SD_LISTEN_FDS_START + n) { @@ -889,10 +865,8 @@ static int remoteserver_init(RemoteServer *s, char *hostname; r = getnameinfo_pretty(fd, &hostname); - if (r < 0) { - log_error("Failed to retrieve remote name: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to retrieve remote name: %m"); log_info("Received a connection socket (fd:%d) from %s", fd, hostname); @@ -903,11 +877,9 @@ static int remoteserver_init(RemoteServer *s, return -EINVAL; } - if(r < 0) { - log_error("Failed to register socket (fd:%d): %s", - fd, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to register socket (fd:%d): %m", + fd); } if (arg_url) { @@ -966,10 +938,8 @@ static int remoteserver_init(RemoteServer *s, log_info("Reading file %s...", *file); fd = open(*file, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); - if (fd < 0) { - log_error("Failed to open %s: %m", *file); - return -errno; - } + if (fd < 0) + return log_error_errno(errno, "Failed to open %s: %m", *file); output_name = *file; } @@ -1059,7 +1029,7 @@ static int dispatch_raw_source_event(sd_event_source *event, } else if (r == -EAGAIN) { return 0; } else if (r < 0) { - log_info("Closing connection: %s", strerror(-r)); + log_info_errno(r, "Closing connection: %m"); remove_source(server, fd); return 0; } else @@ -1079,10 +1049,8 @@ static int accept_connection(const char* type, int fd, log_debug("Accepting new %s connection on fd:%d", type, fd); fd2 = accept4(fd, &addr->sockaddr.sa, &addr->size, SOCK_NONBLOCK|SOCK_CLOEXEC); - if (fd2 < 0) { - log_error("accept() on fd:%d failed: %m", fd); - return -errno; - } + if (fd2 < 0) + return log_error_errno(errno, "accept() on fd:%d failed: %m", fd); switch(socket_address_family(addr)) { case AF_INET: @@ -1092,7 +1060,7 @@ static int accept_connection(const char* type, int fd, r = socket_address_print(addr, &a); if (r < 0) { - log_error("socket_address_print(): %s", strerror(-r)); + log_error_errno(r, "socket_address_print(): %m"); close(fd2); return r; } @@ -1163,10 +1131,10 @@ static int parse_config(void) { { "Remote", "TrustedCertificateFile", config_parse_path, 0, &arg_trust }, {}}; - return config_parse(NULL, PKGSYSCONFDIR "/journal-remote.conf", NULL, - "Remote\0", - config_item_table_lookup, items, - false, false, true, NULL); + return config_parse_many(PKGSYSCONFDIR "/journal-remote.conf", + CONF_DIRS_NULSTR("systemd/journal-remote.conf"), + "Remote\0", config_item_table_lookup, items, + false, NULL); } static void help(void) { @@ -1469,28 +1437,22 @@ static int load_certificates(char **key, char **cert, char **trust) { int r; r = read_full_file(arg_key ?: PRIV_KEY_FILE, key, NULL); - if (r < 0) { - log_error("Failed to read key from file '%s': %s", - arg_key ?: PRIV_KEY_FILE, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to read key from file '%s': %m", + arg_key ?: PRIV_KEY_FILE); r = read_full_file(arg_cert ?: CERT_FILE, cert, NULL); - if (r < 0) { - log_error("Failed to read certificate from file '%s': %s", - arg_cert ?: CERT_FILE, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to read certificate from file '%s': %m", + arg_cert ?: CERT_FILE); if (arg_trust_all) log_info("Certificate checking disabled."); else { r = read_full_file(arg_trust ?: TRUST_FILE, trust, NULL); - if (r < 0) { - log_error("Failed to read CA certificate file '%s': %s", - arg_trust ?: TRUST_FILE, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to read CA certificate file '%s': %m", + arg_trust ?: TRUST_FILE); } return 0; @@ -1550,7 +1512,7 @@ int main(int argc, char **argv) { r = sd_event_set_watchdog(s.events, true); if (r < 0) - log_error("Failed to enable watchdog: %s", strerror(-r)); + log_error_errno(r, "Failed to enable watchdog: %m"); else log_debug("Watchdog is %s.", r > 0 ? "enabled" : "disabled"); @@ -1569,7 +1531,7 @@ int main(int argc, char **argv) { r = sd_event_run(s.events, -1); if (r < 0) { - log_error("Failed to run event loop: %s", strerror(-r)); + log_error_errno(r, "Failed to run event loop: %m"); break; } } diff --git a/src/journal-remote/journal-upload-journal.c b/src/journal-remote/journal-upload-journal.c index 1cd52db2c1..942320cbf6 100644 --- a/src/journal-remote/journal-upload-journal.c +++ b/src/journal-remote/journal-upload-journal.c @@ -25,10 +25,8 @@ static ssize_t write_entry(char *buf, size_t size, Uploader *u) { u->current_cursor = NULL; r = sd_journal_get_cursor(u->journal, &u->current_cursor); - if (r < 0) { - log_error("Failed to get cursor: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get cursor: %m"); r = snprintf(buf + pos, size - pos, "__CURSOR=%s\n", u->current_cursor); @@ -51,10 +49,8 @@ static ssize_t write_entry(char *buf, size_t size, Uploader *u) { usec_t realtime; r = sd_journal_get_realtime_usec(u->journal, &realtime); - if (r < 0) { - log_error("Failed to get realtime timestamp: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get realtime timestamp: %m"); r = snprintf(buf + pos, size - pos, "__REALTIME_TIMESTAMP="USEC_FMT"\n", realtime); @@ -78,10 +74,8 @@ static ssize_t write_entry(char *buf, size_t size, Uploader *u) { sd_id128_t boot_id; r = sd_journal_get_monotonic_usec(u->journal, &monotonic, &boot_id); - if (r < 0) { - log_error("Failed to get monotonic timestamp: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get monotonic timestamp: %m"); r = snprintf(buf + pos, size - pos, "__MONOTONIC_TIMESTAMP="USEC_FMT"\n", monotonic); @@ -105,10 +99,8 @@ static ssize_t write_entry(char *buf, size_t size, Uploader *u) { char sid[33]; r = sd_journal_get_monotonic_usec(u->journal, NULL, &boot_id); - if (r < 0) { - log_error("Failed to get monotonic timestamp: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get monotonic timestamp: %m"); r = snprintf(buf + pos, size - pos, "_BOOT_ID=%s\n", sd_id128_to_string(boot_id, sid)); @@ -133,11 +125,9 @@ static ssize_t write_entry(char *buf, size_t size, Uploader *u) { r = sd_journal_enumerate_data(u->journal, &u->field_data, &u->field_length); - if (r < 0) { - log_error("Failed to move to next field in entry: %s", - strerror(-r)); - return r; - } else if (r == 0) { + if (r < 0) + return log_error_errno(r, "Failed to move to next field in entry: %m"); + else if (r == 0) { u->entry_state = ENTRY_OUTRO; continue; } @@ -250,8 +240,7 @@ static size_t journal_input_callback(void *buf, size_t size, size_t nmemb, void if (u->entry_state == ENTRY_DONE) { r = sd_journal_next(j); if (r < 0) { - log_error("Failed to move to next entry in journal: %s", - strerror(-r)); + log_error_errno(r, "Failed to move to next entry in journal: %m"); return CURL_READFUNC_ABORT; } else if (r == 0) { if (u->input_event) @@ -304,10 +293,9 @@ static int process_journal_input(Uploader *u, int skip) { int r; r = sd_journal_next_skip(u->journal, skip); - if (r < 0) { - log_error("Failed to skip to next entry: %s", strerror(-r)); - return r; - } else if (r < skip) + if (r < 0) + return log_error_errno(r, "Failed to skip to next entry: %m"); + else if (r < skip) return 0; /* have data */ @@ -321,7 +309,7 @@ int check_journal_input(Uploader *u) { r = sd_journal_process(u->journal); if (r < 0) { - log_error("Failed to process journal: %s", strerror(-r)); + log_error_errno(r, "Failed to process journal: %m"); close_journal_input(u); return r; } @@ -363,10 +351,8 @@ int open_journal_for_upload(Uploader *u, if (follow) { fd = sd_journal_get_fd(j); - if (fd < 0) { - log_error("sd_journal_get_fd failed: %s", strerror(-fd)); - return fd; - } + if (fd < 0) + return log_error_errno(fd, "sd_journal_get_fd failed: %m"); events = sd_journal_get_events(j); @@ -379,10 +365,8 @@ int open_journal_for_upload(Uploader *u, r = sd_event_add_io(u->events, &u->input_event, fd, events, dispatch_journal_input, u); - if (r < 0) { - log_error("Failed to register input event: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to register input event: %m"); log_debug("Listening for journal events on fd:%d, timeout %d", fd, u->timeout == (uint64_t) -1 ? -1 : (int) u->timeout); @@ -392,9 +376,8 @@ int open_journal_for_upload(Uploader *u, if (cursor) { r = sd_journal_seek_cursor(j, cursor); if (r < 0) { - log_error("Failed to seek to cursor %s: %s", - cursor, strerror(-r)); - return r; + return log_error_errno(r, "Failed to seek to cursor %s: %m", + cursor); } } diff --git a/src/journal-remote/journal-upload.c b/src/journal-remote/journal-upload.c index 9f13ffdbfe..62853b6367 100644 --- a/src/journal-remote/journal-upload.c +++ b/src/journal-remote/journal-upload.c @@ -87,8 +87,8 @@ static size_t output_callback(char *buf, if (nmemb && !u->answer) { u->answer = strndup(buf, size*nmemb); if (!u->answer) - log_warning("Failed to store server answer (%zu bytes): %s", - size*nmemb, strerror(ENOMEM)); + log_warning_errno(ENOMEM, "Failed to store server answer (%zu bytes): %m", + size*nmemb); } return size * nmemb; @@ -103,18 +103,14 @@ static int check_cursor_updating(Uploader *u) { return 0; r = mkdir_parents(u->state_file, 0755); - if (r < 0) { - log_error("Cannot create parent directory of state file %s: %s", - u->state_file, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Cannot create parent directory of state file %s: %m", + u->state_file); r = fopen_temporary(u->state_file, &f, &temp_path); - if (r < 0) { - log_error("Cannot save state to %s: %s", - u->state_file, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Cannot save state to %s: %m", + u->state_file); unlink(temp_path); return 0; @@ -147,7 +143,7 @@ static int update_cursor_state(Uploader *u) { finish: if (r < 0) - log_error("Failed to save state %s: %s", u->state_file, strerror(-r)); + log_error_errno(r, "Failed to save state %s: %m", u->state_file); return r; } @@ -164,11 +160,10 @@ static int load_cursor_state(Uploader *u) { if (r == -ENOENT) log_debug("State file %s is not present.", u->state_file); - else if (r < 0) { - log_error("Failed to read state file %s: %s", - u->state_file, strerror(-r)); - return r; - } else + else if (r < 0) + return log_error_errno(r, "Failed to read state file %s: %m", + u->state_file); + else log_debug("Last cursor was %s", u->last_cursor); return 0; @@ -313,7 +308,7 @@ static size_t fd_input_callback(void *buf, size_t size, size_t nmemb, void *user close_fd_input(u); return 0; } else { - log_error("Aborting transfer after read error on input: %m."); + log_error_errno(errno, "Aborting transfer after read error on input: %m."); return CURL_READFUNC_ABORT; } } @@ -362,10 +357,8 @@ static int open_file_for_upload(Uploader *u, const char *filename) { fd = STDIN_FILENO; else { fd = open(filename, O_RDONLY|O_CLOEXEC|O_NOCTTY); - if (fd < 0) { - log_error("Failed to open %s: %m", filename); - return -errno; - } + if (fd < 0) + return log_error_errno(errno, "Failed to open %s: %m", filename); } u->input = fd; @@ -374,10 +367,8 @@ static int open_file_for_upload(Uploader *u, const char *filename) { r = sd_event_add_io(u->events, &u->input_event, fd, EPOLLIN, dispatch_fd_input, u); if (r < 0) { - if (r != -EPERM || arg_follow > 0) { - log_error("Failed to register input event: %s", strerror(-r)); - return r; - } + if (r != -EPERM || arg_follow > 0) + return log_error_errno(r, "Failed to register input event: %m"); /* Normal files should just be consumed without polling. */ r = start_upload(u, fd_input_callback, u); @@ -458,16 +449,12 @@ static int setup_uploader(Uploader *u, const char *url, const char *state_file) u->state_file = state_file; r = sd_event_default(&u->events); - if (r < 0) { - log_error("sd_event_default failed: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "sd_event_default failed: %m"); r = setup_signals(u); - if (r < 0) { - log_error("Failed to set up signals: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to set up signals: %m"); return load_cursor_state(u); } @@ -545,10 +532,10 @@ static int parse_config(void) { { "Upload", "TrustedCertificateFile", config_parse_path, 0, &arg_trust }, {}}; - return config_parse(NULL, PKGSYSCONFDIR "/journal-upload.conf", NULL, - "Upload\0", - config_item_table_lookup, items, - false, false, true, NULL); + return config_parse_many(PKGSYSCONFDIR "/journal-upload.conf", + CONF_DIRS_NULSTR("systemd/journal-upload.conf"), + "Upload\0", config_item_table_lookup, items, + false, NULL); } static void help(void) { @@ -701,10 +688,8 @@ static int parse_argv(int argc, char *argv[]) { case ARG_FILE: r = glob_extend(&arg_file, optarg); - if (r < 0) { - log_error("Failed to add paths: %s", strerror(-r)); - return r; - }; + if (r < 0) + return log_error_errno(r, "Failed to add paths: %m"); break; case ARG_CURSOR: @@ -786,9 +771,8 @@ static int open_journal(sd_journal **j) { else r = sd_journal_open(j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type); if (r < 0) - log_error("Failed to open %s: %s", - arg_directory ? arg_directory : arg_file ? "files" : "journal", - strerror(-r)); + log_error_errno(r, "Failed to open %s: %m", + arg_directory ? arg_directory : arg_file ? "files" : "journal"); return r; } @@ -869,7 +853,7 @@ int main(int argc, char **argv) { r = sd_event_run(u.events, u.timeout); if (r < 0) { - log_error("Failed to run event loop: %s", strerror(-r)); + log_error_errno(r, "Failed to run event loop: %m"); break; } } diff --git a/src/journal-remote/microhttpd-util.c b/src/journal-remote/microhttpd-util.c index 55c45f4693..277e125403 100644 --- a/src/journal-remote/microhttpd-util.c +++ b/src/journal-remote/microhttpd-util.c @@ -41,7 +41,7 @@ void microhttpd_logger(void *arg, const char *fmt, va_list ap) { f = strappenda("microhttpd: ", fmt); DISABLE_WARNING_FORMAT_NONLITERAL; - log_metav(LOG_INFO, NULL, 0, NULL, f, ap); + log_internalv(LOG_INFO, 0, NULL, 0, NULL, f, ap); REENABLE_WARNING; } @@ -126,11 +126,10 @@ void log_func_gnutls(int level, const char *message) { if (0 <= level && level < (int) ELEMENTSOF(gnutls_log_map)) { if (gnutls_log_map[level].enabled) - log_meta(gnutls_log_map[level].level, NULL, 0, NULL, - "gnutls %d/%s: %s", level, gnutls_log_map[level].names[1], message); + log_internal(gnutls_log_map[level].level, 0, NULL, 0, NULL, "gnutls %d/%s: %s", level, gnutls_log_map[level].names[1], message); } else { log_debug("Received GNUTLS message with unknown level %d.", level); - log_meta(LOG_DEBUG, NULL, 0, NULL, "gnutls: %s", message); + log_internal(LOG_DEBUG, 0, NULL, 0, NULL, "gnutls: %s", message); } } @@ -171,17 +170,13 @@ static int verify_cert_authorized(gnutls_session_t session) { int r; r = gnutls_certificate_verify_peers2(session, &status); - if (r < 0) { - log_error("gnutls_certificate_verify_peers2 failed: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "gnutls_certificate_verify_peers2 failed: %m"); type = gnutls_certificate_type_get(session); r = gnutls_certificate_verification_status_print(status, type, &out, 0); - if (r < 0) { - log_error("gnutls_certificate_verification_status_print failed: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "gnutls_certificate_verification_status_print failed: %m"); log_info("Certificate status: %s", out.data); diff --git a/src/journal/cat.c b/src/journal/cat.c index 627c0624a5..79706b692d 100644 --- a/src/journal/cat.c +++ b/src/journal/cat.c @@ -134,7 +134,7 @@ int main(int argc, char *argv[]) { fd = sd_journal_stream_fd(arg_identifier, arg_priority, arg_level_prefix); if (fd < 0) { - log_error("Failed to create stream fd: %s", strerror(-fd)); + log_error_errno(fd, "Failed to create stream fd: %m"); r = fd; goto finish; } @@ -143,7 +143,7 @@ int main(int argc, char *argv[]) { if (dup3(fd, STDOUT_FILENO, 0) < 0 || dup3(fd, STDERR_FILENO, 0) < 0) { - log_error("Failed to duplicate fd: %m"); + log_error_errno(errno, "Failed to duplicate fd: %m"); r = -errno; goto finish; } @@ -164,7 +164,7 @@ int main(int argc, char *argv[]) { if (saved_stderr >= 0) dup3(saved_stderr, STDERR_FILENO, 0); - log_error("Failed to execute process: %s", strerror(-r)); + log_error_errno(r, "Failed to execute process: %m"); finish: safe_close(fd); diff --git a/src/journal/catalog.c b/src/journal/catalog.c index 41d450b154..81a2e946e4 100644 --- a/src/journal/catalog.c +++ b/src/journal/catalog.c @@ -209,14 +209,12 @@ int catalog_import_file(Hashmap *h, struct strbuf *sb, const char *path) { assert(path); f = fopen(path, "re"); - if (!f) { - log_error("Failed to open file %s: %m", path); - return -errno; - } + if (!f) + return log_error_errno(errno, "Failed to open file %s: %m", path); r = catalog_file_lang(path, &deflang); if (r < 0) - log_error("Failed to determine language for file %s: %m", path); + log_error_errno(errno, "Failed to determine language for file %s: %m", path); if (r == 1) log_debug("File %s has language %s.", path, deflang); @@ -229,7 +227,7 @@ int catalog_import_file(Hashmap *h, struct strbuf *sb, const char *path) { if (feof(f)) break; - log_error("Failed to read file %s: %m", path); + log_error_errno(errno, "Failed to read file %s: %m", path); return -errno; } @@ -341,17 +339,13 @@ static long write_catalog(const char *database, Hashmap *h, struct strbuf *sb, return log_oom(); r = mkdir_p(d, 0775); - if (r < 0) { - log_error("Recursive mkdir %s: %s", d, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Recursive mkdir %s: %m", d); r = fopen_temporary(database, &w, &p); - if (r < 0) { - log_error("Failed to open database for writing: %s: %s", - database, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to open database for writing: %s: %m", + database); zero(header); memcpy(header.signature, CATALOG_SIGNATURE, sizeof(header.signature)); @@ -389,7 +383,7 @@ static long write_catalog(const char *database, Hashmap *h, struct strbuf *sb, fchmod(fileno(w), 0644); if (rename(p, database) < 0) { - log_error("rename (%s -> %s) failed: %m", p, database); + log_error_errno(errno, "rename (%s -> %s) failed: %m", p, database); r = -errno; goto error; } @@ -422,7 +416,7 @@ int catalog_update(const char* database, const char* root, const char* const* di r = conf_files_list_strv(&files, ".catalog", root, dirs); if (r < 0) { - log_error("Failed to get catalog files: %s", strerror(-r)); + log_error_errno(r, "Failed to get catalog files: %m"); goto finish; } @@ -463,7 +457,7 @@ int catalog_update(const char* database, const char* root, const char* const* di r = write_catalog(database, h, sb, items, n); if (r < 0) - log_error("Failed to write %s: %s", database, strerror(-r)); + log_error_errno(r, "Failed to write %s: %m", database); else log_debug("%s: wrote %u items, with %zu bytes of strings, %ld total size.", database, n, sb->len, r); @@ -687,8 +681,8 @@ int catalog_list_items(FILE *f, const char *database, bool oneline, char **items k = sd_id128_from_string(*item, &id); if (k < 0) { - log_error("Failed to parse id128 '%s': %s", - *item, strerror(-k)); + log_error_errno(k, "Failed to parse id128 '%s': %m", + *item); if (r == 0) r = k; continue; diff --git a/src/journal/compress.c b/src/journal/compress.c index c4c715be2f..9440fcd60e 100644 --- a/src/journal/compress.c +++ b/src/journal/compress.c @@ -400,12 +400,9 @@ int compress_stream_xz(int fdf, int fdt, off_t max_bytes) { n = sizeof(out) - s.avail_out; - errno = 0; k = loop_write(fdt, out, n, false); if (k < 0) return k; - if (k != n) - return errno ? -errno : -EIO; if (ret == LZMA_STREAM_END) { log_debug("XZ compression finished (%"PRIu64" -> %"PRIu64" bytes, %.1f%%)", @@ -478,8 +475,6 @@ int compress_stream_lz4(int fdf, int fdt, off_t max_bytes) { n = loop_write(fdt, out, r, false); if (n < 0) return n; - if (n != r) - return errno ? -errno : -EIO; total_out += sizeof(header) + r; @@ -559,12 +554,9 @@ int decompress_stream_xz(int fdf, int fdt, off_t max_bytes) { max_bytes -= n; } - errno = 0; k = loop_write(fdt, out, n, false); if (k < 0) return k; - if (k != n) - return errno ? -errno : -EIO; if (ret == LZMA_STREAM_END) { log_debug("XZ decompression finished (%"PRIu64" -> %"PRIu64" bytes, %.1f%%)", @@ -645,12 +637,9 @@ int decompress_stream_lz4(int fdf, int fdt, off_t max_bytes) { return -EFBIG; } - errno = 0; n = loop_write(fdt, out, r, false); if (n < 0) return n; - if (n != r) - return errno ? -errno : -EIO; } log_debug("LZ4 decompression finished (%zu -> %zu bytes, %.1f%%)", diff --git a/src/journal/coredump-vacuum.c b/src/journal/coredump-vacuum.c index fec901e8e4..9b73795e5b 100644 --- a/src/journal/coredump-vacuum.c +++ b/src/journal/coredump-vacuum.c @@ -139,10 +139,8 @@ int coredump_vacuum(int exclude_fd, off_t keep_free, off_t max_use) { return 0; if (exclude_fd >= 0) { - if (fstat(exclude_fd, &exclude_st) < 0) { - log_error("Failed to fstat(): %m"); - return -errno; - } + if (fstat(exclude_fd, &exclude_st) < 0) + return log_error_errno(errno, "Failed to fstat(): %m"); } /* This algorithm will keep deleting the oldest file of the @@ -156,7 +154,7 @@ int coredump_vacuum(int exclude_fd, off_t keep_free, off_t max_use) { if (errno == ENOENT) return 0; - log_error("Can't open coredump directory: %m"); + log_error_errno(errno, "Can't open coredump directory: %m"); return -errno; } @@ -258,7 +256,7 @@ int coredump_vacuum(int exclude_fd, off_t keep_free, off_t max_use) { if (errno == ENOENT) continue; - log_error("Failed to remove file %s: %m", worst->oldest_file); + log_error_errno(errno, "Failed to remove file %s: %m", worst->oldest_file); return -errno; } else log_info("Removed old coredump %s.", worst->oldest_file); @@ -267,6 +265,6 @@ int coredump_vacuum(int exclude_fd, off_t keep_free, off_t max_use) { return 0; fail: - log_error("Failed to read directory: %m"); + log_error_errno(errno, "Failed to read directory: %m"); return -errno; } diff --git a/src/journal/coredump.c b/src/journal/coredump.c index 88d720f651..be45a684e5 100644 --- a/src/journal/coredump.c +++ b/src/journal/coredump.c @@ -36,6 +36,7 @@ #include "log.h" #include "util.h" +#include "fileio.h" #include "strv.h" #include "macro.h" #include "mkdir.h" @@ -119,10 +120,11 @@ static int parse_config(void) { {} }; - return config_parse(NULL, "/etc/systemd/coredump.conf", NULL, - "Coredump\0", - config_item_table_lookup, items, - false, false, true, NULL); + return config_parse_many("/etc/systemd/coredump.conf", + CONF_DIRS_NULSTR("systemd/coredump.conf"), + "Coredump\0", + config_item_table_lookup, items, + false, NULL); } static int fix_acl(int fd, uid_t uid) { @@ -141,29 +143,25 @@ static int fix_acl(int fd, uid_t uid) { * their own coredumps */ acl = acl_get_fd(fd); - if (!acl) { - log_error("Failed to get ACL: %m"); - return -errno; - } + if (!acl) + return log_error_errno(errno, "Failed to get ACL: %m"); if (acl_create_entry(&acl, &entry) < 0 || acl_set_tag_type(entry, ACL_USER) < 0 || acl_set_qualifier(entry, &uid) < 0) { - log_error("Failed to patch ACL: %m"); + log_error_errno(errno, "Failed to patch ACL: %m"); return -errno; } if (acl_get_permset(entry, &permset) < 0 || acl_add_perm(permset, ACL_READ) < 0 || calc_acl_mask_if_needed(&acl) < 0) { - log_warning("Failed to patch ACL: %m"); + log_warning_errno(errno, "Failed to patch ACL: %m"); return -errno; } - if (acl_set_fd(fd, acl) < 0) { - log_error("Failed to apply ACL: %m"); - return -errno; - } + if (acl_set_fd(fd, acl) < 0) + return log_error_errno(errno, "Failed to apply ACL: %m"); #endif return 0; @@ -222,15 +220,11 @@ static int fix_permissions( fix_acl(fd, uid); fix_xattr(fd, info); - if (fsync(fd) < 0) { - log_error("Failed to sync coredump %s: %m", filename); - return -errno; - } + if (fsync(fd) < 0) + return log_error_errno(errno, "Failed to sync coredump %s: %m", filename); - if (rename(filename, target) < 0) { - log_error("Failed to rename coredump %s -> %s: %m", filename, target); - return -errno; - } + if (rename(filename, target) < 0) + return log_error_errno(errno, "Failed to rename coredump %s -> %s: %m", filename, target); return 0; } @@ -246,10 +240,8 @@ static int maybe_remove_external_coredump(const char *filename, off_t size) { if (!filename) return 1; - if (unlink(filename) < 0 && errno != ENOENT) { - log_error("Failed to unlink %s: %m", filename); - return -errno; - } + if (unlink(filename) < 0 && errno != ENOENT) + return log_error_errno(errno, "Failed to unlink %s: %m", filename); return 1; } @@ -311,10 +303,8 @@ static int save_external_coredump( assert(ret_size); r = make_filename(info, &fn); - if (r < 0) { - log_error("Failed to determine coredump file name: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to determine coredump file name: %m"); tmp = tempfn_random(fn); if (!tmp) @@ -323,30 +313,28 @@ static int save_external_coredump( mkdir_p_label("/var/lib/systemd/coredump", 0755); fd = open(tmp, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640); - if (fd < 0) { - log_error("Failed to create coredump file %s: %m", tmp); - return -errno; - } + if (fd < 0) + return log_error_errno(errno, "Failed to create coredump file %s: %m", tmp); r = copy_bytes(STDIN_FILENO, fd, arg_process_size_max); - if (r == -E2BIG) { + if (r == -EFBIG) { log_error("Coredump of %s (%s) is larger than configured processing limit, refusing.", info[INFO_PID], info[INFO_COMM]); goto fail; } else if (IN_SET(r, -EDQUOT, -ENOSPC)) { log_error("Not enough disk space for coredump of %s (%s), refusing.", info[INFO_PID], info[INFO_COMM]); goto fail; } else if (r < 0) { - log_error("Failed to dump coredump to file: %s", strerror(-r)); + log_error_errno(r, "Failed to dump coredump to file: %m"); goto fail; } if (fstat(fd, &st) < 0) { - log_error("Failed to fstat coredump %s: %m", tmp); + log_error_errno(errno, "Failed to fstat coredump %s: %m", tmp); goto fail; } if (lseek(fd, 0, SEEK_SET) == (off_t) -1) { - log_error("Failed to seek on %s: %m", tmp); + log_error_errno(errno, "Failed to seek on %s: %m", tmp); goto fail; } @@ -372,13 +360,13 @@ static int save_external_coredump( fd_compressed = open(tmp_compressed, O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0640); if (fd_compressed < 0) { - log_error("Failed to create file %s: %m", tmp_compressed); + log_error_errno(errno, "Failed to create file %s: %m", tmp_compressed); goto uncompressed; } r = compress_stream(fd, fd_compressed, -1); if (r < 0) { - log_error("Failed to compress %s: %s", tmp_compressed, strerror(-r)); + log_error_errno(r, "Failed to compress %s: %m", tmp_compressed); goto fail_compressed; } @@ -430,10 +418,8 @@ static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_s assert(ret); assert(ret_size); - if (lseek(fd, 0, SEEK_SET) == (off_t) -1) { - log_warning("Failed to seek: %m"); - return -errno; - } + if (lseek(fd, 0, SEEK_SET) == (off_t) -1) + return log_warning_errno(errno, "Failed to seek: %m"); field = malloc(9 + size); if (!field) { @@ -444,10 +430,8 @@ static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_s memcpy(field, "COREDUMP=", 9); n = read(fd, field + 9, size); - if (n < 0) { - log_error("Failed to read core data: %s", strerror(-n)); - return (int) n; - } + if (n < 0) + return log_error_errno((int) n, "Failed to read core data: %m"); if ((size_t) n < size) { log_error("Core data too short."); return -EIO; @@ -461,24 +445,118 @@ static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_s return 0; } +/* Joins /proc/[pid]/fd/ and /proc/[pid]/fdinfo/ into the following lines: + * 0:/dev/pts/23 + * pos: 0 + * flags: 0100002 + * + * 1:/dev/pts/23 + * pos: 0 + * flags: 0100002 + * + * 2:/dev/pts/23 + * pos: 0 + * flags: 0100002 + * EOF + */ +static int compose_open_fds(pid_t pid, char **open_fds) { + _cleanup_closedir_ DIR *proc_fd_dir = NULL; + _cleanup_close_ int proc_fdinfo_fd = -1; + _cleanup_free_ char *buffer = NULL; + _cleanup_fclose_ FILE *stream = NULL; + const char *fddelim = "", *path; + struct dirent *dent = NULL; + size_t size = 0; + int r = 0; + + assert(pid >= 0); + assert(open_fds != NULL); + + path = procfs_file_alloca(pid, "fd"); + proc_fd_dir = opendir(path); + if (!proc_fd_dir) + return -errno; + + proc_fdinfo_fd = openat(dirfd(proc_fd_dir), "../fdinfo", O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC|O_PATH); + if (proc_fdinfo_fd < 0) + return -errno; + + stream = open_memstream(&buffer, &size); + if (!stream) + return -ENOMEM; + + FOREACH_DIRENT(dent, proc_fd_dir, return -errno) { + _cleanup_fclose_ FILE *fdinfo = NULL; + _cleanup_free_ char *fdname = NULL; + char line[LINE_MAX]; + int fd; + + r = readlinkat_malloc(dirfd(proc_fd_dir), dent->d_name, &fdname); + if (r < 0) + return r; + + fprintf(stream, "%s%s:%s\n", fddelim, dent->d_name, fdname); + fddelim = "\n"; + + /* Use the directory entry from /proc/[pid]/fd with /proc/[pid]/fdinfo */ + fd = openat(proc_fdinfo_fd, dent->d_name, O_NOFOLLOW|O_CLOEXEC|O_RDONLY); + if (fd < 0) + continue; + + fdinfo = fdopen(fd, "re"); + if (fdinfo == NULL) { + close(fd); + continue; + } + + FOREACH_LINE(line, fdinfo, break) { + fputs(line, stream); + if (!endswith(line, "\n")) + fputc('\n', stream); + } + } + + errno = 0; + fclose(stream); + stream = NULL; + + if (errno != 0) + return -errno; + + *open_fds = buffer; + buffer = NULL; + + return 0; +} + int main(int argc, char* argv[]) { - _cleanup_free_ char *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL, - *core_timestamp = NULL, *core_comm = NULL, *core_exe = NULL, *core_unit = NULL, - *core_session = NULL, *core_message = NULL, *core_cmdline = NULL, *coredump_data = NULL, - *core_slice = NULL, *core_cgroup = NULL, *core_owner_uid = NULL, - *exe = NULL, *comm = NULL, *filename = NULL; + /* The small core field we allocate on the stack, to keep things simple */ + char + *core_pid = NULL, *core_uid = NULL, *core_gid = NULL, *core_signal = NULL, + *core_session = NULL, *core_exe = NULL, *core_comm = NULL, *core_cmdline = NULL, + *core_cgroup = NULL, *core_cwd = NULL, *core_root = NULL, *core_unit = NULL, + *core_slice = NULL; + + /* The larger ones we allocate on the heap */ + _cleanup_free_ char + *core_timestamp = NULL, *core_message = NULL, *coredump_data = NULL, *core_owner_uid = NULL, + *core_open_fds = NULL, *core_proc_status = NULL, *core_proc_maps = NULL, *core_proc_limits = NULL, + *core_proc_cgroup = NULL, *core_environ = NULL; + + _cleanup_free_ char *exe = NULL, *comm = NULL, *filename = NULL; const char *info[_INFO_LEN]; _cleanup_close_ int coredump_fd = -1; - struct iovec iovec[18]; + struct iovec iovec[26]; off_t coredump_size; int r, j = 0; uid_t uid, owner_uid; gid_t gid; pid_t pid; char *t; + const char *p; /* Make sure we never enter a loop */ prctl(PR_SET_DUMPABLE, 0); @@ -521,7 +599,7 @@ int main(int argc, char* argv[]) { } if (get_process_comm(pid, &comm) < 0) { - log_warning("Failed to get COMM, falling back to the commandline."); + log_warning("Failed to get COMM, falling back to the command line."); comm = strv_join(argv + INFO_COMM + 1, " "); } @@ -539,6 +617,7 @@ int main(int argc, char* argv[]) { if (cg_pid_get_unit(pid, &t) >= 0) { if (streq(t, SPECIAL_JOURNALD_SERVICE)) { + free(t); /* If we are journald, we cut things short, * don't write to the journal, but still @@ -559,9 +638,13 @@ int main(int argc, char* argv[]) { goto finish; } - core_unit = strappend("COREDUMP_UNIT=", t); - } else if (cg_pid_get_user_unit(pid, &t) >= 0) - core_unit = strappend("COREDUMP_USER_UNIT=", t); + core_unit = strappenda("COREDUMP_UNIT=", t); + free(t); + + } else if (cg_pid_get_user_unit(pid, &t) >= 0) { + core_unit = strappenda("COREDUMP_USER_UNIT=", t); + free(t); + } if (core_unit) IOVEC_SET_STRING(iovec[j++], core_unit); @@ -571,28 +654,23 @@ int main(int argc, char* argv[]) { log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); log_open(); - core_pid = strappend("COREDUMP_PID=", info[INFO_PID]); - if (core_pid) - IOVEC_SET_STRING(iovec[j++], core_pid); + core_pid = strappenda("COREDUMP_PID=", info[INFO_PID]); + IOVEC_SET_STRING(iovec[j++], core_pid); - core_uid = strappend("COREDUMP_UID=", info[INFO_UID]); - if (core_uid) - IOVEC_SET_STRING(iovec[j++], core_uid); + core_uid = strappenda("COREDUMP_UID=", info[INFO_UID]); + IOVEC_SET_STRING(iovec[j++], core_uid); - core_gid = strappend("COREDUMP_GID=", info[INFO_GID]); - if (core_gid) - IOVEC_SET_STRING(iovec[j++], core_gid); + core_gid = strappenda("COREDUMP_GID=", info[INFO_GID]); + IOVEC_SET_STRING(iovec[j++], core_gid); - core_signal = strappend("COREDUMP_SIGNAL=", info[INFO_SIGNAL]); - if (core_signal) - IOVEC_SET_STRING(iovec[j++], core_signal); + core_signal = strappenda("COREDUMP_SIGNAL=", info[INFO_SIGNAL]); + IOVEC_SET_STRING(iovec[j++], core_signal); if (sd_pid_get_session(pid, &t) >= 0) { - core_session = strappend("COREDUMP_SESSION=", t); + core_session = strappenda("COREDUMP_SESSION=", t); free(t); - if (core_session) - IOVEC_SET_STRING(iovec[j++], core_session); + IOVEC_SET_STRING(iovec[j++], core_session); } if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) { @@ -603,39 +681,100 @@ int main(int argc, char* argv[]) { } if (sd_pid_get_slice(pid, &t) >= 0) { - core_slice = strappend("COREDUMP_SLICE=", t); + core_slice = strappenda("COREDUMP_SLICE=", t); free(t); - if (core_slice) - IOVEC_SET_STRING(iovec[j++], core_slice); + IOVEC_SET_STRING(iovec[j++], core_slice); } if (comm) { - core_comm = strappend("COREDUMP_COMM=", comm); - if (core_comm) - IOVEC_SET_STRING(iovec[j++], core_comm); + core_comm = strappenda("COREDUMP_COMM=", comm); + IOVEC_SET_STRING(iovec[j++], core_comm); } if (exe) { - core_exe = strappend("COREDUMP_EXE=", exe); - if (core_exe) - IOVEC_SET_STRING(iovec[j++], core_exe); + core_exe = strappenda("COREDUMP_EXE=", exe); + IOVEC_SET_STRING(iovec[j++], core_exe); } if (get_process_cmdline(pid, 0, false, &t) >= 0) { - core_cmdline = strappend("COREDUMP_CMDLINE=", t); + core_cmdline = strappenda("COREDUMP_CMDLINE=", t); free(t); - if (core_cmdline) - IOVEC_SET_STRING(iovec[j++], core_cmdline); + IOVEC_SET_STRING(iovec[j++], core_cmdline); } if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0) { - core_cgroup = strappend("COREDUMP_CGROUP=", t); + core_cgroup = strappenda("COREDUMP_CGROUP=", t); + free(t); + + IOVEC_SET_STRING(iovec[j++], core_cgroup); + } + + if (compose_open_fds(pid, &t) >= 0) { + core_open_fds = strappend("COREDUMP_OPEN_FDS=", t); + free(t); + + if (core_open_fds) + IOVEC_SET_STRING(iovec[j++], core_open_fds); + } + + p = procfs_file_alloca(pid, "status"); + if (read_full_file(p, &t, NULL) >= 0) { + core_proc_status = strappend("COREDUMP_PROC_STATUS=", t); + free(t); + + if (core_proc_status) + IOVEC_SET_STRING(iovec[j++], core_proc_status); + } + + p = procfs_file_alloca(pid, "maps"); + if (read_full_file(p, &t, NULL) >= 0) { + core_proc_maps = strappend("COREDUMP_PROC_MAPS=", t); + free(t); + + if (core_proc_maps) + IOVEC_SET_STRING(iovec[j++], core_proc_maps); + } + + p = procfs_file_alloca(pid, "limits"); + if (read_full_file(p, &t, NULL) >= 0) { + core_proc_limits = strappend("COREDUMP_PROC_LIMITS=", t); + free(t); + + if (core_proc_limits) + IOVEC_SET_STRING(iovec[j++], core_proc_limits); + } + + p = procfs_file_alloca(pid, "cgroup"); + if (read_full_file(p, &t, NULL) >=0) { + core_proc_cgroup = strappend("COREDUMP_PROC_CGROUP=", t); + free(t); + + if (core_proc_cgroup) + IOVEC_SET_STRING(iovec[j++], core_proc_cgroup); + } + + if (get_process_cwd(pid, &t) >= 0) { + core_cwd = strappenda("COREDUMP_CWD=", t); + free(t); + + IOVEC_SET_STRING(iovec[j++], core_cwd); + } + + if (get_process_root(pid, &t) >= 0) { + core_root = strappenda("COREDUMP_ROOT=", t); + free(t); + + IOVEC_SET_STRING(iovec[j++], core_root); + } + + if (get_process_environ(pid, &t) >= 0) { + core_environ = strappend("COREDUMP_ENVIRON=", t); free(t); - if (core_cgroup) - IOVEC_SET_STRING(iovec[j++], core_cgroup); + if (core_environ) + IOVEC_SET_STRING(iovec[j++], core_environ); } core_timestamp = strjoin("COREDUMP_TIMESTAMP=", info[INFO_TIMESTAMP], "000000", NULL); @@ -678,7 +817,7 @@ int main(int argc, char* argv[]) { * thus making sure the user gets access to the core dump. */ if (setresgid(gid, gid, gid) < 0 || setresuid(uid, uid, uid) < 0) { - log_error("Failed to drop privileges: %m"); + log_error_errno(errno, "Failed to drop privileges: %m"); r = -errno; goto finish; } @@ -694,7 +833,7 @@ int main(int argc, char* argv[]) { else if (r == -EINVAL) log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno())); else - log_warning("Failed to generate stack trace: %s", strerror(-r)); + log_warning_errno(r, "Failed to generate stack trace: %m"); } if (!core_message) @@ -721,7 +860,7 @@ log: r = sd_journal_sendv(iovec, j); if (r < 0) - log_error("Failed to log coredump: %s", strerror(-r)); + log_error_errno(r, "Failed to log coredump: %m"); finish: return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; diff --git a/src/journal/coredump.conf b/src/journal/coredump.conf index 0cc328f549..0fe9fe801a 100644 --- a/src/journal/coredump.conf +++ b/src/journal/coredump.conf @@ -5,6 +5,9 @@ # the Free Software Foundation; either version 2.1 of the License, or # (at your option) any later version. # +# You can override the directives in this file by creating files in +# /etc/systemd/coredump.conf.d/*.conf. +# # See coredump.conf(5) for details [Coredump] diff --git a/src/journal/coredumpctl.c b/src/journal/coredumpctl.c index d4756fe67d..a6551ac44b 100644 --- a/src/journal/coredumpctl.c +++ b/src/journal/coredumpctl.c @@ -73,7 +73,7 @@ static Set *new_matches(void) { r = set_consume(set, tmp); if (r < 0) { - log_error("failed to add to set: %s", strerror(-r)); + log_error_errno(r, "failed to add to set: %m"); set_free(set); return NULL; } @@ -110,14 +110,13 @@ static int add_match(Set *set, const char *match) { log_debug("Adding pattern: %s", pattern); r = set_consume(set, pattern); if (r < 0) { - log_error("Failed to add pattern: %s", strerror(-r)); + log_error_errno(r, "Failed to add pattern: %m"); goto fail; } return 0; fail: - log_error("Failed to add match: %s", strerror(-r)); - return r; + return log_error_errno(r, "Failed to add match: %m"); } static void help(void) { @@ -191,10 +190,8 @@ static int parse_argv(int argc, char *argv[], Set *matches) { } output = fopen(optarg, "we"); - if (!output) { - log_error("writing to '%s': %m", optarg); - return -errno; - } + if (!output) + return log_error_errno(errno, "writing to '%s': %m", optarg); break; @@ -326,10 +323,8 @@ static int print_list(FILE* file, sd_journal *j, int had_legend) { } r = sd_journal_get_realtime_usec(j, &t); - if (r < 0) { - log_error("Failed to get realtime timestamp: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get realtime timestamp: %m"); format_timestamp(buf, sizeof(buf), t); present = filename && access(filename, F_OK) == 0; @@ -521,10 +516,8 @@ static int focus(sd_journal *j) { r = sd_journal_seek_tail(j); if (r == 0) r = sd_journal_previous(j); - if (r < 0) { - log_error("Failed to search journal: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to search journal: %m"); if (r == 0) { log_error("No match found."); return -ESRCH; @@ -586,7 +579,7 @@ static int save_core(sd_journal *j, int fd, char **path, bool *unlink_temp) { * compressed file (probably uncached). */ r = sd_journal_get_data(j, "COREDUMP_FILENAME", (const void**) &data, &len); if (r < 0 && r != -ENOENT) - log_warning("Failed to retrieve COREDUMP_FILENAME: %s", strerror(-r)); + log_warning_errno(r, "Failed to retrieve COREDUMP_FILENAME: %m"); else if (r == 0) retrieve(data, len, "COREDUMP_FILENAME", &filename); @@ -614,10 +607,8 @@ static int save_core(sd_journal *j, int fd, char **path, bool *unlink_temp) { return log_oom(); fdt = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC); - if (fdt < 0) { - log_error("Failed to create temporary file: %m"); - return -errno; - } + if (fdt < 0) + return log_error_errno(errno, "Failed to create temporary file: %m"); log_debug("Created temporary file %s", temp); fd = fdt; @@ -633,7 +624,7 @@ static int save_core(sd_journal *j, int fd, char **path, bool *unlink_temp) { sz = write(fdt, data, len); if (sz < 0) { - log_error("Failed to write temporary file: %m"); + log_error_errno(errno, "Failed to write temporary file: %m"); r = -errno; goto error; } @@ -648,14 +639,14 @@ static int save_core(sd_journal *j, int fd, char **path, bool *unlink_temp) { fdf = open(filename, O_RDONLY | O_CLOEXEC); if (fdf < 0) { - log_error("Failed to open %s: %m", filename); + log_error_errno(errno, "Failed to open %s: %m", filename); r = -errno; goto error; } r = decompress_stream(filename, fdf, fd, -1); if (r < 0) { - log_error("Failed to decompress %s: %s", filename, strerror(-r)); + log_error_errno(r, "Failed to decompress %s: %m", filename); goto error; } #else @@ -667,7 +658,7 @@ static int save_core(sd_journal *j, int fd, char **path, bool *unlink_temp) { if (r == -ENOENT) log_error("Cannot retrieve coredump from journal nor disk."); else - log_error("Failed to retrieve COREDUMP field: %s", strerror(-r)); + log_error_errno(r, "Failed to retrieve COREDUMP field: %m"); goto error; } @@ -704,10 +695,8 @@ static int dump_core(sd_journal* j) { } r = save_core(j, output ? fileno(output) : STDOUT_FILENO, NULL, NULL); - if (r < 0) { - log_error("Coredump retrieval failed: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Coredump retrieval failed: %m"); r = sd_journal_previous(j); if (r >= 0) @@ -735,10 +724,8 @@ static int run_gdb(sd_journal *j) { fputs("\n", stdout); r = sd_journal_get_data(j, "COREDUMP_EXE", (const void**) &data, &len); - if (r < 0) { - log_error("Failed to retrieve COREDUMP_EXE field: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to retrieve COREDUMP_EXE field: %m"); assert(len > strlen("COREDUMP_EXE=")); data += strlen("COREDUMP_EXE="); @@ -759,27 +746,25 @@ static int run_gdb(sd_journal *j) { } r = save_core(j, -1, &path, &unlink_path); - if (r < 0) { - log_error("Failed to retrieve core: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to retrieve core: %m"); pid = fork(); if (pid < 0) { - log_error("Failed to fork(): %m"); + log_error_errno(errno, "Failed to fork(): %m"); r = -errno; goto finish; } if (pid == 0) { execlp("gdb", "gdb", exe, path, NULL); - log_error("Failed to invoke gdb: %m"); + log_error_errno(errno, "Failed to invoke gdb: %m"); _exit(1); } r = wait_for_terminate(pid, &st); if (r < 0) { - log_error("Failed to wait for gdb: %m"); + log_error_errno(errno, "Failed to wait for gdb: %m"); goto finish; } @@ -820,7 +805,7 @@ int main(int argc, char *argv[]) { r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY); if (r < 0) { - log_error("Failed to open journal: %s", strerror(-r)); + log_error_errno(r, "Failed to open journal: %m"); goto end; } @@ -830,8 +815,8 @@ int main(int argc, char *argv[]) { SET_FOREACH(match, matches, it) { r = sd_journal_add_match(j, match, strlen(match)); if (r != 0) { - log_error("Failed to add match '%s': %s", - match, strerror(-r)); + log_error_errno(r, "Failed to add match '%s': %m", + match); goto end; } } diff --git a/src/journal/journal-authenticate.c b/src/journal/journal-authenticate.c index 5ab1982bf0..f9bd686c11 100644 --- a/src/journal/journal-authenticate.c +++ b/src/journal/journal-authenticate.c @@ -339,7 +339,7 @@ int journal_file_fss_load(JournalFile *f) { fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY, 0600); if (fd < 0) { if (errno != ENOENT) - log_error("Failed to open %s: %m", p); + log_error_errno(errno, "Failed to open %s: %m", p); r = -errno; goto finish; diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index 8a2c0fcac5..c5d2d19433 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -1292,7 +1292,7 @@ void journal_file_post_change(JournalFile *f) { __sync_synchronize(); if (ftruncate(f->fd, f->last_stat.st_size) < 0) - log_error("Failed to truncate file to its own size: %m"); + log_error_errno(errno, "Failed to truncate file to its own size: %m"); } static int entry_item_cmp(const void *_a, const void *_b) { @@ -1657,7 +1657,7 @@ static int generic_array_bisect( } } - if (k > n) { + if (k >= n) { if (direction == DIRECTION_UP) { i = n; subtract_one = true; diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c index bb1ef66dc9..56a96c55dd 100644 --- a/src/journal/journal-send.c +++ b/src/journal/journal-send.c @@ -32,6 +32,7 @@ #include "sd-journal.h" #include "util.h" #include "socket-util.h" +#include "memfd-util.h" #define SNDBUF_SIZE (8*1024*1024) @@ -198,7 +199,7 @@ finish: _public_ int sd_journal_sendv(const struct iovec *iov, int n) { PROTECT_ERRNO; - int fd; + int fd, r; _cleanup_close_ int buffer_fd = -1; struct iovec *w; uint64_t *l; @@ -218,6 +219,7 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) { } control; struct cmsghdr *cmsg; bool have_syslog_identifier = false; + bool seal = true; assert_return(iov, -EINVAL); assert_return(n > 0, -EINVAL); @@ -304,21 +306,36 @@ _public_ int sd_journal_sendv(const struct iovec *iov, int n) { if (errno != EMSGSIZE && errno != ENOBUFS) return -errno; - /* Message doesn't fit... Let's dump the data in a temporary - * file and just pass a file descriptor of it to the other - * side. + /* Message doesn't fit... Let's dump the data in a memfd or + * temporary file and just pass a file descriptor of it to the + * other side. * - * We use /dev/shm instead of /tmp here, since we want this to - * be a tmpfs, and one that is available from early boot on - * and where unprivileged users can create files. */ - buffer_fd = open_tmpfile("/dev/shm", O_RDWR | O_CLOEXEC); - if (buffer_fd < 0) - return buffer_fd; + * For the temporary files we use /dev/shm instead of /tmp + * here, since we want this to be a tmpfs, and one that is + * available from early boot on and where unprivileged users + * can create files. */ + buffer_fd = memfd_new(NULL); + if (buffer_fd < 0) { + if (buffer_fd == -ENOSYS) { + buffer_fd = open_tmpfile("/dev/shm", O_RDWR | O_CLOEXEC); + if (buffer_fd < 0) + return buffer_fd; + + seal = false; + } else + return buffer_fd; + } n = writev(buffer_fd, w, j); if (n < 0) return -errno; + if (seal) { + r = memfd_set_sealed(buffer_fd); + if (r < 0) + return r; + } + mh.msg_iov = NULL; mh.msg_iovlen = 0; @@ -436,13 +453,10 @@ _public_ int sd_journal_stream_fd(const char *identifier, int priority, int leve header[l++] = '0'; header[l++] = '\n'; - r = (int) loop_write(fd, header, l, false); + r = loop_write(fd, header, l, false); if (r < 0) return r; - if ((size_t) r != l) - return -errno; - r = fd; fd = -1; return r; diff --git a/src/journal/journal-vacuum.c b/src/journal/journal-vacuum.c index 7699482a77..4973409848 100644 --- a/src/journal/journal-vacuum.c +++ b/src/journal/journal-vacuum.c @@ -121,29 +121,38 @@ static void patch_realtime( } static int journal_file_empty(int dir_fd, const char *name) { - int r; - le64_t n_entries; _cleanup_close_ int fd; + struct stat st; + le64_t n_entries; + ssize_t n; fd = openat(dir_fd, name, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK); if (fd < 0) return -errno; - if (lseek(fd, offsetof(Header, n_entries), SEEK_SET) < 0) + if (fstat(fd, &st) < 0) return -errno; - r = read(fd, &n_entries, sizeof(n_entries)); - if (r != sizeof(n_entries)) - return r == 0 ? -EINVAL : -errno; + /* If an offline file doesn't even have a header we consider it empty */ + if (st.st_size < (off_t) sizeof(Header)) + return 1; + + /* If the number of entries is empty, we consider it empty, too */ + n = pread(fd, &n_entries, sizeof(n_entries), offsetof(Header, n_entries)); + if (n < 0) + return -errno; + if (n != sizeof(n_entries)) + return -EIO; - return le64toh(n_entries) == 0; + return le64toh(n_entries) <= 0; } int journal_directory_vacuum( const char *directory, uint64_t max_use, usec_t max_retention_usec, - usec_t *oldest_usec) { + usec_t *oldest_usec, + bool verbose) { _cleanup_closedir_ DIR *d = NULL; int r = 0; @@ -152,6 +161,7 @@ int journal_directory_vacuum( size_t n_allocated = 0; uint64_t sum = 0, freed = 0; usec_t retention_limit = 0; + char sbytes[FORMAT_BYTES_MAX]; assert(directory); @@ -262,20 +272,22 @@ int journal_directory_vacuum( uint64_t size = 512UL * (uint64_t) st.st_blocks; if (unlinkat(dirfd(d), p, 0) >= 0) { - log_info("Deleted empty journal %s/%s (%"PRIu64" bytes).", - directory, p, size); + log_full(verbose ? LOG_INFO : LOG_DEBUG, "Deleted empty archived journal %s/%s (%s).", directory, p, format_bytes(sbytes, sizeof(sbytes), size)); freed += size; } else if (errno != ENOENT) - log_warning("Failed to delete %s/%s: %m", directory, p); + log_warning_errno(errno, "Failed to delete empty archived journal %s/%s: %m", directory, p); free(p); - continue; } patch_realtime(directory, p, &st, &realtime); - GREEDY_REALLOC(list, n_allocated, n_list + 1); + if (!GREEDY_REALLOC(list, n_allocated, n_list + 1)) { + free(p); + r = -ENOMEM; + goto finish; + } list[n_list].filename = p; list[n_list].usage = 512UL * (uint64_t) st.st_blocks; @@ -297,8 +309,7 @@ int journal_directory_vacuum( break; if (unlinkat(dirfd(d), list[i].filename, 0) >= 0) { - log_debug("Deleted archived journal %s/%s (%"PRIu64" bytes).", - directory, list[i].filename, list[i].usage); + log_full(verbose ? LOG_INFO : LOG_DEBUG, "Deleted archived journal %s/%s (%s).", directory, list[i].filename, format_bytes(sbytes, sizeof(sbytes), list[i].usage)); freed += list[i].usage; if (list[i].usage < sum) @@ -307,7 +318,7 @@ int journal_directory_vacuum( sum = 0; } else if (errno != ENOENT) - log_warning("Failed to delete %s/%s: %m", directory, list[i].filename); + log_warning_errno(errno, "Failed to delete archived journal %s/%s: %m", directory, list[i].filename); } if (oldest_usec && i < n_list && (*oldest_usec == 0 || list[i].realtime < *oldest_usec)) @@ -318,7 +329,7 @@ finish: free(list[i].filename); free(list); - log_debug("Vacuuming done, freed %"PRIu64" bytes", freed); + log_full(verbose ? LOG_INFO : LOG_DEBUG, "Vacuuming done, freed %s of archived journals on disk.", format_bytes(sbytes, sizeof(sbytes), freed)); return r; } diff --git a/src/journal/journal-vacuum.h b/src/journal/journal-vacuum.h index bc30c3a140..a7fb6f0f0d 100644 --- a/src/journal/journal-vacuum.h +++ b/src/journal/journal-vacuum.h @@ -23,4 +23,4 @@ #include <inttypes.h> -int journal_directory_vacuum(const char *directory, uint64_t max_use, usec_t max_retention_usec, usec_t *oldest_usec); +int journal_directory_vacuum(const char *directory, uint64_t max_use, usec_t max_retention_usec, usec_t *oldest_usec, bool vacuum); diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index f74adcbc89..7e3c974b33 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -825,21 +825,21 @@ int journal_file_verify( data_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC); if (data_fd < 0) { - log_error("Failed to create data file: %m"); + log_error_errno(errno, "Failed to create data file: %m"); r = -errno; goto fail; } entry_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC); if (entry_fd < 0) { - log_error("Failed to create entry file: %m"); + log_error_errno(errno, "Failed to create entry file: %m"); r = -errno; goto fail; } entry_array_fd = open_tmpfile("/var/tmp", O_RDWR | O_CLOEXEC); if (entry_array_fd < 0) { - log_error("Failed to create entry array file: %m"); + log_error_errno(errno, "Failed to create entry array file: %m"); r = -errno; goto fail; } diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index f50faf42ad..b2f6966fca 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -59,6 +59,7 @@ #include "journal-verify.h" #include "journal-authenticate.h" #include "journal-qrcode.h" +#include "journal-vacuum.h" #include "fsprg.h" #include "unit-name.h" #include "catalog.h" @@ -111,6 +112,8 @@ static bool arg_reverse = false; static int arg_journal_type = 0; static const char *arg_root = NULL; static const char *arg_machine = NULL; +static off_t arg_vacuum_size = (off_t) -1; +static usec_t arg_vacuum_time = USEC_INFINITY; static enum { ACTION_SHOW, @@ -124,6 +127,7 @@ static enum { ACTION_UPDATE_CATALOG, ACTION_LIST_BOOTS, ACTION_FLUSH, + ACTION_VACUUM, } arg_action = ACTION_SHOW; typedef struct boot_id_t { @@ -231,14 +235,16 @@ static void help(void) { "\nCommands:\n" " -h --help Show this help text\n" " --version Show package version\n" + " -F --field=FIELD List all values that a specified field takes\n" " --new-id128 Generate a new 128-bit ID\n" - " --header Show journal header information\n" " --disk-usage Show total disk usage of all journal files\n" - " -F --field=FIELD List all values that a specified field takes\n" + " --vacuum-size=BYTES Remove old journals until disk space drops below size\n" + " --vacuum-time=TIME Remove old journals until none left older than\n" + " --flush Flush all journal data from /run into /var\n" + " --header Show journal header information\n" " --list-catalog Show message IDs of all entries in the message catalog\n" " --dump-catalog Show entries in the message catalog\n" " --update-catalog Update the message catalog database\n" - " --flush Flush all journal data from /run into /var\n" #ifdef HAVE_GCRYPT " --setup-keys Generate a new FSS key pair\n" " --verify Verify journal file consistency\n" @@ -276,6 +282,8 @@ static int parse_argv(int argc, char *argv[]) { ARG_FORCE, ARG_UTC, ARG_FLUSH, + ARG_VACUUM_SIZE, + ARG_VACUUM_TIME, }; static const struct option options[] = { @@ -327,6 +335,8 @@ static int parse_argv(int argc, char *argv[]) { { "machine", required_argument, NULL, 'M' }, { "utc", no_argument, NULL, ARG_UTC }, { "flush", no_argument, NULL, ARG_FLUSH }, + { "vacuum-size", required_argument, NULL, ARG_VACUUM_SIZE }, + { "vacuum-time", required_argument, NULL, ARG_VACUUM_TIME }, {} }; @@ -491,10 +501,8 @@ static int parse_argv(int argc, char *argv[]) { case ARG_FILE: r = glob_extend(&arg_file, optarg); - if (r < 0) { - log_error("Failed to add paths: %s", strerror(-r)); - return r; - }; + if (r < 0) + return log_error_errno(r, "Failed to add paths: %m"); break; case ARG_ROOT: @@ -525,6 +533,26 @@ static int parse_argv(int argc, char *argv[]) { arg_action = ACTION_DISK_USAGE; break; + case ARG_VACUUM_SIZE: + r = parse_size(optarg, 1024, &arg_vacuum_size); + if (r < 0) { + log_error("Failed to parse vacuum size: %s", optarg); + return r; + } + + arg_action = ACTION_VACUUM; + break; + + case ARG_VACUUM_TIME: + r = parse_sec(optarg, &arg_vacuum_time); + if (r < 0) { + log_error("Failed to parse vacuum time: %s", optarg); + return r; + } + + arg_action = ACTION_VACUUM; + break; + #ifdef HAVE_GCRYPT case ARG_FORCE: arg_force = true; @@ -682,7 +710,7 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached("Unhandled option"); } - if (arg_follow && !arg_no_tail && arg_lines == ARG_LINES_DEFAULT) + if (arg_follow && !arg_no_tail && !arg_since && arg_lines == ARG_LINES_DEFAULT) arg_lines = 10; if (!!arg_directory + !!arg_file + !!arg_machine > 1) { @@ -719,10 +747,8 @@ static int generate_new_id128(void) { unsigned i; r = sd_id128_randomize(&id); - if (r < 0) { - log_error("Failed to generate ID: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to generate ID: %m"); printf("As string:\n" SD_ID128_FORMAT_STR "\n\n" @@ -768,10 +794,8 @@ static int add_matches(sd_journal *j, char **args) { p = canonicalize_file_name(*i); path = p ? p : *i; - if (stat(path, &st) < 0) { - log_error("Couldn't stat file: %m"); - return -errno; - } + if (stat(path, &st) < 0) + return log_error_errno(errno, "Couldn't stat file: %m"); if (S_ISREG(st.st_mode) && (0111 & st.st_mode)) { if (executable_is_script(path, &interpreter) > 0) { @@ -821,10 +845,8 @@ static int add_matches(sd_journal *j, char **args) { have_term = true; } - if (r < 0) { - log_error("Failed to add match '%s': %s", *i, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add match '%s': %m", *i); } if (!strv_isempty(args) && !have_term) { @@ -1000,7 +1022,7 @@ static int add_boot(sd_journal *j) { r = get_boot_id_by_offset(j, &arg_boot_id, arg_boot_offset); if (r < 0) { if (sd_id128_equal(arg_boot_id, SD_ID128_NULL)) - log_error("Failed to look up boot %+i: %s", arg_boot_offset, strerror(-r)); + log_error_errno(r, "Failed to look up boot %+i: %m", arg_boot_offset); else log_error("Failed to look up boot ID "SD_ID128_FORMAT_STR"%+i: %s", SD_ID128_FORMAT_VAL(arg_boot_id), arg_boot_offset, strerror(-r)); @@ -1010,10 +1032,8 @@ static int add_boot(sd_journal *j) { sd_id128_to_string(arg_boot_id, match + 9); r = sd_journal_add_match(j, match, sizeof(match) - 1); - if (r < 0) { - log_error("Failed to add match: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add match: %m"); r = sd_journal_add_conjunction(j); if (r < 0) @@ -1030,10 +1050,8 @@ static int add_dmesg(sd_journal *j) { return 0; r = sd_journal_add_match(j, "_TRANSPORT=kernel", strlen("_TRANSPORT=kernel")); - if (r < 0) { - log_error("Failed to add match: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add match: %m"); r = sd_journal_add_conjunction(j); if (r < 0) @@ -1233,10 +1251,8 @@ static int add_priorities(sd_journal *j) { match[sizeof(match)-2] = '0' + i; r = sd_journal_add_match(j, match, strlen(match)); - if (r < 0) { - log_error("Failed to add match: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add match: %m"); } r = sd_journal_add_conjunction(j); @@ -1285,10 +1301,8 @@ static int setup_keys(void) { struct stat st; r = stat("/var/log/journal", &st); - if (r < 0 && errno != ENOENT && errno != ENOTDIR) { - log_error("stat(\"%s\") failed: %m", "/var/log/journal"); - return -errno; - } + if (r < 0 && errno != ENOENT && errno != ENOTDIR) + return log_error_errno(errno, "stat(\"%s\") failed: %m", "/var/log/journal"); if (r < 0 || !S_ISDIR(st.st_mode)) { log_error("%s is not a directory, must be using persistent logging for FSS.", @@ -1297,16 +1311,12 @@ static int setup_keys(void) { } r = sd_id128_get_machine(&machine); - if (r < 0) { - log_error("Failed to get machine ID: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get machine ID: %m"); r = sd_id128_get_boot(&boot); - if (r < 0) { - log_error("Failed to get boot ID: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get boot ID: %m"); if (asprintf(&p, "/var/log/journal/" SD_ID128_FORMAT_STR "/fss", SD_ID128_FORMAT_VAL(machine)) < 0) @@ -1316,7 +1326,7 @@ static int setup_keys(void) { if (arg_force) { r = unlink(p); if (r < 0) { - log_error("unlink(\"%s\") failed: %m", p); + log_error_errno(errno, "unlink(\"%s\") failed: %m", p); r = -errno; goto finish; } @@ -1344,7 +1354,7 @@ static int setup_keys(void) { fd = open("/dev/random", O_RDONLY|O_CLOEXEC|O_NOCTTY); if (fd < 0) { - log_error("Failed to open /dev/random: %m"); + log_error_errno(errno, "Failed to open /dev/random: %m"); r = -errno; goto finish; } @@ -1352,7 +1362,7 @@ static int setup_keys(void) { log_info("Generating seed..."); l = loop_read(fd, seed, seed_size, true); if (l < 0 || (size_t) l != seed_size) { - log_error("Failed to read random seed: %s", strerror(EIO)); + log_error_errno(EIO, "Failed to read random seed: %m"); r = -EIO; goto finish; } @@ -1371,7 +1381,7 @@ static int setup_keys(void) { safe_close(fd); fd = mkostemp_safe(k, O_WRONLY|O_CLOEXEC); if (fd < 0) { - log_error("Failed to open %s: %m", k); + log_error_errno(errno, "Failed to open %s: %m", k); r = -errno; goto finish; } @@ -1379,12 +1389,12 @@ static int setup_keys(void) { /* Enable secure remove, exclusion from dump, synchronous * writing and in-place updating */ if (ioctl(fd, FS_IOC_GETFLAGS, &attr) < 0) - log_warning("FS_IOC_GETFLAGS failed: %m"); + log_warning_errno(errno, "FS_IOC_GETFLAGS failed: %m"); attr |= FS_SECRM_FL|FS_NODUMP_FL|FS_SYNC_FL|FS_NOCOW_FL; if (ioctl(fd, FS_IOC_SETFLAGS, &attr) < 0) - log_warning("FS_IOC_SETFLAGS failed: %m"); + log_warning_errno(errno, "FS_IOC_SETFLAGS failed: %m"); zero(h); memcpy(h.signature, "KSHHRHLP", 8); @@ -1396,22 +1406,20 @@ static int setup_keys(void) { h.fsprg_secpar = htole16(FSPRG_RECOMMENDED_SECPAR); h.fsprg_state_size = htole64(state_size); - l = loop_write(fd, &h, sizeof(h), false); - if (l < 0 || (size_t) l != sizeof(h)) { - log_error("Failed to write header: %s", strerror(EIO)); - r = -EIO; + r = loop_write(fd, &h, sizeof(h), false); + if (r < 0) { + log_error_errno(r, "Failed to write header: %m"); goto finish; } - l = loop_write(fd, state, state_size, false); - if (l < 0 || (size_t) l != state_size) { - log_error("Failed to write state: %s", strerror(EIO)); - r = -EIO; + r = loop_write(fd, state, state_size, false); + if (r < 0) { + log_error_errno(r, "Failed to write state: %m"); goto finish; } if (link(k, p) < 0) { - log_error("Failed to link file: %m"); + log_error_errno(errno, "Failed to link file: %m"); r = -errno; goto finish; } @@ -1632,8 +1640,7 @@ static int access_check(sd_journal *j) { assert(err > 0); if (err != EACCES) - log_warning("Error was encountered while opening journal files: %s", - strerror(err)); + log_warning_errno(err, "Error was encountered while opening journal files: %m"); } return r; @@ -1652,10 +1659,8 @@ static int flush_to_var(void) { /* OK, let's actually do the full logic, send SIGUSR1 to the * daemon and set up inotify to wait for the flushed file to appear */ r = bus_open_system_systemd(&bus); - if (r < 0) { - log_error("Failed to get D-Bus connection: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get D-Bus connection: %m"); r = sd_bus_call_method( bus, @@ -1674,37 +1679,27 @@ static int flush_to_var(void) { mkdir_p("/run/systemd/journal", 0755); watch_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); - if (watch_fd < 0) { - log_error("Failed to create inotify watch: %m"); - return -errno; - } + if (watch_fd < 0) + return log_error_errno(errno, "Failed to create inotify watch: %m"); r = inotify_add_watch(watch_fd, "/run/systemd/journal", IN_CREATE|IN_DONT_FOLLOW|IN_ONLYDIR); - if (r < 0) { - log_error("Failed to watch journal directory: %m"); - return -errno; - } + if (r < 0) + return log_error_errno(errno, "Failed to watch journal directory: %m"); for (;;) { if (access("/run/systemd/journal/flushed", F_OK) >= 0) break; - if (errno != ENOENT) { - log_error("Failed to check for existance of /run/systemd/journal/flushed: %m"); - return -errno; - } + if (errno != ENOENT) + return log_error_errno(errno, "Failed to check for existance of /run/systemd/journal/flushed: %m"); r = fd_wait_for_event(watch_fd, POLLIN, USEC_INFINITY); - if (r < 0) { - log_error("Failed to wait for event: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to wait for event: %m"); r = flush_fd(watch_fd); - if (r < 0) { - log_error("Failed to flush inotify events: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to flush inotify events: %m"); } return 0; @@ -1759,7 +1754,7 @@ int main(int argc, char *argv[]) { if (arg_action == ACTION_UPDATE_CATALOG) { r = catalog_update(database, arg_root, catalog_file_dirs); if (r < 0) - log_error("Failed to list catalog: %s", strerror(-r)); + log_error_errno(r, "Failed to list catalog: %m"); } else { bool oneline = arg_action == ACTION_LIST_CATALOG; @@ -1769,7 +1764,7 @@ int main(int argc, char *argv[]) { else r = catalog_list(stdout, database, oneline); if (r < 0) - log_error("Failed to list catalog: %s", strerror(-r)); + log_error_errno(r, "Failed to list catalog: %m"); } goto finish; @@ -1784,9 +1779,8 @@ int main(int argc, char *argv[]) { else r = sd_journal_open(&j, !arg_merge*SD_JOURNAL_LOCAL_ONLY + arg_journal_type); if (r < 0) { - log_error("Failed to open %s: %s", - arg_directory ? arg_directory : arg_file ? "files" : "journal", - strerror(-r)); + log_error_errno(r, "Failed to open %s: %m", + arg_directory ? arg_directory : arg_file ? "files" : "journal"); return EXIT_FAILURE; } @@ -1812,11 +1806,31 @@ int main(int argc, char *argv[]) { if (r < 0) return EXIT_FAILURE; - printf("Journals take up %s on disk.\n", + printf("Archived and active journals take up %s on disk.\n", format_bytes(sbytes, sizeof(sbytes), bytes)); return EXIT_SUCCESS; } + if (arg_action == ACTION_VACUUM) { + Directory *d; + Iterator i; + + HASHMAP_FOREACH(d, j->directories_by_path, i) { + int q; + + if (d->is_root) + continue; + + q = journal_directory_vacuum(d->path, arg_vacuum_size, arg_vacuum_time, NULL, true); + if (q < 0) { + log_error_errno(q, "Failed to vacuum: %m"); + r = q; + } + } + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + } + if (arg_action == ACTION_LIST_BOOTS) { r = list_boots(j); goto finish; @@ -1837,25 +1851,25 @@ int main(int argc, char *argv[]) { strv_free(arg_user_units); if (r < 0) { - log_error("Failed to add filter for units: %s", strerror(-r)); + log_error_errno(r, "Failed to add filter for units: %m"); return EXIT_FAILURE; } r = add_syslog_identifier(j); if (r < 0) { - log_error("Failed to add filter for syslog identifiers: %s", strerror(-r)); + log_error_errno(r, "Failed to add filter for syslog identifiers: %m"); return EXIT_FAILURE; } r = add_priorities(j); if (r < 0) { - log_error("Failed to add filter for priorities: %s", strerror(-r)); + log_error_errno(r, "Failed to add filter for priorities: %m"); return EXIT_FAILURE; } r = add_matches(j, argv + optind); if (r < 0) { - log_error("Failed to add filters: %s", strerror(-r)); + log_error_errno(r, "Failed to add filters: %m"); return EXIT_FAILURE; } @@ -1878,7 +1892,7 @@ int main(int argc, char *argv[]) { r = sd_journal_query_unique(j, arg_field); if (r < 0) { - log_error("Failed to query unique data objects: %s", strerror(-r)); + log_error_errno(r, "Failed to query unique data objects: %m"); return EXIT_FAILURE; } @@ -1910,7 +1924,7 @@ int main(int argc, char *argv[]) { if (arg_cursor || arg_after_cursor) { r = sd_journal_seek_cursor(j, arg_cursor ?: arg_after_cursor); if (r < 0) { - log_error("Failed to seek to cursor: %s", strerror(-r)); + log_error_errno(r, "Failed to seek to cursor: %m"); return EXIT_FAILURE; } if (!arg_reverse) @@ -1918,14 +1932,18 @@ int main(int argc, char *argv[]) { else r = sd_journal_previous_skip(j, 1 + !!arg_after_cursor); - if (arg_after_cursor && r < 2 && !arg_follow) + if (arg_after_cursor && r < 2) { /* We couldn't find the next entry after the cursor. */ - arg_lines = 0; + if (arg_follow) + need_seek = true; + else + arg_lines = 0; + } } else if (arg_since_set && !arg_reverse) { r = sd_journal_seek_realtime_usec(j, arg_since); if (r < 0) { - log_error("Failed to seek to date: %s", strerror(-r)); + log_error_errno(r, "Failed to seek to date: %m"); return EXIT_FAILURE; } r = sd_journal_next(j); @@ -1933,7 +1951,7 @@ int main(int argc, char *argv[]) { } else if (arg_until_set && arg_reverse) { r = sd_journal_seek_realtime_usec(j, arg_until); if (r < 0) { - log_error("Failed to seek to date: %s", strerror(-r)); + log_error_errno(r, "Failed to seek to date: %m"); return EXIT_FAILURE; } r = sd_journal_previous(j); @@ -1941,7 +1959,7 @@ int main(int argc, char *argv[]) { } else if (arg_lines >= 0) { r = sd_journal_seek_tail(j); if (r < 0) { - log_error("Failed to seek to tail: %s", strerror(-r)); + log_error_errno(r, "Failed to seek to tail: %m"); return EXIT_FAILURE; } @@ -1950,7 +1968,7 @@ int main(int argc, char *argv[]) { } else if (arg_reverse) { r = sd_journal_seek_tail(j); if (r < 0) { - log_error("Failed to seek to tail: %s", strerror(-r)); + log_error_errno(r, "Failed to seek to tail: %m"); return EXIT_FAILURE; } @@ -1959,7 +1977,7 @@ int main(int argc, char *argv[]) { } else { r = sd_journal_seek_head(j); if (r < 0) { - log_error("Failed to seek to head: %s", strerror(-r)); + log_error_errno(r, "Failed to seek to head: %m"); return EXIT_FAILURE; } @@ -1967,7 +1985,7 @@ int main(int argc, char *argv[]) { } if (r < 0) { - log_error("Failed to iterate through journal: %s", strerror(-r)); + log_error_errno(r, "Failed to iterate through journal: %m"); return EXIT_FAILURE; } @@ -1980,7 +1998,7 @@ int main(int argc, char *argv[]) { r = sd_journal_get_cutoff_realtime_usec(j, &start, &end); if (r < 0) { - log_error("Failed to get cutoff: %s", strerror(-r)); + log_error_errno(r, "Failed to get cutoff: %m"); goto finish; } @@ -2005,7 +2023,7 @@ int main(int argc, char *argv[]) { else r = sd_journal_previous(j); if (r < 0) { - log_error("Failed to iterate through journal: %s", strerror(-r)); + log_error_errno(r, "Failed to iterate through journal: %m"); goto finish; } if (r == 0) @@ -2017,7 +2035,7 @@ int main(int argc, char *argv[]) { r = sd_journal_get_realtime_usec(j, &usec); if (r < 0) { - log_error("Failed to determine timestamp: %s", strerror(-r)); + log_error_errno(r, "Failed to determine timestamp: %m"); goto finish; } if (usec > arg_until) @@ -2029,7 +2047,7 @@ int main(int argc, char *argv[]) { r = sd_journal_get_realtime_usec(j, &usec); if (r < 0) { - log_error("Failed to determine timestamp: %s", strerror(-r)); + log_error_errno(r, "Failed to determine timestamp: %m"); goto finish; } if (usec < arg_since) @@ -2074,7 +2092,7 @@ int main(int argc, char *argv[]) { r = sd_journal_get_cursor(j, &cursor); if (r < 0 && r != -EADDRNOTAVAIL) - log_error("Failed to get cursor: %s", strerror(-r)); + log_error_errno(r, "Failed to get cursor: %m"); else if (r >= 0) printf("-- cursor: %s\n", cursor); } @@ -2084,7 +2102,7 @@ int main(int argc, char *argv[]) { r = sd_journal_wait(j, (uint64_t) -1); if (r < 0) { - log_error("Couldn't wait for journal event: %s", strerror(-r)); + log_error_errno(r, "Couldn't wait for journal event: %m"); goto finish; } diff --git a/src/journal/journald-audit.c b/src/journal/journald-audit.c new file mode 100644 index 0000000000..69742fa59c --- /dev/null +++ b/src/journal/journald-audit.c @@ -0,0 +1,551 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2014 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 "missing.h" +#include "journald-audit.h" + +typedef struct MapField { + const char *audit_field; + const char *journal_field; + int (*map)(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov); +} MapField; + +static int map_simple_field(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) { + _cleanup_free_ char *c = NULL; + size_t l = 0, allocated = 0; + const char *e; + + assert(field); + assert(p); + assert(iov); + assert(n_iov); + + l = strlen(field); + allocated = l + 1; + c = malloc(allocated); + if (!c) + return -ENOMEM; + + memcpy(c, field, l); + for (e = *p; *e != ' ' && *e != 0; e++) { + if (!GREEDY_REALLOC(c, allocated, l+2)) + return -ENOMEM; + + c[l++] = *e; + } + + c[l] = 0; + + if (!GREEDY_REALLOC(*iov, *n_iov_allocated, *n_iov + 1)) + return -ENOMEM; + + (*iov)[*n_iov].iov_base = c; + (*iov)[*n_iov].iov_len = l; + (*n_iov) ++; + + *p = e; + c = NULL; + + return 1; +} + +static int map_string_field_internal(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov, bool filter_printable) { + _cleanup_free_ char *c = NULL; + const char *s, *e; + size_t l; + + assert(field); + assert(p); + assert(iov); + assert(n_iov); + + /* The kernel formats string fields in one of two formats. */ + + if (**p == '"') { + /* Normal quoted syntax */ + s = *p + 1; + e = strchr(s, '"'); + if (!e) + return 0; + + l = strlen(field) + (e - s); + c = malloc(l+1); + if (!c) + return -ENOMEM; + + *((char*) mempcpy(stpcpy(c, field), s, e - s)) = 0; + + e += 1; + + } else if (unhexchar(**p) >= 0) { + /* Hexadecimal escaping */ + size_t allocated = 0; + + l = strlen(field); + allocated = l + 2; + c = malloc(allocated); + if (!c) + return -ENOMEM; + + memcpy(c, field, l); + for (e = *p; *e != ' ' && *e != 0; e += 2) { + int a, b; + uint8_t x; + + a = unhexchar(e[0]); + if (a < 0) + return 0; + + b = unhexchar(e[1]); + if (b < 0) + return 0; + + x = ((uint8_t) a << 4 | (uint8_t) b); + + if (filter_printable && x < (uint8_t) ' ') + x = (uint8_t) ' '; + + if (!GREEDY_REALLOC(c, allocated, l+2)) + return -ENOMEM; + + c[l++] = (char) x; + } + + c[l] = 0; + } else + return 0; + + if (!GREEDY_REALLOC(*iov, *n_iov_allocated, *n_iov + 1)) + return -ENOMEM; + + (*iov)[*n_iov].iov_base = c; + (*iov)[*n_iov].iov_len = l; + (*n_iov) ++; + + *p = e; + c = NULL; + + return 1; +} + +static int map_string_field(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) { + return map_string_field_internal(field, p, iov, n_iov_allocated, n_iov, false); +} + +static int map_string_field_printable(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) { + return map_string_field_internal(field, p, iov, n_iov_allocated, n_iov, true); +} + +static int map_generic_field(const char *prefix, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) { + const char *e, *f; + char *c, *t; + int r; + + /* Implements fallback mappings for all fields we don't know */ + + for (e = *p; e < *p + 16; e++) { + + if (*e == 0 || *e == ' ') + return 0; + + if (*e == '=') + break; + + if (!((*e >= 'a' && *e <= 'z') || + (*e >= 'A' && *e <= 'Z') || + (*e >= '0' && *e <= '9') || + *e == '_' || *e == '-')) + return 0; + } + + if (e <= *p || e >= *p + 16) + return 0; + + c = alloca(strlen(prefix) + (e - *p) + 2); + + t = stpcpy(c, prefix); + for (f = *p; f < e; f++) { + char x; + + if (*f >= 'a' && *f <= 'z') + x = (*f - 'a') + 'A'; /* uppercase */ + else if (*f == '-') + x = '_'; /* dashes → underscores */ + else + x = *f; + + *(t++) = x; + } + strcpy(t, "="); + + e ++; + + r = map_simple_field(c, &e, iov, n_iov_allocated, n_iov); + if (r < 0) + return r; + + *p = e; + return r; +} + +/* Kernel fields are those occuring in the audit string before + * msg='. All of these fields are trusted, hence carry the "_" prefix. + * We try to translate the fields we know into our native names. The + * other's are generically mapped to _AUDIT_FIELD_XYZ= */ +static const MapField map_fields_kernel[] = { + + /* First, we map certain well-known audit fields into native + * well-known fields */ + { "pid=", "_PID=", map_simple_field }, + { "ppid=", "_PPID=", map_simple_field }, + { "uid=", "_UID=", map_simple_field }, + { "euid=", "_EUID=", map_simple_field }, + { "fsuid=", "_FSUID=", map_simple_field }, + { "gid=", "_GID=", map_simple_field }, + { "egid=", "_EGID=", map_simple_field }, + { "fsgid=", "_FSGID=", map_simple_field }, + { "tty=", "_TTY=", map_simple_field }, + { "ses=", "_AUDIT_SESSION=", map_simple_field }, + { "auid=", "_AUDIT_LOGINUID=", map_simple_field }, + { "subj=", "_SELINUX_CONTEXT=", map_simple_field }, + { "comm=", "_COMM=", map_string_field }, + { "exe=", "_EXE=", map_string_field }, + { "proctitle=", "_CMDLINE=", map_string_field_printable }, + + /* Some fields don't map to native well-known fields. However, + * we know that they are string fields, hence let's undo + * string field escaping for them, though we stick to the + * generic field names. */ + { "path=", "_AUDIT_FIELD_PATH=", map_string_field }, + { "dev=", "_AUDIT_FIELD_DEV=", map_string_field }, + { "name=", "_AUDIT_FIELD_NAME=", map_string_field }, + {} +}; + +/* Userspace fields are thos occuring in the audit string after + * msg='. All of these fields are untrusted, hence carry no "_" + * prefix. We map the fields we don't know to AUDIT_FIELD_XYZ= */ +static const MapField map_fields_userspace[] = { + { "cwd=", "AUDIT_FIELD_CWD=", map_string_field }, + { "cmd=", "AUDIT_FIELD_CMD=", map_string_field }, + { "acct=", "AUDIT_FIELD_ACCT=", map_string_field }, + { "exe=", "AUDIT_FIELD_EXE=", map_string_field }, + { "comm=", "AUDIT_FIELD_COMM=", map_string_field }, + {} +}; + +static int map_all_fields( + const char *p, + const MapField map_fields[], + const char *prefix, + bool handle_msg, + struct iovec **iov, + size_t *n_iov_allocated, + unsigned *n_iov) { + + int r; + + assert(p); + assert(iov); + assert(n_iov_allocated); + assert(n_iov); + + for (;;) { + bool mapped = false; + const MapField *m; + const char *v; + + p += strspn(p, WHITESPACE); + + if (*p == 0) + return 0; + + if (handle_msg) { + v = startswith(p, "msg='"); + if (v) { + const char *e; + char *c; + + /* Userspace message. It's enclosed in + simple quotation marks, is not + escaped, but the last field in the + line, hence let's remove the + quotation mark, and apply the + userspace mapping instead of the + kernel mapping. */ + + e = endswith(v, "'"); + if (!e) + return 0; /* don't continue splitting up if the final quotation mark is missing */ + + c = strndupa(v, e - v); + return map_all_fields(c, map_fields_userspace, "AUDIT_FIELD_", false, iov, n_iov_allocated, n_iov); + } + } + + /* Try to map the kernel fields to our own names */ + for (m = map_fields; m->audit_field; m++) { + v = startswith(p, m->audit_field); + if (!v) + continue; + + r = m->map(m->journal_field, &v, iov, n_iov_allocated, n_iov); + if (r < 0) + return log_debug_errno(r, "Failed to parse audit array: %m"); + + if (r > 0) { + mapped = true; + p = v; + break; + } + } + + if (!mapped) { + r = map_generic_field(prefix, &p, iov, n_iov_allocated, n_iov); + if (r < 0) + return log_debug_errno(r, "Failed to parse audit array: %m"); + + if (r == 0) { + /* Couldn't process as generic field, let's just skip over it */ + p += strcspn(p, WHITESPACE); + } + } + } +} + +static void process_audit_string(Server *s, int type, const char *data, size_t size) { + _cleanup_free_ struct iovec *iov = NULL; + size_t n_iov_allocated = 0; + unsigned n_iov = 0, k; + uint64_t seconds, msec, id; + const char *p; + unsigned z; + char id_field[sizeof("_AUDIT_ID=") + DECIMAL_STR_MAX(uint64_t)], + type_field[sizeof("_AUDIT_TYPE=") + DECIMAL_STR_MAX(int)], + source_time_field[sizeof("_SOURCE_REALTIME_TIMESTAMP=") + DECIMAL_STR_MAX(usec_t)]; + char *m; + + assert(s); + + if (size <= 0) + return; + + if (!data) + return; + + /* Note that the input buffer is NUL terminated, but let's + * check whether there is a spurious NUL byte */ + if (memchr(data, 0, size)) + return; + + p = startswith(data, "audit"); + if (!p) + return; + + if (sscanf(p, "(%" PRIi64 ".%" PRIi64 ":%" PRIi64 "):%n", + &seconds, + &msec, + &id, + &k) != 3) + return; + + p += k; + p += strspn(p, WHITESPACE); + + if (isempty(p)) + return; + + n_iov_allocated = N_IOVEC_META_FIELDS + 5; + iov = new(struct iovec, n_iov_allocated); + if (!iov) { + log_oom(); + return; + } + + IOVEC_SET_STRING(iov[n_iov++], "_TRANSPORT=audit"); + + sprintf(source_time_field, "_SOURCE_REALTIME_TIMESTAMP=%" PRIu64, + (usec_t) seconds * USEC_PER_SEC + (usec_t) msec * USEC_PER_MSEC); + IOVEC_SET_STRING(iov[n_iov++], source_time_field); + + sprintf(type_field, "_AUDIT_TYPE=%i", type); + IOVEC_SET_STRING(iov[n_iov++], type_field); + + sprintf(id_field, "_AUDIT_ID=%" PRIu64, id); + IOVEC_SET_STRING(iov[n_iov++], id_field); + + m = alloca(strlen("MESSAGE=<audit-") + DECIMAL_STR_MAX(int) + strlen("> ") + strlen(p) + 1); + sprintf(m, "MESSAGE=<audit-%i> %s", type, p); + IOVEC_SET_STRING(iov[n_iov++], m); + + z = n_iov; + + map_all_fields(p, map_fields_kernel, "_AUDIT_FIELD_", true, &iov, &n_iov_allocated, &n_iov); + + if (!GREEDY_REALLOC(iov, n_iov_allocated, n_iov + N_IOVEC_META_FIELDS)) { + log_oom(); + goto finish; + } + + server_dispatch_message(s, iov, n_iov, n_iov_allocated, NULL, NULL, NULL, 0, NULL, LOG_NOTICE, 0); + +finish: + /* free() all entries that map_all_fields() added. All others + * are allocated on the stack or are constant. */ + + for (; z < n_iov; z++) + free(iov[z].iov_base); +} + +void server_process_audit_message( + Server *s, + const void *buffer, + size_t buffer_size, + const struct ucred *ucred, + const union sockaddr_union *sa, + socklen_t salen) { + + const struct nlmsghdr *nl = buffer; + + assert(s); + + if (buffer_size < ALIGN(sizeof(struct nlmsghdr))) + return; + + assert(buffer); + + /* Filter out fake data */ + if (!sa || + salen != sizeof(struct sockaddr_nl) || + sa->nl.nl_family != AF_NETLINK || + sa->nl.nl_pid != 0) { + log_debug("Audit netlink message from invalid sender."); + return; + } + + if (!ucred || ucred->pid != 0) { + log_debug("Audit netlink message with invalid credentials."); + return; + } + + if (!NLMSG_OK(nl, buffer_size)) { + log_error("Audit netlink message truncated."); + return; + } + + /* Ignore special Netlink messages */ + if (IN_SET(nl->nlmsg_type, NLMSG_NOOP, NLMSG_ERROR)) + return; + + /* Below AUDIT_FIRST_USER_MSG theer are only control messages, let's ignore those */ + if (nl->nlmsg_type < AUDIT_FIRST_USER_MSG) + return; + + process_audit_string(s, nl->nlmsg_type, NLMSG_DATA(nl), nl->nlmsg_len - ALIGN(sizeof(struct nlmsghdr))); +} + +static int enable_audit(int fd, bool b) { + struct { + union { + struct nlmsghdr header; + uint8_t header_space[NLMSG_HDRLEN]; + }; + struct audit_status body; + } _packed_ request = { + .header.nlmsg_len = NLMSG_LENGTH(sizeof(struct audit_status)), + .header.nlmsg_type = AUDIT_SET, + .header.nlmsg_flags = NLM_F_REQUEST, + .header.nlmsg_seq = 1, + .header.nlmsg_pid = 0, + .body.mask = AUDIT_STATUS_ENABLED, + .body.enabled = b, + }; + union sockaddr_union sa = { + .nl.nl_family = AF_NETLINK, + .nl.nl_pid = 0, + }; + struct iovec iovec = { + .iov_base = &request, + .iov_len = NLMSG_LENGTH(sizeof(struct audit_status)), + }; + struct msghdr mh = { + .msg_iov = &iovec, + .msg_iovlen = 1, + .msg_name = &sa.sa, + .msg_namelen = sizeof(sa.nl), + }; + + ssize_t n; + + n = sendmsg(fd, &mh, MSG_NOSIGNAL); + if (n < 0) + return -errno; + if (n != NLMSG_LENGTH(sizeof(struct audit_status))) + return -EIO; + + /* We don't wait for the result here, we can't do anything + * about it anyway */ + + return 0; +} + +int server_open_audit(Server *s) { + static const int one = 1; + int r; + + if (s->audit_fd < 0) { + static const union sockaddr_union sa = { + .nl.nl_family = AF_NETLINK, + .nl.nl_pid = 0, + .nl.nl_groups = AUDIT_NLGRP_READLOG, + }; + + s->audit_fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_AUDIT); + if (s->audit_fd < 0) { + if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT) + log_debug("Audit not supported in the kernel."); + else + log_warning_errno(errno, "Failed to create audit socket, ignoring: %m"); + + return 0; + } + + r = bind(s->audit_fd, &sa.sa, sizeof(sa.nl)); + if (r < 0) + return log_error_errno(errno, "Failed to join audit multicast group: %m"); + } else + fd_nonblock(s->audit_fd, 1); + + r = setsockopt(s->audit_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); + if (r < 0) + return log_error_errno(errno, "Failed to set SO_PASSCRED on audit socket: %m"); + + r = sd_event_add_io(s->event, &s->audit_event_source, s->audit_fd, EPOLLIN, process_datagram, s); + if (r < 0) + return log_error_errno(r, "Failed to add audit fd to event loop: %m"); + + /* We are listening now, try to enable audit */ + r = enable_audit(s->audit_fd, true); + if (r < 0) + log_warning_errno(r, "Failed to issue audit enable call: %m"); + + return 0; +} diff --git a/src/journal/journald-audit.h b/src/journal/journald-audit.h new file mode 100644 index 0000000000..68cdfb3410 --- /dev/null +++ b/src/journal/journald-audit.h @@ -0,0 +1,29 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 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 "socket-util.h" +#include "journald-server.h" + +void server_process_audit_message(Server *s, const void *buffer, size_t buffer_size, const struct ucred *ucred, const union sockaddr_union *sa, socklen_t salen); + +int server_open_audit(Server*s); diff --git a/src/journal/journald-console.c b/src/journal/journald-console.c index 6ec2528d74..4afa6ef9c3 100644 --- a/src/journal/journald-console.c +++ b/src/journal/journald-console.c @@ -48,7 +48,7 @@ void server_forward_console( int priority, const char *identifier, const char *message, - struct ucred *ucred) { + const struct ucred *ucred) { struct iovec iovec[5]; char header_pid[16]; @@ -100,12 +100,12 @@ void server_forward_console( fd = open_terminal(tty, O_WRONLY|O_NOCTTY|O_CLOEXEC); if (fd < 0) { - log_debug("Failed to open %s for logging: %m", tty); + log_debug_errno(errno, "Failed to open %s for logging: %m", tty); return; } if (writev(fd, iovec, n) < 0) - log_debug("Failed to write to %s for logging: %m", tty); + log_debug_errno(errno, "Failed to write to %s for logging: %m", tty); safe_close(fd); } diff --git a/src/journal/journald-console.h b/src/journal/journald-console.h index aa8e6579ba..d8af2267e1 100644 --- a/src/journal/journald-console.h +++ b/src/journal/journald-console.h @@ -23,4 +23,4 @@ #include "journald-server.h" -void server_forward_console(Server *s, int priority, const char *identifier, const char *message, struct ucred *ucred); +void server_forward_console(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred); diff --git a/src/journal/journald-kmsg.c b/src/journal/journald-kmsg.c index fb8ea08e31..aca4571ece 100644 --- a/src/journal/journald-kmsg.c +++ b/src/journal/journald-kmsg.c @@ -37,7 +37,7 @@ void server_forward_kmsg( int priority, const char *identifier, const char *message, - struct ucred *ucred) { + const struct ucred *ucred) { struct iovec iovec[5]; char header_priority[6], header_pid[16]; @@ -88,7 +88,7 @@ void server_forward_kmsg( IOVEC_SET_STRING(iovec[n++], "\n"); if (writev(s->dev_kmsg_fd, iovec, n) < 0) - log_debug("Failed to write to /dev/kmsg for logging: %m"); + log_debug_errno(errno, "Failed to write to /dev/kmsg for logging: %m"); free(ident_buf); } @@ -104,7 +104,7 @@ static bool is_us(const char *pid) { return t == getpid(); } -static void dev_kmsg_record(Server *s, char *p, size_t l) { +static void dev_kmsg_record(Server *s, const char *p, size_t l) { struct iovec iovec[N_IOVEC_META_FIELDS + 7 + N_IOVEC_KERNEL_FIELDS + 2 + N_IOVEC_UDEV_FIELDS]; char *message = NULL, *syslog_priority = NULL, *syslog_pid = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *source_time = NULL; int priority, r; @@ -342,7 +342,7 @@ static int server_read_dev_kmsg(Server *s) { if (errno == EAGAIN || errno == EINTR || errno == EPIPE) return 0; - log_error("Failed to read from kernel: %m"); + log_error_errno(errno, "Failed to read from kernel: %m"); return -errno; } @@ -413,13 +413,13 @@ int server_open_dev_kmsg(Server *s) { goto fail; } - log_error("Failed to add /dev/kmsg fd to event loop: %s", strerror(-r)); + log_error_errno(r, "Failed to add /dev/kmsg fd to event loop: %m"); goto fail; } r = sd_event_source_set_priority(s->dev_kmsg_event_source, SD_EVENT_PRIORITY_IMPORTANT+10); if (r < 0) { - log_error("Failed to adjust priority of kmsg event source: %s", strerror(-r)); + log_error_errno(r, "Failed to adjust priority of kmsg event source: %m"); goto fail; } @@ -446,18 +446,18 @@ int server_open_kernel_seqnum(Server *s) { fd = open("/run/systemd/journal/kernel-seqnum", O_RDWR|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0644); if (fd < 0) { - log_error("Failed to open /run/systemd/journal/kernel-seqnum, ignoring: %m"); + log_error_errno(errno, "Failed to open /run/systemd/journal/kernel-seqnum, ignoring: %m"); return 0; } if (posix_fallocate(fd, 0, sizeof(uint64_t)) < 0) { - log_error("Failed to allocate sequential number file, ignoring: %m"); + log_error_errno(errno, "Failed to allocate sequential number file, ignoring: %m"); return 0; } p = mmap(NULL, sizeof(uint64_t), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (p == MAP_FAILED) { - log_error("Failed to map sequential number file, ignoring: %m"); + log_error_errno(errno, "Failed to map sequential number file, ignoring: %m"); return 0; } diff --git a/src/journal/journald-kmsg.h b/src/journal/journald-kmsg.h index f60f605572..9a9d089967 100644 --- a/src/journal/journald-kmsg.h +++ b/src/journal/journald-kmsg.h @@ -26,6 +26,6 @@ int server_open_dev_kmsg(Server *s); int server_flush_dev_kmsg(Server *s); -void server_forward_kmsg(Server *s, int priority, const char *identifier, const char *message, struct ucred *ucred); +void server_forward_kmsg(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred); int server_open_kernel_seqnum(Server *s); diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c index a6352022dd..f982696255 100644 --- a/src/journal/journald-native.c +++ b/src/journal/journald-native.c @@ -22,6 +22,7 @@ #include <unistd.h> #include <stddef.h> #include <sys/epoll.h> +#include <sys/mman.h> #include "socket-util.h" #include "path-util.h" @@ -32,6 +33,7 @@ #include "journald-console.h" #include "journald-syslog.h" #include "journald-wall.h" +#include "memfd-util.h" bool valid_user_field(const char *p, size_t l, bool allow_protected) { const char *a; @@ -68,15 +70,15 @@ bool valid_user_field(const char *p, size_t l, bool allow_protected) { return true; } -static bool allow_object_pid(struct ucred *ucred) { +static bool allow_object_pid(const struct ucred *ucred) { return ucred && ucred->uid == 0; } void server_process_native_message( Server *s, const void *buffer, size_t buffer_size, - struct ucred *ucred, - struct timeval *tv, + const struct ucred *ucred, + const struct timeval *tv, const char *label, size_t label_len) { struct iovec *iovec = NULL; @@ -301,22 +303,32 @@ finish: void server_process_native_file( Server *s, int fd, - struct ucred *ucred, - struct timeval *tv, + const struct ucred *ucred, + const struct timeval *tv, const char *label, size_t label_len) { struct stat st; - _cleanup_free_ void *p = NULL; - ssize_t n; + bool sealed; int r; + /* Data is in the passed fd, since it didn't fit in a + * datagram. */ + assert(s); assert(fd >= 0); - if (!ucred || ucred->uid != 0) { + /* If it's a memfd, check if it is sealed. If so, we can just + * use map it and use it, and do not need to copy the data + * out. */ + sealed = memfd_get_sealed(fd) > 0; + + if (!sealed && (!ucred || ucred->uid != 0)) { _cleanup_free_ char *sl = NULL, *k = NULL; const char *e; + /* If this is not a sealed memfd, and the peer is unknown or + * unprivileged, then verify the path. */ + if (asprintf(&sl, "/proc/self/fd/%i", fd) < 0) { log_oom(); return; @@ -324,7 +336,7 @@ void server_process_native_file( r = readlink_malloc(sl, &k); if (r < 0) { - log_error("readlink(%s) failed: %m", sl); + log_error_errno(errno, "readlink(%s) failed: %m", sl); return; } @@ -344,13 +356,8 @@ void server_process_native_file( } } - /* Data is in the passed file, since it didn't fit in a - * datagram. We can't map the file here, since clients might - * then truncate it and trigger a SIGBUS for us. So let's - * stupidly read it */ - if (fstat(fd, &st) < 0) { - log_error("Failed to stat passed file, ignoring: %m"); + log_error_errno(errno, "Failed to stat passed file, ignoring: %m"); return; } @@ -367,21 +374,46 @@ void server_process_native_file( return; } - p = malloc(st.st_size); - if (!p) { - log_oom(); - return; - } + if (sealed) { + void *p; + size_t ps; + + /* The file is sealed, we can just map it and use it. */ - n = pread(fd, p, st.st_size, 0); - if (n < 0) - log_error("Failed to read file, ignoring: %s", strerror(-n)); - else if (n > 0) - server_process_native_message(s, p, n, ucred, tv, label, label_len); + ps = PAGE_ALIGN(st.st_size); + p = mmap(NULL, ps, PROT_READ, MAP_PRIVATE, fd, 0); + if (p == MAP_FAILED) { + log_error_errno(errno, "Failed to map memfd, ignoring: %m"); + return; + } + + server_process_native_message(s, p, st.st_size, ucred, tv, label, label_len); + assert_se(munmap(p, ps) >= 0); + } else { + _cleanup_free_ void *p = NULL; + ssize_t n; + + /* The file is not sealed, we can't map the file here, since + * clients might then truncate it and trigger a SIGBUS for + * us. So let's stupidly read it */ + + p = malloc(st.st_size); + if (!p) { + log_oom(); + return; + } + + n = pread(fd, p, st.st_size, 0); + if (n < 0) + log_error_errno(n, "Failed to read file, ignoring: %m"); + else if (n > 0) + server_process_native_message(s, p, n, ucred, tv, label, label_len); + } } int server_open_native_socket(Server*s) { - int one, r; + static const int one = 1; + int r; assert(s); @@ -392,51 +424,38 @@ int server_open_native_socket(Server*s) { }; s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (s->native_fd < 0) { - log_error("socket() failed: %m"); - return -errno; - } + if (s->native_fd < 0) + return log_error_errno(errno, "socket() failed: %m"); unlink(sa.un.sun_path); r = bind(s->native_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)); - if (r < 0) { - log_error("bind(%s) failed: %m", sa.un.sun_path); - return -errno; - } + if (r < 0) + return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); chmod(sa.un.sun_path, 0666); } else fd_nonblock(s->native_fd, 1); - one = 1; r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); - if (r < 0) { - log_error("SO_PASSCRED failed: %m"); - return -errno; - } + if (r < 0) + return log_error_errno(errno, "SO_PASSCRED failed: %m"); #ifdef HAVE_SELINUX if (mac_selinux_use()) { - one = 1; r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one)); if (r < 0) - log_warning("SO_PASSSEC failed: %m"); + log_warning_errno(errno, "SO_PASSSEC failed: %m"); } #endif - one = 1; r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)); - if (r < 0) { - log_error("SO_TIMESTAMP failed: %m"); - return -errno; - } + if (r < 0) + return log_error_errno(errno, "SO_TIMESTAMP failed: %m"); r = sd_event_add_io(s->event, &s->native_event_source, s->native_fd, EPOLLIN, process_datagram, s); - if (r < 0) { - log_error("Failed to add native server fd to event loop: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add native server fd to event loop: %m"); return 0; } diff --git a/src/journal/journald-native.h b/src/journal/journald-native.h index e82a5b87d5..2f9d458fb5 100644 --- a/src/journal/journald-native.h +++ b/src/journal/journald-native.h @@ -30,8 +30,8 @@ bool valid_user_field(const char *p, size_t l, bool allow_protected); -void server_process_native_message(Server *s, const void *buffer, size_t buffer_size, struct ucred *ucred, struct timeval *tv, const char *label, size_t label_len); +void server_process_native_message(Server *s, const void *buffer, size_t buffer_size, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len); -void server_process_native_file(Server *s, int fd, struct ucred *ucred, struct timeval *tv, const char *label, size_t label_len); +void server_process_native_file(Server *s, int fd, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len); int server_open_native_socket(Server*s); diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 12735c4b81..80c9736420 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -50,6 +50,7 @@ #include "journald-stream.h" #include "journald-console.h" #include "journald-native.h" +#include "journald-audit.h" #include "journald-server.h" #ifdef HAVE_ACL @@ -203,7 +204,7 @@ void server_fix_perms(Server *s, JournalFile *f, uid_t uid) { r = fchmod(f->fd, 0640); if (r < 0) - log_warning("Failed to fix access mode on %s, ignoring: %s", f->path, strerror(-r)); + log_warning_errno(r, "Failed to fix access mode on %s, ignoring: %m", f->path); #ifdef HAVE_ACL if (uid <= SYSTEM_UID_MAX) @@ -211,7 +212,7 @@ void server_fix_perms(Server *s, JournalFile *f, uid_t uid) { acl = acl_get_fd(f->fd); if (!acl) { - log_warning("Failed to read ACL on %s, ignoring: %m", f->path); + log_warning_errno(errno, "Failed to read ACL on %s, ignoring: %m", f->path); return; } @@ -221,7 +222,7 @@ void server_fix_perms(Server *s, JournalFile *f, uid_t uid) { if (acl_create_entry(&acl, &entry) < 0 || acl_set_tag_type(entry, ACL_USER) < 0 || acl_set_qualifier(entry, &uid) < 0) { - log_warning("Failed to patch ACL on %s, ignoring: %m", f->path); + log_warning_errno(errno, "Failed to patch ACL on %s, ignoring: %m", f->path); goto finish; } } @@ -231,12 +232,12 @@ void server_fix_perms(Server *s, JournalFile *f, uid_t uid) { if (acl_get_permset(entry, &permset) < 0 || acl_add_perm(permset, ACL_READ) < 0 || calc_acl_mask_if_needed(&acl) < 0) { - log_warning("Failed to patch ACL on %s, ignoring: %m", f->path); + log_warning_errno(errno, "Failed to patch ACL on %s, ignoring: %m", f->path); goto finish; } if (acl_set_fd(f->fd, acl) < 0) - log_warning("Failed to set ACL on %s, ignoring: %m", f->path); + log_warning_errno(errno, "Failed to set ACL on %s, ignoring: %m", f->path); finish: acl_free(acl); @@ -307,11 +308,11 @@ static int do_rotate(Server *s, JournalFile **f, const char* name, r = journal_file_rotate(f, s->compress, seal); if (r < 0) if (*f) - log_error("Failed to rotate %s: %s", - (*f)->path, strerror(-r)); + log_error_errno(r, "Failed to rotate %s: %m", + (*f)->path); else - log_error("Failed to create new %s journal: %s", - name, strerror(-r)); + log_error_errno(r, "Failed to create new %s journal: %m", + name); else server_fix_perms(s, *f, uid); return r; @@ -347,19 +348,19 @@ void server_sync(Server *s) { if (s->system_journal) { r = journal_file_set_offline(s->system_journal); if (r < 0) - log_error("Failed to sync system journal: %s", strerror(-r)); + log_error_errno(r, "Failed to sync system journal: %m"); } ORDERED_HASHMAP_FOREACH_KEY(f, k, s->user_journals, i) { r = journal_file_set_offline(f); if (r < 0) - log_error("Failed to sync user journal: %s", strerror(-r)); + log_error_errno(r, "Failed to sync user journal: %m"); } if (s->sync_event_source) { r = sd_event_source_set_enabled(s->sync_event_source, SD_EVENT_OFF); if (r < 0) - log_error("Failed to disable sync timer source: %s", strerror(-r)); + log_error_errno(r, "Failed to disable sync timer source: %m"); } s->sync_scheduled = false; @@ -374,9 +375,9 @@ static void do_vacuum(Server *s, char *ids, JournalFile *f, const char* path, return; p = strappenda(path, ids); - r = journal_directory_vacuum(p, metrics->max_use, s->max_retention_usec, &s->oldest_file_usec); + r = journal_directory_vacuum(p, metrics->max_use, s->max_retention_usec, &s->oldest_file_usec, false); if (r < 0 && r != -ENOENT) - log_error("Failed to vacuum %s: %s", p, strerror(-r)); + log_error_errno(r, "Failed to vacuum %s: %m", p); } void server_vacuum(Server *s) { @@ -390,7 +391,7 @@ void server_vacuum(Server *s) { r = sd_id128_get_machine(&machine); if (r < 0) { - log_error("Failed to get machine ID: %s", strerror(-r)); + log_error_errno(r, "Failed to get machine ID: %m"); return; } sd_id128_to_string(machine, ids); @@ -510,7 +511,7 @@ static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, unsigned for (i = 0; i < n; i++) size += iovec[i].iov_len; - log_error("Failed to write entry (%d items, %zu bytes), ignoring: %s", n, size, strerror(-r)); + log_error_errno(r, "Failed to write entry (%d items, %zu bytes), ignoring: %m", n, size); return; } @@ -529,7 +530,7 @@ static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, unsigned for (i = 0; i < n; i++) size += iovec[i].iov_len; - log_error("Failed to write entry (%d items, %zu bytes) despite vacuuming, ignoring: %s", n, size, strerror(-r)); + log_error_errno(r, "Failed to write entry (%d items, %zu bytes) despite vacuuming, ignoring: %m", n, size); } else server_schedule_sync(s, priority); } @@ -537,8 +538,8 @@ static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, unsigned static void dispatch_message_real( Server *s, struct iovec *iovec, unsigned n, unsigned m, - struct ucred *ucred, - struct timeval *tv, + const struct ucred *ucred, + const struct timeval *tv, const char *label, size_t label_len, const char *unit_id, int priority, @@ -840,7 +841,7 @@ void server_driver_message(Server *s, sd_id128_t message_id, const char *format, IOVEC_SET_STRING(iovec[n++], buffer); if (!sd_id128_equal(message_id, SD_ID128_NULL)) { - snprintf(mid, sizeof(mid), MESSAGE_ID(message_id)); + snprintf(mid, sizeof(mid), LOG_MESSAGE_ID(message_id)); char_array_0(mid); IOVEC_SET_STRING(iovec[n++], mid); } @@ -855,8 +856,8 @@ void server_driver_message(Server *s, sd_id128_t message_id, const char *format, void server_dispatch_message( Server *s, struct iovec *iovec, unsigned n, unsigned m, - struct ucred *ucred, - struct timeval *tv, + const struct ucred *ucred, + const struct timeval *tv, const char *label, size_t label_len, const char *unit_id, int priority, @@ -926,10 +927,8 @@ static int system_journal_open(Server *s, bool flush_requested) { char ids[33]; r = sd_id128_get_machine(&machine); - if (r < 0) { - log_error("Failed to get machine id: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get machine id: %m"); sd_id128_to_string(machine, ids); @@ -957,7 +956,7 @@ static int system_journal_open(Server *s, bool flush_requested) { server_fix_perms(s, s->system_journal, 0); else if (r < 0) { if (r != -ENOENT && r != -EROFS) - log_warning("Failed to open system journal: %s", strerror(-r)); + log_warning_errno(r, "Failed to open system journal: %m"); r = 0; } @@ -981,7 +980,7 @@ static int system_journal_open(Server *s, bool flush_requested) { if (r < 0) { if (r != -ENOENT) - log_warning("Failed to open runtime journal: %s", strerror(-r)); + log_warning_errno(r, "Failed to open runtime journal: %m"); r = 0; } @@ -998,10 +997,8 @@ static int system_journal_open(Server *s, bool flush_requested) { r = journal_file_open_reliably(fn, O_RDWR|O_CREAT, 0640, s->compress, false, &s->runtime_metrics, s->mmap, NULL, &s->runtime_journal); free(fn); - if (r < 0) { - log_error("Failed to open runtime journal: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to open runtime journal: %m"); } if (s->runtime_journal) @@ -1044,10 +1041,8 @@ int server_flush_to_var(Server *s) { return r; r = sd_journal_open(&j, SD_JOURNAL_RUNTIME_ONLY); - if (r < 0) { - log_error("Failed to read runtime journal: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to read runtime journal: %m"); sd_journal_set_data_threshold(j, 0); @@ -1062,7 +1057,7 @@ int server_flush_to_var(Server *s) { r = journal_file_move_to_object(f, OBJECT_ENTRY, f->current_offset, &o); if (r < 0) { - log_error("Can't read entry: %s", strerror(-r)); + log_error_errno(r, "Can't read entry: %m"); goto finish; } @@ -1071,7 +1066,7 @@ int server_flush_to_var(Server *s) { continue; if (!shall_try_append_again(s->system_journal, r)) { - log_error("Can't write entry: %s", strerror(-r)); + log_error_errno(r, "Can't write entry: %m"); goto finish; } @@ -1087,7 +1082,7 @@ int server_flush_to_var(Server *s) { log_debug("Retrying write."); r = journal_file_copy_entry(f, s->system_journal, o, f->current_offset, NULL, NULL, NULL); if (r < 0) { - log_error("Can't write entry: %s", strerror(-r)); + log_error_errno(r, "Can't write entry: %m"); goto finish; } } @@ -1112,7 +1107,7 @@ int process_datagram(sd_event_source *es, int fd, uint32_t revents, void *userda Server *s = userdata; assert(s); - assert(fd == s->native_fd || fd == s->syslog_fd); + assert(fd == s->native_fd || fd == s->syslog_fd || fd == s->audit_fd); if (revents != EPOLLIN) { log_error("Got invalid event from epoll for datagram fd: %"PRIx32, revents); @@ -1142,35 +1137,44 @@ int process_datagram(sd_event_source *es, int fd, uint32_t revents, void *userda CMSG_SPACE(sizeof(int)) + /* fd */ CMSG_SPACE(NAME_MAX)]; /* selinux label */ } control = {}; + union sockaddr_union sa = {}; struct msghdr msghdr = { .msg_iov = &iovec, .msg_iovlen = 1, .msg_control = &control, .msg_controllen = sizeof(control), + .msg_name = &sa, + .msg_namelen = sizeof(sa), }; ssize_t n; - int v; int *fds = NULL; unsigned n_fds = 0; + int v = 0; + size_t m; - if (ioctl(fd, SIOCINQ, &v) < 0) { - log_error("SIOCINQ failed: %m"); - return -errno; - } + /* Try to get the right size, if we can. (Not all + * sockets support SIOCINQ, hence we just try, but + * don't rely on it. */ + (void) ioctl(fd, SIOCINQ, &v); + + /* Fix it up, if it is too small. We use the same fixed value as auditd here. Awful!*/ + m = PAGE_ALIGN(MAX3((size_t) v + 1, + (size_t) LINE_MAX, + ALIGN(sizeof(struct nlmsghdr)) + ALIGN((size_t) MAX_AUDIT_MESSAGE_LENGTH)) + 1); - if (!GREEDY_REALLOC(s->buffer, s->buffer_size, LINE_MAX + (size_t) v)) + if (!GREEDY_REALLOC(s->buffer, s->buffer_size, m)) return log_oom(); iovec.iov_base = s->buffer; - iovec.iov_len = s->buffer_size; + iovec.iov_len = s->buffer_size - 1; /* Leave room for trailing NUL we add later */ n = recvmsg(fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); if (n < 0) { if (errno == EINTR || errno == EAGAIN) return 0; - log_error("recvmsg() failed: %m"); + log_error_errno(errno, "recvmsg() failed: %m"); return -errno; } @@ -1195,20 +1199,30 @@ int process_datagram(sd_event_source *es, int fd, uint32_t revents, void *userda } } + /* And a trailing NUL, just in case */ + s->buffer[n] = 0; + if (fd == s->syslog_fd) { - if (n > 0 && n_fds == 0) { - s->buffer[n] = 0; + if (n > 0 && n_fds == 0) server_process_syslog_message(s, strstrip(s->buffer), ucred, tv, label, label_len); - } else if (n_fds > 0) + else if (n_fds > 0) log_warning("Got file descriptors via syslog socket. Ignoring."); - } else { + } else if (fd == s->native_fd) { if (n > 0 && n_fds == 0) server_process_native_message(s, s->buffer, n, ucred, tv, label, label_len); else if (n == 0 && n_fds == 1) server_process_native_file(s, fds[0], ucred, tv, label, label_len); else if (n_fds > 0) log_warning("Got too many file descriptors via native socket. Ignoring."); + + } else { + assert(fd == s->audit_fd); + + if (n > 0 && n_fds == 0) + server_process_audit_message(s, s->buffer, n, ucred, &sa, msghdr.msg_namelen); + else if (n_fds > 0) + log_warning("Got file descriptors via audit socket. Ignoring."); } close_many(fds, n_fds); @@ -1290,10 +1304,10 @@ static int server_parse_proc_cmdline(Server *s) { int r; r = proc_cmdline(&line); - if (r < 0) - log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r)); - if (r <= 0) + if (r < 0) { + log_warning_errno(r, "Failed to read /proc/cmdline, ignoring: %m"); return 0; + } FOREACH_WORD_QUOTED(w, l, line, state) { _cleanup_free_ char *word; @@ -1337,10 +1351,11 @@ static int server_parse_proc_cmdline(Server *s) { static int server_parse_config_file(Server *s) { assert(s); - return config_parse(NULL, "/etc/systemd/journald.conf", NULL, - "Journal\0", - config_item_perf_lookup, journald_gperf_lookup, - false, false, true, s); + return config_parse_many("/etc/systemd/journald.conf", + CONF_DIRS_NULSTR("systemd/journald.conf"), + "Journal\0", + config_item_perf_lookup, journald_gperf_lookup, + false, s); } static int server_dispatch_sync(sd_event_source *es, usec_t t, void *userdata) { @@ -1417,10 +1432,8 @@ static int server_open_hostname(Server *s) { assert(s); s->hostname_fd = open("/proc/sys/kernel/hostname", O_RDONLY|O_CLOEXEC|O_NDELAY|O_NOCTTY); - if (s->hostname_fd < 0) { - log_error("Failed to open /proc/sys/kernel/hostname: %m"); - return -errno; - } + if (s->hostname_fd < 0) + return log_error_errno(errno, "Failed to open /proc/sys/kernel/hostname: %m"); r = sd_event_add_io(s->event, &s->hostname_event_source, s->hostname_fd, 0, dispatch_hostname_change, s); if (r < 0) { @@ -1433,15 +1446,12 @@ static int server_open_hostname(Server *s) { return 0; } - log_error("Failed to register hostname fd in event loop: %s", strerror(-r)); - return r; + return log_error_errno(r, "Failed to register hostname fd in event loop: %m"); } r = sd_event_source_set_priority(s->hostname_event_source, SD_EVENT_PRIORITY_IMPORTANT-10); - if (r < 0) { - log_error("Failed to adjust priority of host name event source: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to adjust priority of host name event source: %m"); return 0; } @@ -1452,7 +1462,7 @@ int server_init(Server *s) { assert(s); zero(*s); - s->syslog_fd = s->native_fd = s->stdout_fd = s->dev_kmsg_fd = s->hostname_fd = -1; + s->syslog_fd = s->native_fd = s->stdout_fd = s->dev_kmsg_fd = s->audit_fd = s->hostname_fd = -1; s->compress = true; s->seal = true; @@ -1494,18 +1504,14 @@ int server_init(Server *s) { return log_oom(); r = sd_event_default(&s->event); - if (r < 0) { - log_error("Failed to create event loop: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to create event loop: %m"); sd_event_set_watchdog(s->event, true); n = sd_listen_fds(true); - if (n < 0) { - log_error("Failed to read listening file descriptors from environment: %s", strerror(-n)); - return n; - } + if (n < 0) + return log_error_errno(n, "Failed to read listening file descriptors from environment: %m"); for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) { @@ -1537,9 +1543,24 @@ int server_init(Server *s) { s->syslog_fd = fd; + } else if (sd_is_socket(fd, AF_NETLINK, SOCK_RAW, -1) > 0) { + + if (s->audit_fd >= 0) { + log_error("Too many audit sockets passed."); + return -EINVAL; + } + + s->audit_fd = fd; + } else { - log_error("Unknown socket passed."); - return -EINVAL; + log_warning("Unknown socket passed as file descriptor %d, ignoring.", fd); + + /* Let's close the fd, better be safe than + sorry. The fd might reference some resource + that we really want to release if we don't + make use of it. */ + + safe_close(fd); } } @@ -1559,6 +1580,10 @@ int server_init(Server *s) { if (r < 0) return r; + r = server_open_audit(s); + if (r < 0) + return r; + r = server_open_kernel_seqnum(s); if (r < 0) return r; @@ -1632,6 +1657,7 @@ void server_done(Server *s) { sd_event_source_unref(s->native_event_source); sd_event_source_unref(s->stdout_event_source); sd_event_source_unref(s->dev_kmsg_event_source); + sd_event_source_unref(s->audit_event_source); sd_event_source_unref(s->sync_event_source); sd_event_source_unref(s->sigusr1_event_source); sd_event_source_unref(s->sigusr2_event_source); @@ -1644,6 +1670,7 @@ void server_done(Server *s) { safe_close(s->native_fd); safe_close(s->stdout_fd); safe_close(s->dev_kmsg_fd); + safe_close(s->audit_fd); safe_close(s->hostname_fd); if (s->rate_limit) @@ -1655,6 +1682,7 @@ void server_done(Server *s) { free(s->buffer); free(s->tty_path); free(s->cgroup_root); + free(s->hostname_field); if (s->mmap) mmap_cache_unref(s->mmap); diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h index 6888187df9..9c7fa50a96 100644 --- a/src/journal/journald-server.h +++ b/src/journal/journald-server.h @@ -59,6 +59,7 @@ typedef struct Server { int native_fd; int stdout_fd; int dev_kmsg_fd; + int audit_fd; int hostname_fd; sd_event *event; @@ -67,6 +68,7 @@ typedef struct Server { sd_event_source *native_event_source; sd_event_source *stdout_event_source; sd_event_source *dev_kmsg_event_source; + sd_event_source *audit_event_source; sd_event_source *sync_event_source; sd_event_source *sigusr1_event_source; sd_event_source *sigusr2_event_source; @@ -148,7 +150,7 @@ typedef struct Server { #define N_IOVEC_UDEV_FIELDS 32 #define N_IOVEC_OBJECT_FIELDS 11 -void server_dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigned m, struct ucred *ucred, struct timeval *tv, const char *label, size_t label_len, const char *unit_id, int priority, pid_t object_pid); +void server_dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigned m, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len, const char *unit_id, int priority, pid_t object_pid); void server_driver_message(Server *s, sd_id128_t message_id, const char *format, ...) _printf_(3,4); /* gperf lookup function */ diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c index 76580f8263..be498d4919 100644 --- a/src/journal/journald-stream.c +++ b/src/journal/journald-stream.c @@ -306,7 +306,7 @@ static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents, if (errno == EAGAIN) return 0; - log_warning("Failed to read from stream: %m"); + log_warning_errno(errno, "Failed to read from stream: %m"); goto terminate; } @@ -370,7 +370,7 @@ static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revent if (errno == EAGAIN) return 0; - log_error("Failed to accept stdout connection: %m"); + log_error_errno(errno, "Failed to accept stdout connection: %m"); return -errno; } @@ -390,31 +390,31 @@ static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revent r = getpeercred(fd, &stream->ucred); if (r < 0) { - log_error("Failed to determine peer credentials: %m"); + log_error_errno(errno, "Failed to determine peer credentials: %m"); goto fail; } #ifdef HAVE_SELINUX if (mac_selinux_use()) { if (getpeercon(fd, &stream->security_context) < 0 && errno != ENOPROTOOPT) - log_error("Failed to determine peer security context: %m"); + log_error_errno(errno, "Failed to determine peer security context: %m"); } #endif if (shutdown(fd, SHUT_WR) < 0) { - log_error("Failed to shutdown writing side of socket: %m"); + log_error_errno(errno, "Failed to shutdown writing side of socket: %m"); goto fail; } r = sd_event_add_io(s->event, &stream->event_source, fd, EPOLLIN, stdout_stream_process, stream); if (r < 0) { - log_error("Failed to add stream to event loop: %s", strerror(-r)); + log_error_errno(r, "Failed to add stream to event loop: %m"); goto fail; } r = sd_event_source_set_priority(stream->event_source, SD_EVENT_PRIORITY_NORMAL+5); if (r < 0) { - log_error("Failed to adjust stdout event source priority: %s", strerror(-r)); + log_error_errno(r, "Failed to adjust stdout event source priority: %m"); goto fail; } @@ -441,39 +441,29 @@ int server_open_stdout_socket(Server *s) { }; s->stdout_fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (s->stdout_fd < 0) { - log_error("socket() failed: %m"); - return -errno; - } + if (s->stdout_fd < 0) + return log_error_errno(errno, "socket() failed: %m"); unlink(sa.un.sun_path); r = bind(s->stdout_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)); - if (r < 0) { - log_error("bind(%s) failed: %m", sa.un.sun_path); - return -errno; - } + if (r < 0) + return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); chmod(sa.un.sun_path, 0666); - if (listen(s->stdout_fd, SOMAXCONN) < 0) { - log_error("listen(%s) failed: %m", sa.un.sun_path); - return -errno; - } + if (listen(s->stdout_fd, SOMAXCONN) < 0) + return log_error_errno(errno, "listen(%s) failed: %m", sa.un.sun_path); } else fd_nonblock(s->stdout_fd, 1); r = sd_event_add_io(s->event, &s->stdout_event_source, s->stdout_fd, EPOLLIN, stdout_stream_new, s); - if (r < 0) { - log_error("Failed to add stdout server fd to event source: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add stdout server fd to event source: %m"); r = sd_event_source_set_priority(s->stdout_event_source, SD_EVENT_PRIORITY_NORMAL+10); - if (r < 0) { - log_error("Failed to adjust priority of stdout server event source: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to adjust priority of stdout server event source: %m"); return 0; } diff --git a/src/journal/journald-syslog.c b/src/journal/journald-syslog.c index 79c07fffa1..cc44d45949 100644 --- a/src/journal/journald-syslog.c +++ b/src/journal/journald-syslog.c @@ -35,7 +35,7 @@ /* Warn once every 30s if we missed syslog message */ #define WARN_FORWARD_SYSLOG_MISSED_USEC (30 * USEC_PER_SEC) -static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, struct ucred *ucred, struct timeval *tv) { +static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, const struct ucred *ucred, const struct timeval *tv) { static const union sockaddr_union sa = { .un.sun_family = AF_UNIX, @@ -106,10 +106,10 @@ static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned } if (errno != ENOENT) - log_debug("Failed to forward syslog message: %m"); + log_debug_errno(errno, "Failed to forward syslog message: %m"); } -static void forward_syslog_raw(Server *s, int priority, const char *buffer, struct ucred *ucred, struct timeval *tv) { +static void forward_syslog_raw(Server *s, int priority, const char *buffer, const struct ucred *ucred, const struct timeval *tv) { struct iovec iovec; assert(s); @@ -122,7 +122,7 @@ static void forward_syslog_raw(Server *s, int priority, const char *buffer, stru forward_syslog_iovec(s, &iovec, 1, ucred, tv); } -void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, struct ucred *ucred, struct timeval *tv) { +void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred, const struct timeval *tv) { struct iovec iovec[5]; char header_priority[6], header_time[64], header_pid[16]; int n = 0; @@ -351,16 +351,18 @@ static void syslog_skip_date(char **buf) { void server_process_syslog_message( Server *s, const char *buf, - struct ucred *ucred, - struct timeval *tv, + const struct ucred *ucred, + const struct timeval *tv, const char *label, size_t label_len) { - char *message = NULL, *syslog_priority = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *syslog_pid = NULL; + char syslog_priority[sizeof("PRIORITY=") + DECIMAL_STR_MAX(int)], + syslog_facility[sizeof("SYSLOG_FACILITY") + DECIMAL_STR_MAX(int)]; + const char *message = NULL, *syslog_identifier = NULL, *syslog_pid = NULL; struct iovec iovec[N_IOVEC_META_FIELDS + 6]; unsigned n = 0; int priority = LOG_USER | LOG_INFO; - char *identifier = NULL, *pid = NULL; + _cleanup_free_ char *identifier = NULL, *pid = NULL; const char *orig; assert(s); @@ -386,42 +388,36 @@ void server_process_syslog_message( IOVEC_SET_STRING(iovec[n++], "_TRANSPORT=syslog"); - if (asprintf(&syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK) >= 0) - IOVEC_SET_STRING(iovec[n++], syslog_priority); + sprintf(syslog_priority, "PRIORITY=%i", priority & LOG_PRIMASK); + IOVEC_SET_STRING(iovec[n++], syslog_priority); - if (priority & LOG_FACMASK) - if (asprintf(&syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)) >= 0) - IOVEC_SET_STRING(iovec[n++], syslog_facility); + if (priority & LOG_FACMASK) { + sprintf(syslog_facility, "SYSLOG_FACILITY=%i", LOG_FAC(priority)); + IOVEC_SET_STRING(iovec[n++], syslog_facility); + } if (identifier) { - syslog_identifier = strappend("SYSLOG_IDENTIFIER=", identifier); + syslog_identifier = strappenda("SYSLOG_IDENTIFIER=", identifier); if (syslog_identifier) IOVEC_SET_STRING(iovec[n++], syslog_identifier); } if (pid) { - syslog_pid = strappend("SYSLOG_PID=", pid); + syslog_pid = strappenda("SYSLOG_PID=", pid); if (syslog_pid) IOVEC_SET_STRING(iovec[n++], syslog_pid); } - message = strappend("MESSAGE=", buf); + message = strappenda("MESSAGE=", buf); if (message) IOVEC_SET_STRING(iovec[n++], message); server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, NULL, priority, 0); - - free(message); - free(identifier); - free(pid); - free(syslog_priority); - free(syslog_facility); - free(syslog_identifier); - free(syslog_pid); } int server_open_syslog_socket(Server *s) { - int one, r; + static const int one = 1; + int r; assert(s); @@ -432,51 +428,38 @@ int server_open_syslog_socket(Server *s) { }; s->syslog_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (s->syslog_fd < 0) { - log_error("socket() failed: %m"); - return -errno; - } + if (s->syslog_fd < 0) + return log_error_errno(errno, "socket() failed: %m"); unlink(sa.un.sun_path); r = bind(s->syslog_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path)); - if (r < 0) { - log_error("bind(%s) failed: %m", sa.un.sun_path); - return -errno; - } + if (r < 0) + return log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); chmod(sa.un.sun_path, 0666); } else fd_nonblock(s->syslog_fd, 1); - one = 1; r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); - if (r < 0) { - log_error("SO_PASSCRED failed: %m"); - return -errno; - } + if (r < 0) + return log_error_errno(errno, "SO_PASSCRED failed: %m"); #ifdef HAVE_SELINUX if (mac_selinux_use()) { - one = 1; r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one)); if (r < 0) - log_warning("SO_PASSSEC failed: %m"); + log_warning_errno(errno, "SO_PASSSEC failed: %m"); } #endif - one = 1; r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)); - if (r < 0) { - log_error("SO_TIMESTAMP failed: %m"); - return -errno; - } + if (r < 0) + return log_error_errno(errno, "SO_TIMESTAMP failed: %m"); r = sd_event_add_io(s->event, &s->syslog_event_source, s->syslog_fd, EPOLLIN, process_datagram, s); - if (r < 0) { - log_error("Failed to add syslog server fd to event loop: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add syslog server fd to event loop: %m"); return 0; } diff --git a/src/journal/journald-syslog.h b/src/journal/journald-syslog.h index 057ea79def..25f89883f4 100644 --- a/src/journal/journald-syslog.h +++ b/src/journal/journald-syslog.h @@ -28,9 +28,9 @@ int syslog_fixup_facility(int priority) _const_; void syslog_parse_priority(const char **p, int *priority, bool with_facility); size_t syslog_parse_identifier(const char **buf, char **identifier, char **pid); -void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, struct ucred *ucred, struct timeval *tv); +void server_forward_syslog(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred, const struct timeval *tv); -void server_process_syslog_message(Server *s, const char *buf, struct ucred *ucred, struct timeval *tv, const char *label, size_t label_len); +void server_process_syslog_message(Server *s, const char *buf, const struct ucred *ucred, const struct timeval *tv, const char *label, size_t label_len); int server_open_syslog_socket(Server *s); void server_maybe_warn_forward_syslog_missed(Server *s); diff --git a/src/journal/journald-wall.c b/src/journal/journald-wall.c index fcbd9183fe..e3201674d4 100644 --- a/src/journal/journald-wall.c +++ b/src/journal/journald-wall.c @@ -28,7 +28,7 @@ void server_forward_wall( int priority, const char *identifier, const char *message, - struct ucred *ucred) { + const struct ucred *ucred) { _cleanup_free_ char *ident_buf = NULL, *l_buf = NULL; const char *l; @@ -65,5 +65,5 @@ void server_forward_wall( r = utmp_wall(l, "systemd-journald", NULL); if (r < 0) - log_debug("Failed to send wall message: %s", strerror(-r)); + log_debug_errno(r, "Failed to send wall message: %m"); } diff --git a/src/journal/journald-wall.h b/src/journal/journald-wall.h index 93c3cec1d2..45c52854a0 100644 --- a/src/journal/journald-wall.h +++ b/src/journal/journald-wall.h @@ -23,4 +23,4 @@ #include "journald-server.h" -void server_forward_wall(Server *s, int priority, const char *identifier, const char *message, struct ucred *ucred); +void server_forward_wall(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred); diff --git a/src/journal/journald.c b/src/journal/journald.c index de40827d6a..604c8617bb 100644 --- a/src/journal/journald.c +++ b/src/journal/journald.c @@ -104,7 +104,7 @@ int main(int argc, char *argv[]) { r = sd_event_run(server.event, t); if (r < 0) { - log_error("Failed to run event loop: %s", strerror(-r)); + log_error_errno(r, "Failed to run event loop: %m"); goto finish; } diff --git a/src/journal/journald.conf b/src/journal/journald.conf index 2073f1bf21..29bdf8f183 100644 --- a/src/journal/journald.conf +++ b/src/journal/journald.conf @@ -5,6 +5,9 @@ # the Free Software Foundation; either version 2.1 of the License, or # (at your option) any later version. # +# You can override the directives in this file by creating files in +# /etc/systemd/journald.conf.d/*.conf. +# # See journald.conf(5) for details [Journal] diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index cf21c4d899..23aad740dd 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -497,6 +497,47 @@ static int compare_entry_order(JournalFile *af, Object *_ao, return 0; } +static bool whole_file_precedes_location(JournalFile *f, Location *l, direction_t direction) { + assert(f); + assert(l); + + if (l->type != LOCATION_DISCRETE && l->type != LOCATION_SEEK) + return false; + + if (l->seqnum_set && sd_id128_equal(l->seqnum_id, f->header->seqnum_id)) + return direction == DIRECTION_DOWN ? + l->seqnum > le64toh(f->header->tail_entry_seqnum) : + l->seqnum < le64toh(f->header->head_entry_seqnum); + + if (l->realtime_set) + return direction == DIRECTION_DOWN ? + l->realtime > le64toh(f->header->tail_entry_realtime) : + l->realtime < le64toh(f->header->head_entry_realtime); + + return false; +} + +static bool file_may_have_preceding_entry(JournalFile *f, JournalFile *of, uint64_t op, direction_t direction) { + Object *o; + int r; + + assert(f); + assert(of); + + r = journal_file_move_to_object(of, OBJECT_ENTRY, op, &o); + if (r < 0) + return true; + + if (sd_id128_equal(f->header->seqnum_id, of->header->seqnum_id)) + return direction == DIRECTION_DOWN ? + le64toh(o->entry.seqnum) >= le64toh(f->header->head_entry_seqnum) : + le64toh(o->entry.seqnum) <= le64toh(f->header->tail_entry_seqnum); + + return direction == DIRECTION_DOWN ? + le64toh(o->entry.realtime) >= le64toh(f->header->head_entry_realtime) : + le64toh(o->entry.realtime) <= le64toh(f->header->tail_entry_realtime); +} + _pure_ static int compare_with_location(JournalFile *af, Object *ao, Location *l) { uint64_t a; @@ -882,9 +923,15 @@ static int real_journal_next(sd_journal *j, direction_t direction) { ORDERED_HASHMAP_FOREACH(f, j->files, i) { bool found; + if (whole_file_precedes_location(f, &j->current_location, direction)) + continue; + + if (new_file && !file_may_have_preceding_entry(f, new_file, new_offset, direction)) + continue; + r = next_beyond_location(j, f, direction, &o, &p); if (r < 0) { - log_debug("Can't iterate through %s, ignoring: %s", f->path, strerror(-r)); + log_debug_errno(r, "Can't iterate through %s, ignoring: %m", f->path); remove_file_real(j, f); continue; } else if (r == 0) @@ -1410,7 +1457,7 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname) d = opendir(path); if (!d) { - log_debug("Failed to open %s: %m", path); + log_debug_errno(errno, "Failed to open %s: %m", path); if (errno == ENOENT) return 0; return -errno; @@ -1456,7 +1503,7 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname) de = readdir(d); if (!de && errno != 0) { r = -errno; - log_debug("Failed to read directory %s: %m", m->path); + log_debug_errno(errno, "Failed to read directory %s: %m", m->path); return r; } if (!de) @@ -1466,8 +1513,8 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname) dirent_is_file_with_suffix(de, ".journal~")) { r = add_file(j, m->path, de->d_name); if (r < 0) { - log_debug("Failed to add file %s/%s: %s", - m->path, de->d_name, strerror(-r)); + log_debug_errno(r, "Failed to add file %s/%s: %m", + m->path, de->d_name); r = set_put_error(j, r); if (r < 0) return r; @@ -1546,7 +1593,7 @@ static int add_root_directory(sd_journal *j, const char *p) { de = readdir(d); if (!de && errno != 0) { r = -errno; - log_debug("Failed to read directory %s: %m", m->path); + log_debug_errno(errno, "Failed to read directory %s: %m", m->path); return r; } if (!de) @@ -1556,8 +1603,8 @@ static int add_root_directory(sd_journal *j, const char *p) { dirent_is_file_with_suffix(de, ".journal~")) { r = add_file(j, m->path, de->d_name); if (r < 0) { - log_debug("Failed to add file %s/%s: %s", - m->path, de->d_name, strerror(-r)); + log_debug_errno(r, "Failed to add file %s/%s: %m", + m->path, de->d_name); r = set_put_error(j, r); if (r < 0) return r; @@ -1567,7 +1614,7 @@ static int add_root_directory(sd_journal *j, const char *p) { r = add_directory(j, m->path, de->d_name); if (r < 0) - log_debug("Failed to add directory %s/%s: %s", m->path, de->d_name, strerror(-r)); + log_debug_errno(r, "Failed to add directory %s/%s: %m", m->path, de->d_name); } } @@ -1810,7 +1857,7 @@ _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int fla STRV_FOREACH(path, paths) { r = add_any_file(j, *path); if (r < 0) { - log_error("Failed to open %s: %s", *path, strerror(-r)); + log_error_errno(r, "Failed to open %s: %m", *path); goto fail; } } @@ -2218,8 +2265,8 @@ static void process_inotify_event(sd_journal *j, struct inotify_event *e) { if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) { r = add_file(j, d->path, e->name); if (r < 0) { - log_debug("Failed to add file %s/%s: %s", - d->path, e->name, strerror(-r)); + log_debug_errno(r, "Failed to add file %s/%s: %m", + d->path, e->name); set_put_error(j, r); } @@ -2227,7 +2274,7 @@ static void process_inotify_event(sd_journal *j, struct inotify_event *e) { r = remove_file(j, d->path, e->name); if (r < 0) - log_debug("Failed to remove file %s/%s: %s", d->path, e->name, strerror(-r)); + log_debug_errno(r, "Failed to remove file %s/%s: %m", d->path, e->name); } } else if (!d->is_root && e->len == 0) { @@ -2237,7 +2284,7 @@ static void process_inotify_event(sd_journal *j, struct inotify_event *e) { if (e->mask & (IN_DELETE_SELF|IN_MOVE_SELF|IN_UNMOUNT)) { r = remove_directory(j, d); if (r < 0) - log_debug("Failed to remove directory %s: %s", d->path, strerror(-r)); + log_debug_errno(r, "Failed to remove directory %s: %m", d->path); } @@ -2248,7 +2295,7 @@ static void process_inotify_event(sd_journal *j, struct inotify_event *e) { if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) { r = add_directory(j, d->path, e->name); if (r < 0) - log_debug("Failed to add directory %s/%s: %s", d->path, e->name, strerror(-r)); + log_debug_errno(r, "Failed to add directory %s/%s: %m", d->path, e->name); } } @@ -2273,7 +2320,6 @@ static int determine_change(sd_journal *j) { } _public_ int sd_journal_process(sd_journal *j) { - uint8_t buffer[sizeof(struct inotify_event) + FILENAME_MAX] _alignas_(struct inotify_event); bool got_something = false; assert_return(j, -EINVAL); @@ -2282,6 +2328,7 @@ _public_ int sd_journal_process(sd_journal *j) { j->last_process_usec = now(CLOCK_MONOTONIC); for (;;) { + uint8_t buffer[INOTIFY_EVENT_MAX] _alignas_(struct inotify_event); struct inotify_event *e; ssize_t l; @@ -2295,18 +2342,8 @@ _public_ int sd_journal_process(sd_journal *j) { got_something = true; - e = (struct inotify_event*) buffer; - while (l > 0) { - size_t step; - + FOREACH_INOTIFY_EVENT(e, buffer, l) process_inotify_event(j, e); - - step = sizeof(struct inotify_event) + e->len; - assert(step <= (size_t) l); - - e = (struct inotify_event*) ((uint8_t*) e + step); - l -= step; - } } } diff --git a/src/journal/test-catalog.c b/src/journal/test-catalog.c index 5fe2f6a269..c605ee0e70 100644 --- a/src/journal/test-catalog.c +++ b/src/journal/test-catalog.c @@ -49,11 +49,11 @@ static void test_import(Hashmap *h, struct strbuf *sb, _cleanup_close_ int fd; fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); - assert(fd >= 0); + assert_se(fd >= 0); assert_se(write(fd, contents, size) == size); r = catalog_import_file(h, sb, name); - assert(r == code); + assert_se(r == code); unlink(name); } @@ -68,7 +68,7 @@ static void test_catalog_importing(void) { #define BUF "xxx" test_import(h, sb, BUF, sizeof(BUF), -EINVAL); #undef BUF - assert(hashmap_isempty(h)); + assert_se(hashmap_isempty(h)); log_debug("----------------------------------------"); #define BUF \ @@ -89,7 +89,7 @@ static void test_catalog_importing(void) { test_import(h, sb, BUF, sizeof(BUF), 0); #undef BUF - assert(hashmap_size(h) == 1); + assert_se(hashmap_size(h) == 1); log_debug("----------------------------------------"); @@ -105,22 +105,22 @@ static void test_catalog_update(void) { int r; r = mkostemp_safe(name, O_RDWR|O_CLOEXEC); - assert(r >= 0); + assert_se(r >= 0); database = name; /* Test what happens if there are no files. */ r = catalog_update(database, NULL, NULL); - assert(r >= 0); + assert_se(r >= 0); /* Test what happens if there are no files in the directory. */ r = catalog_update(database, NULL, no_catalog_dirs); - assert(r >= 0); + assert_se(r >= 0); /* Make sure that we at least have some files loaded or the catalog_list below will fail. */ r = catalog_update(database, NULL, catalog_dirs); - assert(r >= 0); + assert_se(r >= 0); } static void test_catalog_file_lang(void) { diff --git a/src/journal/test-compress.c b/src/journal/test-compress.c index 78b3203bef..97577e827c 100644 --- a/src/journal/test-compress.c +++ b/src/journal/test-compress.c @@ -63,29 +63,29 @@ static void test_compress_decompress(int compression, r = compress(data, data_len, compressed, &csize); if (r == -ENOBUFS) { - log_info("compression failed: %s", strerror(-r)); - assert(may_fail); + log_info_errno(r, "compression failed: %m"); + assert_se(may_fail); } else { - assert(r == 0); + assert_se(r == 0); r = decompress(compressed, csize, (void **) &decompressed, &usize, &csize, 0); - assert(r == 0); + assert_se(r == 0); assert_se(decompressed); assert_se(memcmp(decompressed, data, data_len) == 0); } r = decompress("garbage", 7, (void **) &decompressed, &usize, &csize, 0); - assert(r < 0); + assert_se(r < 0); /* make sure to have the minimal lz4 compressed size */ r = decompress("00000000\1g", 9, (void **) &decompressed, &usize, &csize, 0); - assert(r < 0); + assert_se(r < 0); r = decompress("\100000000g", 9, (void **) &decompressed, &usize, &csize, 0); - assert(r < 0); + assert_se(r < 0); memzero(decompressed, usize); } @@ -108,11 +108,11 @@ static void test_decompress_startswith(int compression, r = compress(data, data_len, compressed, &csize); if (r == -ENOBUFS) { - log_info("compression failed: %s", strerror(-r)); - assert(may_fail); + log_info_errno(r, "compression failed: %m"); + assert_se(may_fail); return; } - assert(r == 0); + assert_se(r == 0); assert_se(decompress_sw(compressed, csize, @@ -184,7 +184,7 @@ static void test_compress_stream(int compression, assert_se(lseek(dst, 1, SEEK_SET) == 1); r = decompress(dst, dst2, st.st_size); - assert(r == -EBADMSG); + assert_se(r == -EBADMSG); assert_se(lseek(dst, 0, SEEK_SET) == 0); assert_se(lseek(dst2, 0, SEEK_SET) == 0); diff --git a/src/journal/test-journal-interleaving.c b/src/journal/test-journal-interleaving.c index 6c5995e0c1..23a26c43f6 100644 --- a/src/journal/test-journal-interleaving.c +++ b/src/journal/test-journal-interleaving.c @@ -37,9 +37,9 @@ static bool arg_keep = false; noreturn static void log_assert_errno(const char *text, int eno, const char *file, int line, const char *func) { - log_meta(LOG_CRIT, file, line, func, - "'%s' failed at %s:%u (%s): %s.", - text, file, line, func, strerror(eno)); + log_internal(LOG_CRIT, 0, file, line, func, + "'%s' failed at %s:%u (%s): %s.", + text, file, line, func, strerror(eno)); abort(); } @@ -189,7 +189,7 @@ static void test_skip(void (*setup)(void)) { if (arg_keep) log_info("Not removing %s", t); else { - journal_directory_vacuum(".", 3000000, 0, NULL); + journal_directory_vacuum(".", 3000000, 0, NULL, true); assert_se(rm_rf_dangerous(t, false, true, false) >= 0); } @@ -274,7 +274,7 @@ static void test_sequence_numbers(void) { if (arg_keep) log_info("Not removing %s", t); else { - journal_directory_vacuum(".", 3000000, 0, NULL); + journal_directory_vacuum(".", 3000000, 0, NULL, true); assert_se(rm_rf_dangerous(t, false, true, false) >= 0); } diff --git a/src/journal/test-journal-syslog.c b/src/journal/test-journal-syslog.c index c4d407148c..c99ca0654b 100644 --- a/src/journal/test-journal-syslog.c +++ b/src/journal/test-journal-syslog.c @@ -30,9 +30,9 @@ static void test_syslog_parse_identifier(const char* str, ret2 = syslog_parse_identifier(&buf, &ident2, &pid2); - assert(ret == ret2); - assert(ident == ident2 || streq_ptr(ident, ident2)); - assert(pid == pid2 || streq_ptr(pid, pid2)); + assert_se(ret == ret2); + assert_se(ident == ident2 || streq_ptr(ident, ident2)); + assert_se(pid == pid2 || streq_ptr(pid, pid2)); } int main(void) { diff --git a/src/journal/test-journal-verify.c b/src/journal/test-journal-verify.c index 3b181c6794..9da120c0bb 100644 --- a/src/journal/test-journal-verify.c +++ b/src/journal/test-journal-verify.c @@ -38,15 +38,15 @@ static void bit_toggle(const char *fn, uint64_t p) { int fd; fd = open(fn, O_RDWR|O_CLOEXEC); - assert(fd >= 0); + assert_se(fd >= 0); r = pread(fd, &b, 1, p/8); - assert(r == 1); + assert_se(r == 1); b ^= 1 << (p % 8); r = pwrite(fd, &b, 1, p/8); - assert(r == 1); + assert_se(r == 1); safe_close(fd); } diff --git a/src/journal/test-journal.c b/src/journal/test-journal.c index 6025d04ba4..ff9dc9e170 100644 --- a/src/journal/test-journal.c +++ b/src/journal/test-journal.c @@ -66,55 +66,55 @@ static void test_non_empty(void) { #endif journal_file_dump(f); - assert(journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, &o, &p) == 1); - assert(le64toh(o->entry.seqnum) == 1); + assert_se(journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, &o, &p) == 1); + assert_se(le64toh(o->entry.seqnum) == 1); - assert(journal_file_next_entry(f, o, p, DIRECTION_DOWN, &o, &p) == 1); - assert(le64toh(o->entry.seqnum) == 2); + assert_se(journal_file_next_entry(f, o, p, DIRECTION_DOWN, &o, &p) == 1); + assert_se(le64toh(o->entry.seqnum) == 2); - assert(journal_file_next_entry(f, o, p, DIRECTION_DOWN, &o, &p) == 1); - assert(le64toh(o->entry.seqnum) == 3); + assert_se(journal_file_next_entry(f, o, p, DIRECTION_DOWN, &o, &p) == 1); + assert_se(le64toh(o->entry.seqnum) == 3); - assert(journal_file_next_entry(f, o, p, DIRECTION_DOWN, &o, &p) == 0); + assert_se(journal_file_next_entry(f, o, p, DIRECTION_DOWN, &o, &p) == 0); - assert(journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, &o, &p) == 1); - assert(le64toh(o->entry.seqnum) == 1); + assert_se(journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, &o, &p) == 1); + assert_se(le64toh(o->entry.seqnum) == 1); - assert(journal_file_skip_entry(f, o, p, 2, &o, &p) == 1); - assert(le64toh(o->entry.seqnum) == 3); + assert_se(journal_file_skip_entry(f, o, p, 2, &o, &p) == 1); + assert_se(le64toh(o->entry.seqnum) == 3); - assert(journal_file_skip_entry(f, o, p, -2, &o, &p) == 1); - assert(le64toh(o->entry.seqnum) == 1); + assert_se(journal_file_skip_entry(f, o, p, -2, &o, &p) == 1); + assert_se(le64toh(o->entry.seqnum) == 1); - assert(journal_file_skip_entry(f, o, p, -2, &o, &p) == 1); - assert(le64toh(o->entry.seqnum) == 1); + assert_se(journal_file_skip_entry(f, o, p, -2, &o, &p) == 1); + assert_se(le64toh(o->entry.seqnum) == 1); - assert(journal_file_find_data_object(f, test, strlen(test), NULL, &p) == 1); - assert(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_DOWN, &o, NULL) == 1); - assert(le64toh(o->entry.seqnum) == 1); + assert_se(journal_file_find_data_object(f, test, strlen(test), NULL, &p) == 1); + assert_se(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_DOWN, &o, NULL) == 1); + assert_se(le64toh(o->entry.seqnum) == 1); - assert(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_UP, &o, NULL) == 1); - assert(le64toh(o->entry.seqnum) == 3); + assert_se(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_UP, &o, NULL) == 1); + assert_se(le64toh(o->entry.seqnum) == 3); - assert(journal_file_find_data_object(f, test2, strlen(test2), NULL, &p) == 1); - assert(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_UP, &o, NULL) == 1); - assert(le64toh(o->entry.seqnum) == 2); + assert_se(journal_file_find_data_object(f, test2, strlen(test2), NULL, &p) == 1); + assert_se(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_UP, &o, NULL) == 1); + assert_se(le64toh(o->entry.seqnum) == 2); - assert(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_DOWN, &o, NULL) == 1); - assert(le64toh(o->entry.seqnum) == 2); + assert_se(journal_file_next_entry_for_data(f, NULL, 0, p, DIRECTION_DOWN, &o, NULL) == 1); + assert_se(le64toh(o->entry.seqnum) == 2); - assert(journal_file_find_data_object(f, "quux", 4, NULL, &p) == 0); + assert_se(journal_file_find_data_object(f, "quux", 4, NULL, &p) == 0); - assert(journal_file_move_to_entry_by_seqnum(f, 1, DIRECTION_DOWN, &o, NULL) == 1); - assert(le64toh(o->entry.seqnum) == 1); + assert_se(journal_file_move_to_entry_by_seqnum(f, 1, DIRECTION_DOWN, &o, NULL) == 1); + assert_se(le64toh(o->entry.seqnum) == 1); - assert(journal_file_move_to_entry_by_seqnum(f, 3, DIRECTION_DOWN, &o, NULL) == 1); - assert(le64toh(o->entry.seqnum) == 3); + assert_se(journal_file_move_to_entry_by_seqnum(f, 3, DIRECTION_DOWN, &o, NULL) == 1); + assert_se(le64toh(o->entry.seqnum) == 3); - assert(journal_file_move_to_entry_by_seqnum(f, 2, DIRECTION_DOWN, &o, NULL) == 1); - assert(le64toh(o->entry.seqnum) == 2); + assert_se(journal_file_move_to_entry_by_seqnum(f, 2, DIRECTION_DOWN, &o, NULL) == 1); + assert_se(le64toh(o->entry.seqnum) == 2); - assert(journal_file_move_to_entry_by_seqnum(f, 10, DIRECTION_DOWN, &o, NULL) == 0); + assert_se(journal_file_move_to_entry_by_seqnum(f, 10, DIRECTION_DOWN, &o, NULL) == 0); journal_file_rotate(&f, true, true); journal_file_rotate(&f, true, true); @@ -126,7 +126,7 @@ static void test_non_empty(void) { if (arg_keep) log_info("Not removing %s", t); else { - journal_directory_vacuum(".", 3000000, 0, NULL); + journal_directory_vacuum(".", 3000000, 0, NULL, true); assert_se(rm_rf_dangerous(t, false, true, false) >= 0); } @@ -165,7 +165,7 @@ static void test_empty(void) { if (arg_keep) log_info("Not removing %s", t); else { - journal_directory_vacuum(".", 3000000, 0, NULL); + journal_directory_vacuum(".", 3000000, 0, NULL, true); assert_se(rm_rf_dangerous(t, false, true, false) >= 0); } diff --git a/src/journal/test-mmap-cache.c b/src/journal/test-mmap-cache.c index 778e884c3f..1227b62310 100644 --- a/src/journal/test-mmap-cache.c +++ b/src/journal/test-mmap-cache.c @@ -38,37 +38,37 @@ int main(int argc, char *argv[]) { assert_se(m = mmap_cache_new()); x = mkostemp_safe(px, O_RDWR|O_CLOEXEC); - assert(x >= 0); + assert_se(x >= 0); unlink(px); y = mkostemp_safe(py, O_RDWR|O_CLOEXEC); - assert(y >= 0); + assert_se(y >= 0); unlink(py); z = mkostemp_safe(pz, O_RDWR|O_CLOEXEC); - assert(z >= 0); + assert_se(z >= 0); unlink(pz); r = mmap_cache_get(m, x, PROT_READ, 0, false, 1, 2, NULL, &p, NULL); - assert(r >= 0); + assert_se(r >= 0); r = mmap_cache_get(m, x, PROT_READ, 0, false, 2, 2, NULL, &q, NULL); - assert(r >= 0); + assert_se(r >= 0); - assert((uint8_t*) p + 1 == (uint8_t*) q); + assert_se((uint8_t*) p + 1 == (uint8_t*) q); r = mmap_cache_get(m, x, PROT_READ, 1, false, 3, 2, NULL, &q, NULL); - assert(r >= 0); + assert_se(r >= 0); - assert((uint8_t*) p + 2 == (uint8_t*) q); + assert_se((uint8_t*) p + 2 == (uint8_t*) q); r = mmap_cache_get(m, x, PROT_READ, 0, false, 16ULL*1024ULL*1024ULL, 2, NULL, &p, NULL); - assert(r >= 0); + assert_se(r >= 0); r = mmap_cache_get(m, x, PROT_READ, 1, false, 16ULL*1024ULL*1024ULL+1, 2, NULL, &q, NULL); - assert(r >= 0); + assert_se(r >= 0); - assert((uint8_t*) p + 1 == (uint8_t*) q); + assert_se((uint8_t*) p + 1 == (uint8_t*) q); mmap_cache_unref(m); diff --git a/src/kernel-install/90-loaderentry.install b/src/kernel-install/90-loaderentry.install index 6f032b5a4b..d433e00a5c 100644 --- a/src/kernel-install/90-loaderentry.install +++ b/src/kernel-install/90-loaderentry.install @@ -47,7 +47,7 @@ if [[ -f /etc/kernel/cmdline ]]; then fi if ! [[ ${BOOT_OPTIONS[*]} ]]; then - read -ar line < /proc/cmdline + read -a line -r < /proc/cmdline for i in "${line[@]}"; do [[ "${i#initrd=*}" != "$i" ]] && continue BOOT_OPTIONS[${#BOOT_OPTIONS[@]}]="$i" diff --git a/src/libsystemd-network/dhcp-internal.h b/src/libsystemd-network/dhcp-internal.h index d358a49307..7c60ef123c 100644 --- a/src/libsystemd-network/dhcp-internal.h +++ b/src/libsystemd-network/dhcp-internal.h @@ -71,4 +71,4 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_client*, sd_dhcp_client_unref); #define DHCP_CLIENT_DONT_DESTROY(client) \ _cleanup_dhcp_client_unref_ _unused_ sd_dhcp_client *_dont_destroy_##client = sd_dhcp_client_ref(client) -#define log_dhcp_client(client, fmt, ...) log_meta(LOG_DEBUG, __FILE__, __LINE__, __func__, "DHCP CLIENT (0x%x): " fmt, client->xid, ##__VA_ARGS__) +#define log_dhcp_client(client, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "DHCP CLIENT (0x%x): " fmt, client->xid, ##__VA_ARGS__) diff --git a/src/libsystemd-network/dhcp-lease-internal.h b/src/libsystemd-network/dhcp-lease-internal.h index d4675f3e47..9e184ac4b5 100644 --- a/src/libsystemd-network/dhcp-lease-internal.h +++ b/src/libsystemd-network/dhcp-lease-internal.h @@ -35,7 +35,7 @@ struct sd_dhcp_route { struct in_addr dst_addr; struct in_addr gw_addr; - uint8_t dst_prefixlen; + unsigned char dst_prefixlen; }; struct sd_dhcp_lease { @@ -70,16 +70,18 @@ struct sd_dhcp_lease { char *domainname; char *hostname; char *root_path; + uint8_t *client_id; + size_t client_id_len; }; int dhcp_lease_new(sd_dhcp_lease **ret); int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option, void *user_data); -int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file); -int dhcp_lease_load(const char *lease_file, sd_dhcp_lease **ret); - int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease); +int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const uint8_t *client_id, + size_t client_id_len); + DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_lease*, sd_dhcp_lease_unref); #define _cleanup_dhcp_lease_unref_ _cleanup_(sd_dhcp_lease_unrefp) diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h index 480da22c01..eb7d6d44d3 100644 --- a/src/libsystemd-network/dhcp-server-internal.h +++ b/src/libsystemd-network/dhcp-server-internal.h @@ -79,7 +79,7 @@ typedef struct DHCPRequest { DEFINE_TRIVIAL_CLEANUP_FUNC(sd_dhcp_server*, sd_dhcp_server_unref); #define _cleanup_dhcp_server_unref_ _cleanup_(sd_dhcp_server_unrefp) -#define log_dhcp_server(client, fmt, ...) log_meta(LOG_DEBUG, __FILE__, __LINE__, __func__, "DHCP SERVER: " fmt, ##__VA_ARGS__) +#define log_dhcp_server(client, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "DHCP SERVER: " fmt, ##__VA_ARGS__) int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, size_t length); diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index 6cc0aa8a8d..4f54ad89a6 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -56,7 +56,7 @@ struct DHCP6IA { typedef struct DHCP6IA DHCP6IA; -#define log_dhcp6_client(p, fmt, ...) log_meta(LOG_DEBUG, __FILE__, __LINE__, __func__, "DHCPv6 CLIENT: " fmt, ##__VA_ARGS__) +#define log_dhcp6_client(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "DHCPv6 CLIENT: " fmt, ##__VA_ARGS__) int dhcp_network_icmp6_bind_router_solicitation(int index); int dhcp_network_icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr); diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index e6a31778f4..ea863f45e4 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -24,31 +24,37 @@ #include <string.h> #include "sparse-endian.h" +#include "unaligned.h" #include "util.h" #include "dhcp6-internal.h" #include "dhcp6-protocol.h" -#define DHCP6_OPTION_HDR_LEN 4 #define DHCP6_OPTION_IA_NA_LEN 12 #define DHCP6_OPTION_IA_TA_LEN 4 +typedef struct DHCP6Option { + be16_t code; + be16_t len; + uint8_t data[]; +} _packed_ DHCP6Option; + static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, size_t optlen) { + DHCP6Option *option = (DHCP6Option*) *buf; + assert_return(buf, -EINVAL); assert_return(*buf, -EINVAL); assert_return(buflen, -EINVAL); - if (optlen > 0xffff || *buflen < optlen + DHCP6_OPTION_HDR_LEN) + if (optlen > 0xffff || *buflen < optlen + sizeof(DHCP6Option)) return -ENOBUFS; - (*buf)[0] = optcode >> 8; - (*buf)[1] = optcode & 0xff; - (*buf)[2] = optlen >> 8; - (*buf)[3] = optlen & 0xff; + option->code = htobe16(optcode); + option->len = htobe16(optlen); - *buf += DHCP6_OPTION_HDR_LEN; - *buflen -= DHCP6_OPTION_HDR_LEN; + *buf += sizeof(DHCP6Option); + *buflen -= sizeof(DHCP6Option); return 0; } @@ -100,8 +106,8 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { ia_hdr = *buf; ia_buflen = *buflen; - *buf += DHCP6_OPTION_HDR_LEN; - *buflen -= DHCP6_OPTION_HDR_LEN; + *buf += sizeof(DHCP6Option); + *buflen -= sizeof(DHCP6Option); memcpy(*buf, &ia->id, len); @@ -119,7 +125,7 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { *buf += sizeof(addr->iaaddr); *buflen -= sizeof(addr->iaaddr); - ia_addrlen += DHCP6_OPTION_HDR_LEN + sizeof(addr->iaaddr); + ia_addrlen += sizeof(DHCP6Option) + sizeof(addr->iaaddr); } r = option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len + ia_addrlen); @@ -130,23 +136,23 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { } -static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *opt, - size_t *optlen) { +static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen) { + DHCP6Option *option = (DHCP6Option*) *buf; uint16_t len; assert_return(buf, -EINVAL); - assert_return(opt, -EINVAL); + assert_return(optcode, -EINVAL); assert_return(optlen, -EINVAL); - if (*buflen < 4) + if (*buflen < sizeof(DHCP6Option)) return -ENOMSG; - len = (*buf)[2] << 8 | (*buf)[3]; + len = be16toh(option->len); if (len > *buflen) return -ENOMSG; - *opt = (*buf)[0] << 8 | (*buf)[1]; + *optcode = be16toh(option->code); *optlen = len; *buf += 4; @@ -190,7 +196,7 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, switch (iatype) { case DHCP6_OPTION_IA_NA: - if (*buflen < DHCP6_OPTION_IA_NA_LEN + DHCP6_OPTION_HDR_LEN + + if (*buflen < DHCP6_OPTION_IA_NA_LEN + sizeof(DHCP6Option) + sizeof(addr->iaaddr)) { r = -ENOBUFS; goto error; @@ -212,7 +218,7 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, break; case DHCP6_OPTION_IA_TA: - if (*buflen < DHCP6_OPTION_IA_TA_LEN + DHCP6_OPTION_HDR_LEN + + if (*buflen < DHCP6_OPTION_IA_TA_LEN + sizeof(DHCP6Option) + sizeof(addr->iaaddr)) { r = -ENOBUFS; goto error; diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h index eaa671711f..3e0f339237 100644 --- a/src/libsystemd-network/dhcp6-protocol.h +++ b/src/libsystemd-network/dhcp6-protocol.h @@ -51,6 +51,8 @@ enum { DHCP6_PORT_CLIENT = 546, }; +#define DHCP6_INF_TIMEOUT 1 * USEC_PER_SEC +#define DHCP6_INF_MAX_RT 120 * USEC_PER_SEC #define DHCP6_SOL_MAX_DELAY 1 * USEC_PER_SEC #define DHCP6_SOL_TIMEOUT 1 * USEC_PER_SEC #define DHCP6_SOL_MAX_RT 120 * USEC_PER_SEC @@ -71,6 +73,7 @@ enum { enum DHCP6State { DHCP6_STATE_STOPPED = 0, + DHCP6_STATE_INFORMATION_REQUEST = 1, DHCP6_STATE_SOLICITATION = 2, DHCP6_STATE_REQUEST = 3, DHCP6_STATE_BOUND = 4, diff --git a/src/libsystemd-network/ipv4ll-internal.h b/src/libsystemd-network/ipv4ll-internal.h index fe5ef8e2b8..ae0ce43985 100644 --- a/src/libsystemd-network/ipv4ll-internal.h +++ b/src/libsystemd-network/ipv4ll-internal.h @@ -35,4 +35,4 @@ void arp_packet_probe(struct ether_arp *arp, be32_t pa, const struct ether_addr void arp_packet_announcement(struct ether_arp *arp, be32_t pa, const struct ether_addr *ha); int arp_packet_verify_headers(struct ether_arp *arp); -#define log_ipv4ll(ll, fmt, ...) log_meta(LOG_DEBUG, __FILE__, __LINE__, __func__, "IPv4LL: " fmt, ##__VA_ARGS__) +#define log_ipv4ll(ll, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "IPv4LL: " fmt, ##__VA_ARGS__) diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c index 372f3ed371..6852a7129a 100644 --- a/src/libsystemd-network/network-internal.c +++ b/src/libsystemd-network/network-internal.c @@ -98,38 +98,38 @@ bool net_match_config(const struct ether_addr *match_mac, const char *dev_type, const char *dev_name) { - if (match_host && !condition_test_host(match_host)) - return 0; + if (match_host && !condition_test(match_host)) + return false; - if (match_virt && !condition_test_virtualization(match_virt)) - return 0; + if (match_virt && !condition_test(match_virt)) + return false; - if (match_kernel && !condition_test_kernel_command_line(match_kernel)) - return 0; + if (match_kernel && !condition_test(match_kernel)) + return false; - if (match_arch && !condition_test_architecture(match_arch)) - return 0; + if (match_arch && !condition_test(match_arch)) + return false; if (match_mac && (!dev_mac || memcmp(match_mac, dev_mac, ETH_ALEN))) - return 0; + return false; if (match_path && (!dev_path || fnmatch(match_path, dev_path, 0))) - return 0; + return false; if (match_driver) { if (dev_parent_driver && !streq(match_driver, dev_parent_driver)) - return 0; + return false; else if (!streq_ptr(match_driver, dev_driver)) - return 0; + return false; } if (match_type && !streq_ptr(match_type, dev_type)) - return 0; + return false; if (match_name && (!dev_name || fnmatch(match_name, dev_name, 0))) - return 0; + return false; - return 1; + return true; } int config_parse_net_condition(const char *unit, @@ -392,10 +392,12 @@ void serialize_dhcp_routes(FILE *f, const char *key, struct sd_dhcp_route *route fprintf(f, "%s=", key); - for (i = 0; i < size; i++) - fprintf(f, "%s/%" PRIu8 ",%s%s", inet_ntoa(routes[i].dst_addr), - routes[i].dst_prefixlen, inet_ntoa(routes[i].gw_addr), + for (i = 0; i < size; i++) { + fprintf(f, "%s/%" PRIu8, inet_ntoa(routes[i].dst_addr), + routes[i].dst_prefixlen); + fprintf(f, ",%s%s", inet_ntoa(routes[i].gw_addr), (i < (size - 1)) ? " ": ""); + } fputs("\n", f); } diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h index 49387d03cf..c64db2e79d 100644 --- a/src/libsystemd-network/network-internal.h +++ b/src/libsystemd-network/network-internal.h @@ -26,7 +26,7 @@ #include <stdbool.h> #include "udev.h" -#include "condition-util.h" +#include "condition.h" bool net_match_config(const struct ether_addr *match_mac, const char *match_path, diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 0eba4c379d..b2b72befbf 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -38,6 +38,7 @@ #include "dhcp-lease-internal.h" #include "sd-dhcp-client.h" +#define MAX_CLIENT_ID_LEN 64 /* Arbitrary limit */ #define MAX_MAC_ADDR_LEN INFINIBAND_ALEN struct sd_dhcp_client { @@ -56,19 +57,38 @@ struct sd_dhcp_client { size_t req_opts_allocated; size_t req_opts_size; be32_t last_addr; - struct { - uint8_t type; - struct ether_addr mac_addr; - } _packed_ client_id; uint8_t mac_addr[MAX_MAC_ADDR_LEN]; size_t mac_addr_len; uint16_t arp_type; + union { + struct { + uint8_t type; /* 0: Generic (non-LL) (RFC 2132) */ + uint8_t data[MAX_CLIENT_ID_LEN]; + } _packed_ gen; + struct { + uint8_t type; /* 1: Ethernet Link-Layer (RFC 2132) */ + uint8_t haddr[ETH_ALEN]; + } _packed_ eth; + struct { + uint8_t type; /* 2 - 254: ARP/Link-Layer (RFC 2132) */ + uint8_t haddr[0]; + } _packed_ ll; + struct { + uint8_t type; /* 255: Node-specific (RFC 4361) */ + uint8_t iaid[4]; + uint8_t duid[MAX_CLIENT_ID_LEN - 4]; + } _packed_ ns; + struct { + uint8_t type; + uint8_t data[MAX_CLIENT_ID_LEN]; + } _packed_ raw; + } client_id; + size_t client_id_len; char *hostname; char *vendor_class_identifier; uint32_t mtu; uint32_t xid; usec_t start_time; - uint16_t secs; unsigned int attempt; usec_t request_sent; sd_event_source *timeout_t1; @@ -201,8 +221,70 @@ int sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr, client->mac_addr_len = addr_len; client->arp_type = arp_type; - memcpy(&client->client_id.mac_addr, addr, ETH_ALEN); - client->client_id.type = 0x01; + if (need_restart && client->state != DHCP_STATE_STOPPED) + sd_dhcp_client_start(client); + + return 0; +} + +int sd_dhcp_client_get_client_id(sd_dhcp_client *client, uint8_t *type, + const uint8_t **data, size_t *data_len) { + + assert_return(client, -EINVAL); + assert_return(type, -EINVAL); + assert_return(data, -EINVAL); + assert_return(data_len, -EINVAL); + + *type = 0; + *data = NULL; + *data_len = 0; + if (client->client_id_len) { + *type = client->client_id.raw.type; + *data = client->client_id.raw.data; + *data_len = client->client_id_len - + sizeof (client->client_id.raw.type); + } + + return 0; +} + +int sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type, + const uint8_t *data, size_t data_len) { + DHCP_CLIENT_DONT_DESTROY(client); + bool need_restart = false; + + assert_return(client, -EINVAL); + assert_return(data, -EINVAL); + assert_return(data_len > 0 && data_len <= MAX_CLIENT_ID_LEN, -EINVAL); + + switch (type) { + case ARPHRD_ETHER: + if (data_len != ETH_ALEN) + return -EINVAL; + break; + case ARPHRD_INFINIBAND: + if (data_len != INFINIBAND_ALEN) + return -EINVAL; + break; + default: + break; + } + + if (client->client_id_len == data_len + sizeof (client->client_id.raw.type) && + client->client_id.raw.type == type && + memcmp(&client->client_id.raw.data, data, data_len) == 0) + return 0; + + if (!IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_STOPPED)) { + log_dhcp_client(client, "Changing client ID on running DHCP " + "client, restarting"); + need_restart = true; + client_stop(client, DHCP_EVENT_STOP); + } + + client->client_id.raw.type = type; + memcpy(&client->client_id.raw.data, data, data_len); + client->client_id_len = data_len + sizeof (client->client_id.raw.type); if (need_restart && client->state != DHCP_STATE_STOPPED) sd_dhcp_client_start(client); @@ -321,10 +403,12 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret, _cleanup_free_ DHCPPacket *packet; size_t optlen, optoffset, size; be16_t max_size; + usec_t time_now; + uint16_t secs; int r; assert(client); - assert(client->secs); + assert(client->start_time); assert(ret); assert(_optlen); assert(_optoffset); @@ -344,7 +428,15 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret, /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers refuse to issue an DHCP lease if 'secs' is set to zero */ - packet->dhcp.secs = htobe16(client->secs); + r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); + if (r < 0) + return r; + assert(time_now >= client->start_time); + + /* seconds between sending first and last DISCOVER + * must always be strictly positive to deal with broken servers */ + secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1; + packet->dhcp.secs = htobe16(secs); /* RFC2132 section 4.1 A client that cannot receive unicast IP datagrams until its protocol @@ -369,14 +461,24 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret, if (client->arp_type == ARPHRD_ETHER) memcpy(&packet->dhcp.chaddr, &client->mac_addr, ETH_ALEN); + /* If no client identifier exists, construct one from an ethernet + address if present */ + if (client->client_id_len == 0 && client->arp_type == ARPHRD_ETHER) { + client->client_id.eth.type = ARPHRD_ETHER; + memcpy(&client->client_id.eth.haddr, &client->mac_addr, ETH_ALEN); + client->client_id_len = sizeof (client->client_id.eth); + } + /* Some DHCP servers will refuse to issue an DHCP lease if the Client Identifier option is not set */ - r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0, - DHCP_OPTION_CLIENT_IDENTIFIER, - sizeof(client->client_id), &client->client_id); - if (r < 0) - return r; - + if (client->client_id_len) { + r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0, + DHCP_OPTION_CLIENT_IDENTIFIER, + client->client_id_len, + &client->client_id.raw); + if (r < 0) + return r; + } /* RFC2131 section 3.5: in its initial DHCPDISCOVER or DHCPREQUEST message, a @@ -441,24 +543,12 @@ static int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet, static int client_send_discover(sd_dhcp_client *client) { _cleanup_free_ DHCPPacket *discover = NULL; size_t optoffset, optlen; - usec_t time_now; int r; assert(client); assert(client->state == DHCP_STATE_INIT || client->state == DHCP_STATE_SELECTING); - /* See RFC2131 section 4.4.1 */ - - r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); - if (r < 0) - return r; - assert(time_now >= client->start_time); - - /* seconds between sending first and last DISCOVER - * must always be strictly positive to deal with broken servers */ - client->secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1; - r = client_message_init(client, &discover, DHCP_DISCOVER, &optlen, &optoffset); if (r < 0) @@ -723,8 +813,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, if (r < 0) goto error; - r = sd_event_source_set_name(client->timeout_resend, - "dhcp4-resend-timer"); + r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer"); if (r < 0) goto error; @@ -801,8 +890,7 @@ static int client_initialize_io_events(sd_dhcp_client *client, if (r < 0) goto error; - r = sd_event_source_set_name(client->receive_message, - "dhcp4-receive-message"); + r = sd_event_source_set_description(client->receive_message, "dhcp4-receive-message"); if (r < 0) goto error; @@ -832,8 +920,7 @@ static int client_initialize_time_events(sd_dhcp_client *client) { r = sd_event_source_set_priority(client->timeout_resend, client->event_priority); - r = sd_event_source_set_name(client->timeout_resend, - "dhcp4-resend-timer"); + r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer"); if (r < 0) goto error; @@ -875,10 +962,8 @@ static int client_start(sd_dhcp_client *client) { } client->fd = r; - if (client->state == DHCP_STATE_INIT) { + if (client->state == DHCP_STATE_INIT || client->state == DHCP_STATE_INIT_REBOOT) client->start_time = now(clock_boottime_or_monotonic()); - client->secs = 0; - } return client_initialize_events(client, client_receive_message_raw); } @@ -944,6 +1029,14 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, if (r < 0) return r; + if (client->client_id_len) { + r = dhcp_lease_set_client_id(lease, + (uint8_t *) &client->client_id.raw, + client->client_id_len); + if (r < 0) + return r; + } + r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease); if (r != DHCP_OFFER) { log_dhcp_client(client, "received message was not an OFFER, ignoring"); @@ -1003,6 +1096,14 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, if (r < 0) return r; + if (client->client_id_len) { + r = dhcp_lease_set_client_id(lease, + (uint8_t *) &client->client_id.raw, + client->client_id_len); + if (r < 0) + return r; + } + r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease); if (r == DHCP_NAK) { log_dhcp_client(client, "NAK"); @@ -1149,8 +1250,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { if (r < 0) return r; - r = sd_event_source_set_name(client->timeout_expire, - "dhcp4-lifetime"); + r = sd_event_source_set_description(client->timeout_expire, "dhcp4-lifetime"); if (r < 0) return r; @@ -1177,8 +1277,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { if (r < 0) return r; - r = sd_event_source_set_name(client->timeout_t2, - "dhcp4-t2-timeout"); + r = sd_event_source_set_description(client->timeout_t2, "dhcp4-t2-timeout"); if (r < 0) return r; @@ -1204,8 +1303,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { if (r < 0) return r; - r = sd_event_source_set_name(client->timeout_t1, - "dhcp4-t1-timer"); + r = sd_event_source_set_description(client->timeout_t1, "dhcp4-t1-timer"); if (r < 0) return r; @@ -1250,8 +1348,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, if (r < 0) goto error; - r = sd_event_source_set_name(client->timeout_resend, - "dhcp4-resend-timer"); + r = sd_event_source_set_description(client->timeout_resend, "dhcp4-resend-timer"); if (r < 0) goto error; } else if (r == -ENOMSG) @@ -1269,6 +1366,9 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, if (r >= 0) { client->timeout_resend = sd_event_source_unref(client->timeout_resend); + client->receive_message = + sd_event_source_unref(client->receive_message); + client->fd = asynchronous_close(client->fd); if (IN_SET(client->state, DHCP_STATE_REQUESTING, DHCP_STATE_REBOOTING)) diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c index 4fb01c0729..22a4af6837 100644 --- a/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libsystemd-network/sd-dhcp-lease.c @@ -30,6 +30,7 @@ #include "list.h" #include "mkdir.h" #include "fileio.h" +#include "unaligned.h" #include "in-addr-util.h" #include "dhcp-protocol.h" @@ -50,7 +51,7 @@ int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr) { int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime) { assert_return(lease, -EINVAL); - assert_return(lease, -EINVAL); + assert_return(lifetime, -EINVAL); *lifetime = lease->lifetime; @@ -198,6 +199,7 @@ sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) { free(lease->dns); free(lease->ntp); free(lease->static_route); + free(lease->client_id); free(lease); } @@ -205,14 +207,11 @@ sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease) { } static void lease_parse_u32(const uint8_t *option, size_t len, uint32_t *ret, uint32_t min) { - be32_t val; - assert(option); assert(ret); if (len == 4) { - memcpy(&val, option, 4); - *ret = be32toh(val); + *ret = unaligned_read_be32((be32_t*) option); if (*ret < min) *ret = min; @@ -224,14 +223,11 @@ static void lease_parse_s32(const uint8_t *option, size_t len, int32_t *ret) { } static void lease_parse_u16(const uint8_t *option, size_t len, uint16_t *ret, uint16_t min) { - be16_t val; - assert(option); assert(ret); if (len == 2) { - memcpy(&val, option, 2); - *ret = be16toh(val); + *ret = unaligned_read_be16((be16_t*) option); if (*ret < min) *ret = min; @@ -315,23 +311,6 @@ static int lease_parse_in_addrs_pairs(const uint8_t *option, size_t len, struct return lease_parse_in_addrs_aux(option, len, ret, ret_size, 2); } -static int class_prefixlen(uint8_t msb_octet, uint8_t *ret) { - if (msb_octet < 128) - /* Class A */ - *ret = 8; - else if (msb_octet < 192) - /* Class B */ - *ret = 16; - else if (msb_octet < 224) - /* Class C */ - *ret = 24; - else - /* Class D or E -- no subnet mask */ - return -ERANGE; - - return 0; -} - static int lease_parse_routes(const uint8_t *option, size_t len, struct sd_dhcp_route **routes, size_t *routes_size, size_t *routes_allocated) { @@ -353,8 +332,10 @@ static int lease_parse_routes(const uint8_t *option, size_t len, struct sd_dhcp_ while (len >= 8) { struct sd_dhcp_route *route = *routes + *routes_size; + int r; - if (class_prefixlen(*option, &route->dst_prefixlen) < 0) { + r = in_addr_default_prefixlen((struct in_addr*) option, &route->dst_prefixlen); + if (r < 0) { log_error("Failed to determine destination prefix length from class based IP, ignoring"); continue; } @@ -600,11 +581,13 @@ int dhcp_lease_new(sd_dhcp_lease **ret) { return 0; } -int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { +int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { _cleanup_free_ char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; struct in_addr address; const struct in_addr *addresses; + const uint8_t *client_id; + size_t client_id_len; const char *string; uint16_t mtu; struct sd_dhcp_route *routes; @@ -678,6 +661,18 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { if (r >= 0) serialize_dhcp_routes(f, "ROUTES", routes, r); + r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len); + if (r >= 0) { + _cleanup_free_ char *client_id_hex; + + client_id_hex = hexmem (client_id, client_id_len); + if (!client_id_hex) { + r = -ENOMEM; + goto finish; + } + fprintf(f, "CLIENTID=%s\n", client_id_hex); + } + r = 0; fflush(f); @@ -690,16 +685,17 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { finish: if (r < 0) - log_error("Failed to save lease data %s: %s", lease_file, strerror(-r)); + log_error_errno(r, "Failed to save lease data %s: %m", lease_file); return r; } -int dhcp_lease_load(const char *lease_file, sd_dhcp_lease **ret) { +int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL; _cleanup_free_ char *address = NULL, *router = NULL, *netmask = NULL, *server_address = NULL, *next_server = NULL, - *dns = NULL, *ntp = NULL, *mtu = NULL, *routes = NULL; + *dns = NULL, *ntp = NULL, *mtu = NULL, + *routes = NULL, *client_id_hex = NULL; struct in_addr addr; int r; @@ -723,13 +719,13 @@ int dhcp_lease_load(const char *lease_file, sd_dhcp_lease **ret) { "HOSTNAME", &lease->hostname, "ROOT_PATH", &lease->root_path, "ROUTES", &routes, + "CLIENTID", &client_id_hex, NULL); if (r < 0) { if (r == -ENOENT) return 0; - log_error("Failed to read %s: %s", lease_file, strerror(-r)); - return r; + return log_error_errno(r, "Failed to read %s: %m", lease_file); } r = inet_pton(AF_INET, address, &addr); @@ -797,6 +793,16 @@ int dhcp_lease_load(const char *lease_file, sd_dhcp_lease **ret) { return r; } + if (client_id_hex) { + if (strlen (client_id_hex) % 2) + return -EINVAL; + + lease->client_id = unhexmem (client_id_hex, strlen (client_id_hex)); + if (!lease->client_id) + return -ENOMEM; + lease->client_id_len = strlen (client_id_hex) / 2; + } + *ret = lease; lease = NULL; @@ -821,3 +827,32 @@ int dhcp_lease_set_default_subnet_mask(sd_dhcp_lease *lease) { return 0; } + +int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const uint8_t **client_id, + size_t *client_id_len) { + assert_return(lease, -EINVAL); + assert_return(client_id, -EINVAL); + assert_return(client_id_len, -EINVAL); + + *client_id = lease->client_id; + *client_id_len = lease->client_id_len; + return 0; +} + +int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const uint8_t *client_id, + size_t client_id_len) { + assert_return(lease, -EINVAL); + assert_return((!client_id && !client_id_len) || + (client_id && client_id_len), -EINVAL); + + free (lease->client_id); + lease->client_id = NULL; + lease->client_id_len = 0; + + if (client_id) { + lease->client_id = memdup (client_id, client_id_len); + lease->client_id_len = client_id_len; + } + + return 0; +} diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index fa4f9b5dc2..017371e837 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -62,6 +62,7 @@ struct sd_dhcp6_client { usec_t transaction_start; struct sd_dhcp6_lease *lease; int fd; + bool information_request; be16_t *req_opts; size_t req_opts_allocated; size_t req_opts_len; @@ -200,19 +201,19 @@ int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *du switch (type) { case DHCP6_DUID_LLT: - if (duid_len <= sizeof(client->duid.llt)) + if (duid_len <= sizeof(client->duid.llt) - 2) return -EINVAL; break; case DHCP6_DUID_EN: - if (duid_len != sizeof(client->duid.en)) + if (duid_len != sizeof(client->duid.en) - 2) return -EINVAL; break; case DHCP6_DUID_LL: - if (duid_len <= sizeof(client->duid.ll)) + if (duid_len <= sizeof(client->duid.ll) - 2) return -EINVAL; break; case DHCP6_DUID_UUID: - if (duid_len != sizeof(client->duid.uuid)) + if (duid_len != sizeof(client->duid.uuid) - 2) return -EINVAL; break; default: @@ -222,7 +223,26 @@ int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *du client->duid.raw.type = htobe16(type); memcpy(&client->duid.raw.data, duid, duid_len); - client->duid_len = duid_len; + client->duid_len = duid_len + 2; /* +2 for sizeof(type) */ + + return 0; +} + +int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, + bool enabled) { + assert_return(client, -EINVAL); + + client->information_request = enabled; + + return 0; +} + +int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, + bool *enabled) { + assert_return(client, -EINVAL); + assert_return(enabled, -EINVAL); + + *enabled = client->information_request; return 0; } @@ -333,6 +353,11 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { message->transaction_id = client->transaction_id; switch(client->state) { + case DHCP6_STATE_INFORMATION_REQUEST: + message->type = DHCP6_INFORMATION_REQUEST; + + break; + case DHCP6_STATE_SOLICITATION: message->type = DHCP6_SOLICIT; @@ -494,6 +519,12 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, client->timeout_resend = sd_event_source_unref(client->timeout_resend); switch (client->state) { + case DHCP6_STATE_INFORMATION_REQUEST: + init_retransmit_time = DHCP6_INF_TIMEOUT; + max_retransmit_time = DHCP6_INF_MAX_RT; + + break; + case DHCP6_STATE_SOLICITATION: if (client->retransmit_count && client->lease) { @@ -590,8 +621,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, if (r < 0) goto error; - r = sd_event_source_set_name(client->timeout_resend, - "dhcp6-resend-timer"); + r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timer"); if (r < 0) goto error; @@ -614,8 +644,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, if (r < 0) goto error; - r = sd_event_source_set_name(client->timeout_resend_expire, - "dhcp6-resend-expire-timer"); + r = sd_event_source_set_description(client->timeout_resend_expire, "dhcp6-resend-expire-timer"); if (r < 0) goto error; } @@ -746,6 +775,12 @@ static int client_parse_message(sd_dhcp6_client *client, break; case DHCP6_OPTION_IA_NA: + if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { + log_dhcp6_client(client, "Information request ignoring IA NA option"); + + break; + } + r = dhcp6_option_parse_ia(&optval, &optlen, optcode, &lease->ia); if (r < 0 && r != -ENOMSG) @@ -772,16 +807,21 @@ static int client_parse_message(sd_dhcp6_client *client, } } - if ((r < 0 && r != -ENOMSG) || !clientid) { + if (r == -ENOMSG) + r = 0; + + if (r < 0 || !clientid) { log_dhcp6_client(client, "%s has incomplete options", dhcp6_message_type_to_string(message->type)); return -EINVAL; } - r = dhcp6_lease_get_serverid(lease, &id, &id_len); - if (r < 0) - log_dhcp6_client(client, "%s has no server id", - dhcp6_message_type_to_string(message->type)); + if (client->state != DHCP6_STATE_INFORMATION_REQUEST) { + r = dhcp6_lease_get_serverid(lease, &id, &id_len); + if (r < 0) + log_dhcp6_client(client, "%s has no server id", + dhcp6_message_type_to_string(message->type)); + } return r; } @@ -813,12 +853,15 @@ static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, return 0; } - if (client->lease) + if (client->lease) { dhcp6_lease_clear_timers(&client->lease->ia); + client->lease = sd_dhcp6_lease_unref(client->lease); + } - client->lease = sd_dhcp6_lease_unref(client->lease); - client->lease = lease; - lease = NULL; + if (client->state != DHCP6_STATE_INFORMATION_REQUEST) { + client->lease = lease; + lease = NULL; + } return DHCP6_STATE_BOUND; } @@ -845,7 +888,8 @@ static int client_receive_advertise(sd_dhcp6_client *client, return r; r = dhcp6_lease_get_preference(client->lease, &pref_lease); - if (!client->lease || r < 0 || pref_advertise > pref_lease) { + + if (r < 0 || pref_advertise > pref_lease) { sd_dhcp6_lease_unref(client->lease); client->lease = lease; lease = NULL; @@ -912,6 +956,17 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents, return 0; switch (client->state) { + case DHCP6_STATE_INFORMATION_REQUEST: + r = client_receive_reply(client, message, len); + if (r < 0) + return 0; + + client_notify(client, DHCP6_EVENT_INFORMATION_REQUEST); + + client_start(client, DHCP6_STATE_STOPPED); + + break; + case DHCP6_STATE_SOLICITATION: r = client_receive_advertise(client, message, len); @@ -970,6 +1025,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) assert_return(client->index > 0, -EINVAL); assert_return(client->state != state, -EINVAL); + log_dhcp6_client(client, "client state %d new state %d", client->state, state); client->timeout_resend_expire = sd_event_source_unref(client->timeout_resend_expire); client->timeout_resend = sd_event_source_unref(client->timeout_resend); @@ -987,38 +1043,19 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) switch (state) { case DHCP6_STATE_STOPPED: - case DHCP6_STATE_SOLICITATION: + if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { + client->state = DHCP6_STATE_STOPPED; - r = client_ensure_iaid(client); - if (r < 0) - return r; - - r = dhcp6_network_bind_udp_socket(client->index, NULL); - if (r < 0) - return r; - - client->fd = r; - - r = sd_event_add_io(client->event, &client->receive_message, - client->fd, EPOLLIN, client_receive_message, - client); - if (r < 0) - return r; - - r = sd_event_source_set_priority(client->receive_message, - client->event_priority); - if (r < 0) - return r; - - r = sd_event_source_set_name(client->receive_message, - "dhcp6-receive-message"); - if (r < 0) - return r; + return 0; + } + /* fall through */ + case DHCP6_STATE_SOLICITATION: client->state = DHCP6_STATE_SOLICITATION; break; + case DHCP6_STATE_INFORMATION_REQUEST: case DHCP6_STATE_REQUEST: case DHCP6_STATE_RENEW: case DHCP6_STATE_REBIND: @@ -1059,8 +1096,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) if (r < 0) return r; - r = sd_event_source_set_name(client->lease->ia.timeout_t1, - "dhcp6-t1-timeout"); + r = sd_event_source_set_description(client->lease->ia.timeout_t1, "dhcp6-t1-timeout"); if (r < 0) return r; @@ -1084,8 +1120,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) if (r < 0) return r; - r = sd_event_source_set_name(client->lease->ia.timeout_t2, - "dhcp6-t2-timeout"); + r = sd_event_source_set_description(client->lease->ia.timeout_t2, "dhcp6-t2-timeout"); if (r < 0) return r; @@ -1108,8 +1143,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) if (r < 0) return r; - r = sd_event_source_set_name(client->timeout_resend, - "dhcp6-resend-timeout"); + r = sd_event_source_set_description(client->timeout_resend, "dhcp6-resend-timeout"); if (r < 0) return r; @@ -1126,6 +1160,7 @@ int sd_dhcp6_client_stop(sd_dhcp6_client *client) int sd_dhcp6_client_start(sd_dhcp6_client *client) { int r = 0; + enum DHCP6State state = DHCP6_STATE_SOLICITATION; assert_return(client, -EINVAL); assert_return(client->event, -EINVAL); @@ -1135,7 +1170,44 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) if (r < 0) return r; - return client_start(client, DHCP6_STATE_SOLICITATION); + r = client_ensure_iaid(client); + if (r < 0) + return r; + + r = dhcp6_network_bind_udp_socket(client->index, NULL); + if (r < 0) + return r; + + client->fd = r; + + r = sd_event_add_io(client->event, &client->receive_message, + client->fd, EPOLLIN, client_receive_message, + client); + if (r < 0) + goto error; + + r = sd_event_source_set_priority(client->receive_message, + client->event_priority); + if (r < 0) + goto error; + + r = sd_event_source_set_description(client->receive_message, + "dhcp6-receive-message"); + if (r < 0) + goto error; + + if (client->information_request) + state = DHCP6_STATE_INFORMATION_REQUEST; + + log_dhcp6_client(client, "Started in %s mode", + client->information_request? "Information request": + "Managed"); + + return client_start(client, state); + +error: + client_reset(client); + return r; } int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index e2715ea659..8960fac92f 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -110,9 +110,11 @@ int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference) { } int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference) { - assert_return(lease, -EINVAL); assert_return(preference, -EINVAL); + if (!lease) + return -EINVAL; + *preference = lease->preference; return 0; diff --git a/src/libsystemd-network/sd-icmp6-nd.c b/src/libsystemd-network/sd-icmp6-nd.c index bbb4531ddb..cb06151514 100644 --- a/src/libsystemd-network/sd-icmp6-nd.c +++ b/src/libsystemd-network/sd-icmp6-nd.c @@ -54,7 +54,7 @@ struct sd_icmp6_nd { void *userdata; }; -#define log_icmp6_nd(p, fmt, ...) log_meta(LOG_DEBUG, __FILE__, __LINE__, __func__, "ICMPv6 CLIENT: " fmt, ##__VA_ARGS__) +#define log_icmp6_nd(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "ICMPv6 CLIENT: " fmt, ##__VA_ARGS__) static void icmp6_nd_notify(sd_icmp6_nd *nd, int event) { @@ -278,7 +278,7 @@ static int icmp6_router_solicitation_timeout(sd_event_source *s, uint64_t usec, return 0; } - r = sd_event_source_set_name(nd->timeout, "icmp6-timeout"); + r = sd_event_source_set_description(nd->timeout, "icmp6-timeout"); if (r < 0) { icmp6_nd_notify(nd, r); return 0; @@ -328,7 +328,7 @@ int sd_icmp6_router_solicitation_start(sd_icmp6_nd *nd) { if (r < 0) goto error; - r = sd_event_source_set_name(nd->recv, "icmp6-receive-message"); + r = sd_event_source_set_description(nd->recv, "icmp6-receive-message"); if (r < 0) goto error; @@ -341,7 +341,7 @@ int sd_icmp6_router_solicitation_start(sd_icmp6_nd *nd) { if (r < 0) goto error; - r = sd_event_source_set_name(nd->timeout, "icmp6-timeout"); + r = sd_event_source_set_description(nd->timeout, "icmp6-timeout"); error: if (r < 0) icmp6_nd_init(nd); diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c index 8b243319b6..8626d4afa9 100644 --- a/src/libsystemd-network/sd-ipv4ll.c +++ b/src/libsystemd-network/sd-ipv4ll.c @@ -353,7 +353,7 @@ static void ipv4ll_run_state_machine(sd_ipv4ll *ll, IPv4LLTrigger trigger, void if (r < 0) goto out; - r = sd_event_source_set_name(ll->timer, "ipv4ll-timer"); + r = sd_event_source_set_description(ll->timer, "ipv4ll-timer"); if (r < 0) goto out; } @@ -564,7 +564,7 @@ int sd_ipv4ll_start (sd_ipv4ll *ll) { if (r < 0) goto out; - r = sd_event_source_set_name(ll->receive_message, "ipv4ll-receive-message"); + r = sd_event_source_set_description(ll->receive_message, "ipv4ll-receive-message"); if (r < 0) goto out; @@ -581,7 +581,7 @@ int sd_ipv4ll_start (sd_ipv4ll *ll) { if (r < 0) goto out; - r = sd_event_source_set_name(ll->timer, "ipv4ll-timer"); + r = sd_event_source_set_description(ll->timer, "ipv4ll-timer"); out: if (r < 0) ipv4ll_stop(ll, IPV4LL_EVENT_STOP); diff --git a/src/libsystemd-network/sd-pppoe.c b/src/libsystemd-network/sd-pppoe.c new file mode 100644 index 0000000000..4f49b799ec --- /dev/null +++ b/src/libsystemd-network/sd-pppoe.c @@ -0,0 +1,802 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright (C) 2014 Tom Gundersen + + 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/>. +***/ + +/* See RFC 2516 */ + +#include <sys/ioctl.h> +#include <linux/ppp_defs.h> +#include <linux/ppp-ioctl.h> +#include <net/if.h> +#include <netinet/in.h> +#include <linux/if_pppox.h> + +#include "sd-pppoe.h" + +#include "event-util.h" + +#include "util.h" +#include "socket-util.h" +#include "async.h" +#include "refcnt.h" +#include "unaligned.h" +#include "utf8.h" + +#define PPPOE_MAX_PACKET_SIZE 1484 +#define PPPOE_MAX_PADR_RESEND 16 + +/* TODO: move this to socket-util.h without getting into + * a mess with the includes */ +union sockaddr_union_pppox { + struct sockaddr sa; + struct sockaddr_pppox pppox; +}; + +typedef enum PPPoEState { + PPPOE_STATE_INITIALIZING, + PPPOE_STATE_REQUESTING, + PPPOE_STATE_RUNNING, + PPPOE_STATE_STOPPED, + _PPPOE_STATE_MAX, + _PPPOE_STATE_INVALID = -1, +} PPPoEState; + +typedef struct PPPoETags { + char *service_name; + char *ac_name; + uint8_t *host_uniq; + size_t host_uniq_len; + uint8_t *cookie; + size_t cookie_len; +} PPPoETags; + +struct sd_pppoe { + RefCount n_ref; + + PPPoEState state; + uint64_t host_uniq; + + int ifindex; + char *ifname; + + sd_event *event; + int event_priority; + int fd; + sd_event_source *io; + sd_event_source *timeout; + int padr_resend_count; + + char *service_name; + struct ether_addr peer_mac; + be16_t session_id; + + int pppoe_fd; + int channel; + + sd_pppoe_cb_t cb; + void *userdata; + + PPPoETags tags; +}; + +#define PPPOE_PACKET_LENGTH(header) \ + be16toh((header)->length) + +#define PPPOE_PACKET_TAIL(packet) \ + (struct pppoe_tag*)((uint8_t*)(packet) + sizeof(struct pppoe_hdr) + PPPOE_PACKET_LENGTH(packet)) + +#define PPPOE_TAG_LENGTH(tag) \ + be16toh((tag)->tag_len) + +#define PPPOE_TAG_TYPE(tag) \ + (tag)->tag_type + +#define PPPOE_TAG_NEXT(tag) \ + (struct pppoe_tag *)((uint8_t *)(tag) + sizeof(struct pppoe_tag) + PPPOE_TAG_LENGTH(tag)) + +#define PPPOE_TAGS_FOREACH(tag, header) \ + for (tag = (header)->tag; \ + ((uint8_t *)(tag) + sizeof(struct pppoe_tag) < (uint8_t*)PPPOE_PACKET_TAIL(header)) && \ + (PPPOE_TAG_NEXT(tag) <= PPPOE_PACKET_TAIL(header)) && \ + (tag >= (header)->tag) && \ + (PPPOE_TAG_TYPE(tag) != PTT_EOL); \ + tag = PPPOE_TAG_NEXT(tag)) + +static void pppoe_tags_clear(PPPoETags *tags) { + free(tags->service_name); + free(tags->ac_name); + free(tags->host_uniq); + free(tags->cookie); + + zero(*tags); +} + +int sd_pppoe_set_ifindex(sd_pppoe *ppp, int ifindex) { + assert_return(ppp, -EINVAL); + assert_return(ifindex > 0, -EINVAL); + + ppp->ifindex = ifindex; + + return 0; +} + +int sd_pppoe_set_ifname(sd_pppoe *ppp, const char *ifname) { + char *name; + + assert_return(ppp, -EINVAL); + assert_return(ifname, -EINVAL); + + if (strlen(ifname) > IFNAMSIZ) + return -EINVAL; + + name = strdup(ifname); + if (!name) + return -ENOMEM; + + free(ppp->ifname); + ppp->ifname = name; + + return 0; +} + +int sd_pppoe_set_service_name(sd_pppoe *ppp, const char *service_name) { + _cleanup_free_ char *name = NULL; + + assert_return(ppp, -EINVAL); + + if (service_name) { + name = strdup(service_name); + if (!name) + return -ENOMEM; + } + + free(ppp->service_name); + ppp->service_name = name; + name = NULL; + + return 0; +} + +int sd_pppoe_attach_event(sd_pppoe *ppp, sd_event *event, int priority) { + int r; + + assert_return(ppp, -EINVAL); + assert_return(!ppp->event, -EBUSY); + + if (event) + ppp->event = sd_event_ref(event); + else { + r = sd_event_default(&ppp->event); + if (r < 0) + return r; + } + + ppp->event_priority = priority; + + return 0; +} + +int sd_pppoe_detach_event(sd_pppoe *ppp) { + assert_return(ppp, -EINVAL); + + ppp->event = sd_event_unref(ppp->event); + + return 0; +} + +sd_pppoe *sd_pppoe_ref(sd_pppoe *ppp) { + if (ppp) + assert_se(REFCNT_INC(ppp->n_ref) >= 2); + + return ppp; +} + +sd_pppoe *sd_pppoe_unref(sd_pppoe *ppp) { + if (ppp && REFCNT_DEC(ppp->n_ref) <= 0) { + pppoe_tags_clear(&ppp->tags); + free(ppp->ifname); + free(ppp->service_name); + sd_pppoe_stop(ppp); + sd_pppoe_detach_event(ppp); + + free(ppp); + } + + return NULL; +} + +int sd_pppoe_new (sd_pppoe **ret) { + sd_pppoe *ppp; + + assert_return(ret, -EINVAL); + + ppp = new0(sd_pppoe, 1); + if (!ppp) + return -ENOMEM; + + ppp->n_ref = REFCNT_INIT; + ppp->state = _PPPOE_STATE_INVALID; + ppp->ifindex = -1; + ppp->fd = -1; + ppp->pppoe_fd = -1; + ppp->padr_resend_count = PPPOE_MAX_PADR_RESEND; + + *ret = ppp; + + return 0; +} + +int sd_pppoe_get_channel(sd_pppoe *ppp, int *channel) { + assert_return(ppp, -EINVAL); + assert_return(channel, -EINVAL); + assert_return(ppp->pppoe_fd != -1, -EUNATCH); + assert_return(ppp->state == PPPOE_STATE_RUNNING, -EUNATCH); + + *channel = ppp->channel; + + return 0; +} + +int sd_pppoe_set_callback(sd_pppoe *ppp, sd_pppoe_cb_t cb, void *userdata) { + assert_return(ppp, -EINVAL); + + ppp->cb = cb; + ppp->userdata = userdata; + + return 0; +} + +static void pppoe_tag_append(struct pppoe_hdr *packet, size_t packet_size, be16_t tag_type, const void *tag_data, uint16_t tag_len) { + struct pppoe_tag *tag; + + assert(packet); + assert(sizeof(struct pppoe_hdr) + PPPOE_PACKET_LENGTH(packet) + sizeof(struct pppoe_tag) + tag_len <= packet_size); + assert(!(!tag_data ^ !tag_len)); + + tag = PPPOE_PACKET_TAIL(packet); + + tag->tag_len = htobe16(tag_len); + tag->tag_type = tag_type; + if (tag_data) + memcpy(tag->tag_data, tag_data, tag_len); + + packet->length = htobe16(PPPOE_PACKET_LENGTH(packet) + sizeof(struct pppoe_tag) + tag_len); +} + +static int pppoe_send(sd_pppoe *ppp, uint8_t code) { + union sockaddr_union link = { + .ll = { + .sll_family = AF_PACKET, + .sll_protocol = htons(ETH_P_PPP_DISC), + .sll_halen = ETH_ALEN, + }, + }; + _cleanup_free_ struct pppoe_hdr *packet = NULL; + int r; + + assert(ppp); + assert(ppp->fd != -1); + assert(IN_SET(code, PADI_CODE, PADR_CODE, PADT_CODE)); + + link.ll.sll_ifindex = ppp->ifindex; + if (code == PADI_CODE) + memset(&link.ll.sll_addr, 0xff, ETH_ALEN); + else + memcpy(&link.ll.sll_addr, &ppp->peer_mac, ETH_ALEN); + + packet = malloc0(PPPOE_MAX_PACKET_SIZE); + if (!packet) + return -ENOMEM; + + packet->ver = 0x1; + packet->type = 0x1; + packet->code = code; + if (code == PADT_CODE) + packet->sid = ppp->session_id; + + /* Service-Name */ + pppoe_tag_append(packet, PPPOE_MAX_PACKET_SIZE, PTT_SRV_NAME, + ppp->service_name, ppp->service_name ? strlen(ppp->service_name) : 0); + + /* AC-Cookie */ + if (code == PADR_CODE && ppp->tags.cookie) + pppoe_tag_append(packet, PPPOE_MAX_PACKET_SIZE, PTT_AC_COOKIE, + ppp->tags.cookie, ppp->tags.cookie_len); + + /* Host-Uniq */ + if (code != PADT_CODE) { + ppp->host_uniq = random_u64(); + + pppoe_tag_append(packet, PPPOE_MAX_PACKET_SIZE, PTT_HOST_UNIQ, + &ppp->host_uniq, sizeof(ppp->host_uniq)); + } + + r = sendto(ppp->fd, packet, sizeof(struct pppoe_hdr) + PPPOE_PACKET_LENGTH(packet), + 0, &link.sa, sizeof(link.ll)); + if (r < 0) + return -errno; + + return 0; +} + +static int pppoe_timeout(sd_event_source *s, uint64_t usec, void *userdata); + +static int pppoe_arm_timeout(sd_pppoe *ppp) { + _cleanup_event_source_unref_ sd_event_source *timeout = NULL; + usec_t next_timeout; + int r; + + assert(ppp); + + r = sd_event_now(ppp->event, clock_boottime_or_monotonic(), &next_timeout); + if (r == -ENODATA) + next_timeout = now(clock_boottime_or_monotonic()); + else if (r < 0) + return r; + + next_timeout += 500 * USEC_PER_MSEC; + + r = sd_event_add_time(ppp->event, &timeout, clock_boottime_or_monotonic(), next_timeout, + 10 * USEC_PER_MSEC, pppoe_timeout, ppp); + if (r < 0) + return r; + + r = sd_event_source_set_priority(timeout, ppp->event_priority); + if (r < 0) + return r; + + sd_event_source_unref(ppp->timeout); + ppp->timeout = timeout; + timeout = NULL; + + return 0; +} + +static int pppoe_send_initiation(sd_pppoe *ppp) { + int r; + + r = pppoe_send(ppp, PADI_CODE); + if (r < 0) + return r; + + log_debug("PPPoE: sent DISCOVER (Service-Name: %s)", + ppp->service_name ? : ""); + + pppoe_arm_timeout(ppp); + + return r; +} + +static int pppoe_send_request(sd_pppoe *ppp) { + int r; + + r = pppoe_send(ppp, PADR_CODE); + if (r < 0) + return r; + + log_debug("PPPoE: sent REQUEST"); + + ppp->padr_resend_count --; + + pppoe_arm_timeout(ppp); + + return 0; +} + +static int pppoe_send_terminate(sd_pppoe *ppp) { + int r; + + r = pppoe_send(ppp, PADT_CODE); + if (r < 0) + return r; + + log_debug("PPPoE: sent TERMINATE"); + + return 0; +} + +static int pppoe_timeout(sd_event_source *s, uint64_t usec, void *userdata) { + sd_pppoe *ppp = userdata; + int r; + + assert(ppp); + + switch (ppp->state) { + case PPPOE_STATE_INITIALIZING: + r = pppoe_send_initiation(ppp); + if (r < 0) + log_warning_errno(r, "PPPoE: sending PADI failed: %m"); + + break; + case PPPOE_STATE_REQUESTING: + if (ppp->padr_resend_count <= 0) { + log_debug("PPPoE: PADR timed out, restarting PADI"); + + r = pppoe_send_initiation(ppp); + if (r < 0) + log_warning_errno(r, "PPPoE: sending PADI failed: %m"); + + ppp->padr_resend_count = PPPOE_MAX_PADR_RESEND; + ppp->state = PPPOE_STATE_INITIALIZING; + } else { + r = pppoe_send_request(ppp); + if (r < 0) + log_warning_errno(r, "PPPoE: sending PADR failed: %m"); + } + + break; + default: + assert_not_reached("timeout in invalid state"); + } + + return 0; +} + +static int pppoe_tag_parse_binary(struct pppoe_tag *tag, uint8_t **ret, size_t *length) { + uint8_t *data; + + assert(ret); + assert(length); + + data = memdup(tag->tag_data, PPPOE_TAG_LENGTH(tag)); + if (!data) + return -ENOMEM; + + free(*ret); + *ret = data; + *length = PPPOE_TAG_LENGTH(tag); + + return 0; +} + +static int pppoe_tag_parse_string(struct pppoe_tag *tag, char **ret) { + char *string; + + assert(ret); + + string = strndup(tag->tag_data, PPPOE_TAG_LENGTH(tag)); + if (!string) + return -ENOMEM; + + free(*ret); + *ret = string; + + return 0; +} + +static int pppoe_payload_parse(PPPoETags *tags, struct pppoe_hdr *header) { + struct pppoe_tag *tag; + int r; + + assert(tags); + + pppoe_tags_clear(tags); + + PPPOE_TAGS_FOREACH(tag, header) { + switch (PPPOE_TAG_TYPE(tag)) { + case PTT_SRV_NAME: + r = pppoe_tag_parse_string(tag, &tags->service_name); + if (r < 0) + return r; + + break; + case PTT_AC_NAME: + r = pppoe_tag_parse_string(tag, &tags->ac_name); + if (r < 0) + return r; + + break; + case PTT_HOST_UNIQ: + r = pppoe_tag_parse_binary(tag, &tags->host_uniq, &tags->host_uniq_len); + if (r < 0) + return r; + + break; + case PTT_AC_COOKIE: + r = pppoe_tag_parse_binary(tag, &tags->cookie, &tags->cookie_len); + if (r < 0) + return r; + + break; + case PTT_SRV_ERR: + case PTT_SYS_ERR: + case PTT_GEN_ERR: + { + _cleanup_free_ char *error = NULL; + + /* TODO: do something more sensible with the error messages */ + r = pppoe_tag_parse_string(tag, &error); + if (r < 0) + return r; + + if (strlen(error) > 0 && utf8_is_valid(error)) + log_debug("PPPoE: error - '%s'", error); + else + log_debug("PPPoE: error"); + + break; + } + default: + log_debug("PPPoE: ignoring unknown PPPoE tag type: 0x%.2x", PPPOE_TAG_TYPE(tag)); + } + } + + return 0; +} + +static int pppoe_open_pppoe_socket(sd_pppoe *ppp) { + int s; + + assert(ppp); + assert(ppp->pppoe_fd == -1); + + s = socket(AF_PPPOX, SOCK_STREAM, 0); + if (s < 0) + return -errno; + + ppp->pppoe_fd = s; + + return 0; +} + +static int pppoe_connect_pppoe_socket(sd_pppoe *ppp) { + union sockaddr_union_pppox link = { + .pppox = { + .sa_family = AF_PPPOX, + .sa_protocol = PX_PROTO_OE, + }, + }; + int r, channel; + + assert(ppp); + assert(ppp->pppoe_fd != -1); + assert(ppp->session_id); + assert(ppp->ifname); + + link.pppox.sa_addr.pppoe.sid = ppp->session_id; + memcpy(link.pppox.sa_addr.pppoe.dev, ppp->ifname, strlen(ppp->ifname)); + memcpy(link.pppox.sa_addr.pppoe.remote, &ppp->peer_mac, ETH_ALEN); + + r = connect(ppp->pppoe_fd, &link.sa, sizeof(link.pppox)); + if (r < 0) + return r; + + r = ioctl(ppp->pppoe_fd, PPPIOCGCHAN, &channel); + if (r < 0) + return -errno; + + ppp->channel = channel; + + return 0; +} + +static int pppoe_handle_message(sd_pppoe *ppp, struct pppoe_hdr *packet, struct ether_addr *mac) { + int r; + + assert(packet); + + if (packet->ver != 0x1 || packet->type != 0x1) + return 0; + + r = pppoe_payload_parse(&ppp->tags, packet); + if (r < 0) + return 0; + + switch (ppp->state) { + case PPPOE_STATE_INITIALIZING: + if (packet->code != PADO_CODE) + return 0; + + if (ppp->tags.host_uniq_len != sizeof(ppp->host_uniq) || + memcmp(ppp->tags.host_uniq, &ppp->host_uniq, sizeof(ppp->host_uniq)) != 0) + return 0; + + log_debug("PPPoE: got OFFER (Peer: " + "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx; " + "Service-Name: '%s'; AC-Name: '%s')", + mac->ether_addr_octet[0], + mac->ether_addr_octet[1], + mac->ether_addr_octet[2], + mac->ether_addr_octet[3], + mac->ether_addr_octet[4], + mac->ether_addr_octet[5], + ppp->tags.service_name ? : "", + ppp->tags.ac_name ? : ""); + + memcpy(&ppp->peer_mac, mac, ETH_ALEN); + + r = pppoe_open_pppoe_socket(ppp); + if (r < 0) { + log_warning("PPPoE: could not open socket"); + return r; + } + + r = pppoe_send_request(ppp); + if (r < 0) + return 0; + + ppp->state = PPPOE_STATE_REQUESTING; + + break; + case PPPOE_STATE_REQUESTING: + if (packet->code != PADS_CODE) + return 0; + + if (ppp->tags.host_uniq_len != sizeof(ppp->host_uniq) || + memcmp(ppp->tags.host_uniq, &ppp->host_uniq, + sizeof(ppp->host_uniq)) != 0) + return 0; + + if (memcmp(&ppp->peer_mac, mac, ETH_ALEN) != 0) + return 0; + + ppp->session_id = packet->sid; + + log_debug("PPPoE: got CONFIRMATION (Session ID: %"PRIu16")", + be16toh(ppp->session_id)); + + r = pppoe_connect_pppoe_socket(ppp); + if (r < 0) { + log_warning("PPPoE: could not connect socket"); + return r; + } + + ppp->state = PPPOE_STATE_RUNNING; + + ppp->timeout = sd_event_source_unref(ppp->timeout); + assert(ppp->cb); + ppp->cb(ppp, PPPOE_EVENT_RUNNING, ppp->userdata); + + break; + case PPPOE_STATE_RUNNING: + if (packet->code != PADT_CODE) + return 0; + + if (memcmp(&ppp->peer_mac, mac, ETH_ALEN) != 0) + return 0; + + if (ppp->session_id != packet->sid) + return 0; + + log_debug("PPPoE: got TERMINATE"); + + ppp->state = PPPOE_STATE_STOPPED; + + assert(ppp->cb); + ppp->cb(ppp, PPPOE_EVENT_STOPPED, ppp->userdata); + + break; + case PPPOE_STATE_STOPPED: + break; + default: + assert_not_reached("PPPoE: invalid state when receiving message"); + } + + return 0; +} + +static int pppoe_receive_message(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + sd_pppoe *ppp = userdata; + _cleanup_free_ struct pppoe_hdr *packet = NULL; + union sockaddr_union link = {}; + socklen_t addrlen = sizeof(link); + int buflen = 0, len, r; + + assert(ppp); + assert(fd != -1); + + r = ioctl(fd, FIONREAD, &buflen); + if (r < 0) + return r; + + if (buflen < 0) + /* this can't be right */ + return -EIO; + + packet = malloc0(buflen); + if (!packet) + return -ENOMEM; + + len = recvfrom(fd, packet, buflen, 0, &link.sa, &addrlen); + if (len < 0) { + log_warning_errno(r, "PPPoE: could not receive message from raw socket: %m"); + return 0; + } else if ((size_t)len < sizeof(struct pppoe_hdr)) + return 0; + else if ((size_t)len != sizeof(struct pppoe_hdr) + PPPOE_PACKET_LENGTH(packet)) + return 0; + + if (link.ll.sll_halen != ETH_ALEN) + /* not ethernet? */ + return 0; + + r = pppoe_handle_message(ppp, packet, (struct ether_addr*)&link.ll.sll_addr); + if (r < 0) + return r; + + return 1; +} + +int sd_pppoe_start(sd_pppoe *ppp) { + union sockaddr_union link = { + .ll = { + .sll_family = AF_PACKET, + .sll_protocol = htons(ETH_P_PPP_DISC), + }, + }; + _cleanup_close_ int s = -1; + _cleanup_event_source_unref_ sd_event_source *io = NULL; + int r; + + assert_return(ppp, -EINVAL); + assert_return(ppp->fd == -1, -EBUSY); + assert_return(!ppp->io, -EBUSY); + assert_return(ppp->ifindex > 0, -EUNATCH); + assert_return(ppp->ifname, -EUNATCH); + assert_return(ppp->event, -EUNATCH); + assert_return(ppp->cb, -EUNATCH); + + s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); + if (s < 0) + return -errno; + + link.ll.sll_ifindex = ppp->ifindex; + + r = bind(s, &link.sa, sizeof(link.ll)); + if (r < 0) + return r; + + r = sd_event_add_io(ppp->event, &io, + s, EPOLLIN, pppoe_receive_message, + ppp); + if (r < 0) + return r; + + r = sd_event_source_set_priority(io, ppp->event_priority); + if (r < 0) + return r; + + ppp->fd = s; + s = -1; + ppp->io = io; + io = NULL; + + r = pppoe_send_initiation(ppp); + if (r < 0) + return r; + + ppp->state = PPPOE_STATE_INITIALIZING; + + return 0; +} + +int sd_pppoe_stop(sd_pppoe *ppp) { + assert_return(ppp, -EINVAL); + + if (ppp->state == PPPOE_STATE_RUNNING) + pppoe_send_terminate(ppp); + + ppp->io = sd_event_source_unref(ppp->io); + ppp->timeout = sd_event_source_unref(ppp->timeout); + ppp->fd = asynchronous_close(ppp->fd); + ppp->pppoe_fd = asynchronous_close(ppp->pppoe_fd); + + return 0; +} diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index 26b28a20e8..75908391f0 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -41,7 +41,7 @@ static struct ether_addr mac_addr = { .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'} }; -static bool verbose = false; +static bool verbose = true; static sd_event_source *hangcheck; static int test_dhcp_fd[2]; @@ -335,13 +335,19 @@ int detect_virtualization(const char **id) { return 1; } -int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { - assert_se(index == test_index); +static void test_client_solicit_cb(sd_dhcp6_client *client, int event, + void *userdata) { + sd_event *e = userdata; - if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_dhcp_fd) < 0) - return -errno; + assert_se(e); + assert_se(event == DHCP6_EVENT_IP_ACQUIRE); - return test_dhcp_fd[0]; + assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DNS_SERVERS) == -EBUSY); + + if (verbose) + printf(" got DHCPv6 event %d\n", event); + + sd_event_exit(e, 0); } static int test_client_send_reply(DHCP6Message *request) { @@ -513,6 +519,83 @@ static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option, return 0; } +static void test_client_information_cb(sd_dhcp6_client *client, int event, + void *userdata) { + sd_event *e = userdata; + + assert_se(e); + assert_se(event == DHCP6_EVENT_INFORMATION_REQUEST); + + if (verbose) + printf(" got DHCPv6 event %d\n", event); + + assert_se(sd_dhcp6_client_set_information_request(client, false) >= 0); + assert_se(sd_dhcp6_client_set_callback(client, + test_client_solicit_cb, e) >= 0); + + assert_se(sd_dhcp6_client_start(client) >= 0); +} + +static int test_client_verify_information_request(DHCP6Message *information_request, + uint8_t *option, size_t len) { + + _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL; + uint8_t *optval; + uint16_t optcode; + size_t optlen; + bool found_clientid = false, found_elapsed_time = false; + int r; + struct in6_addr addr; + uint32_t lt_pref, lt_valid; + + assert_se(information_request->type == DHCP6_INFORMATION_REQUEST); + + assert_se(dhcp6_lease_new(&lease) >= 0); + + while ((r = dhcp6_option_parse(&option, &len, + &optcode, &optlen, &optval)) >= 0) { + switch(optcode) { + case DHCP6_OPTION_CLIENTID: + assert_se(!found_clientid); + found_clientid = true; + + assert_se(optlen == sizeof(test_duid)); + memcpy(&test_duid, optval, sizeof(test_duid)); + + break; + + case DHCP6_OPTION_IA_NA: + assert_not_reached("IA TA option must not be present"); + + break; + + case DHCP6_OPTION_SERVERID: + assert_not_reached("Server ID option must not be present"); + + break; + + case DHCP6_OPTION_ELAPSED_TIME: + assert_se(!found_elapsed_time); + found_elapsed_time = true; + + assert_se(optlen == 2); + + break; + } + } + + assert_se(r == -ENOMSG); + assert_se(found_clientid && found_elapsed_time); + + assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, <_pref, + <_valid) == -ENOMSG); + + assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, <_pref, + <_valid) == -ENOMSG); + + return 0; +} + int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, const void *packet, size_t len) { struct in6_addr mcast = @@ -534,10 +617,14 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, assert_se(message->transaction_id & 0x00ffffff); if (test_client_message_num == 0) { + test_client_verify_information_request(message, option, len); + test_client_send_reply(message); + test_client_message_num++; + } else if (test_client_message_num == 1) { test_client_verify_solicit(message, option, len); test_client_send_advertise(message); test_client_message_num++; - } else if (test_client_message_num == 1) { + } else if (test_client_message_num == 2) { test_client_verify_request(message, option, len); test_client_send_reply(message); test_client_message_num++; @@ -546,24 +633,19 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, return len; } -static void test_client_solicit_cb(sd_dhcp6_client *client, int event, - void *userdata) { - sd_event *e = userdata; - - assert_se(e); - assert_se(event == DHCP6_EVENT_IP_ACQUIRE); - - assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DNS_SERVERS) == -EBUSY); +int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { + assert_se(index == test_index); - if (verbose) - printf(" got DHCPv6 event %d\n", event); + if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_dhcp_fd) < 0) + return -errno; - sd_event_exit(e, 0); + return test_dhcp_fd[0]; } static int test_client_solicit(sd_event *e) { sd_dhcp6_client *client; usec_t time_now = now(clock_boottime_or_monotonic()); + bool val = true; if (verbose) printf("* %s\n", __FUNCTION__); @@ -578,8 +660,14 @@ static int test_client_solicit(sd_event *e) { sizeof (mac_addr), ARPHRD_ETHER) >= 0); + assert_se(sd_dhcp6_client_get_information_request(client, &val) >= 0); + assert_se(val == false); + assert_se(sd_dhcp6_client_set_information_request(client, true) >= 0); + assert_se(sd_dhcp6_client_get_information_request(client, &val) >= 0); + assert_se(val == true); + assert_se(sd_dhcp6_client_set_callback(client, - test_client_solicit_cb, e) >= 0); + test_client_information_cb, e) >= 0); assert_se(sd_event_add_time(e, &hangcheck, clock_boottime_or_monotonic(), time_now + 2 * USEC_PER_SEC, 0, diff --git a/src/libsystemd-network/test-icmp6-rs.c b/src/libsystemd-network/test-icmp6-rs.c index afa8562659..be64d334fa 100644 --- a/src/libsystemd-network/test-icmp6-rs.c +++ b/src/libsystemd-network/test-icmp6-rs.c @@ -36,13 +36,13 @@ static int test_fd[2]; static int test_rs_hangcheck(sd_event_source *s, uint64_t usec, void *userdata) { - assert(false); + assert_se(false); return 0; } int dhcp_network_icmp6_bind_router_solicitation(int index) { - assert(index == 42); + assert_se(index == 42); if (socketpair(AF_UNIX, SOCK_DGRAM, 0, test_fd) < 0) return -errno; @@ -69,7 +69,7 @@ static int send_ra(uint8_t flags) { advertisement[5] = flags; - assert(write(test_fd[1], advertisement, sizeof(advertisement)) == + assert_se(write(test_fd[1], advertisement, sizeof(advertisement)) == sizeof(advertisement)); if (verbose) @@ -93,9 +93,9 @@ static void test_rs_done(sd_icmp6_nd *nd, int event, void *userdata) { { ND_RA_FLAG_OTHER, ICMP6_EVENT_ROUTER_ADVERTISMENT_OTHER }, { ND_RA_FLAG_MANAGED, ICMP6_EVENT_ROUTER_ADVERTISMENT_MANAGED } }; - assert(nd); + assert_se(nd); - assert(event == flag_event[idx].event); + assert_se(event == flag_event[idx].event); idx++; if (verbose) @@ -114,31 +114,31 @@ static void test_rs(sd_event *e) { if (verbose) printf("* %s\n", __FUNCTION__); - assert(sd_icmp6_nd_new(&nd) >= 0); - assert(nd); + assert_se(sd_icmp6_nd_new(&nd) >= 0); + assert_se(nd); - assert(sd_icmp6_nd_attach_event(nd, e, 0) >= 0); + assert_se(sd_icmp6_nd_attach_event(nd, e, 0) >= 0); - assert(sd_icmp6_nd_set_index(nd, 42) >= 0); - assert(sd_icmp6_nd_set_mac(nd, &mac_addr) >= 0); - assert(sd_icmp6_nd_set_callback(nd, test_rs_done, e) >= 0); + assert_se(sd_icmp6_nd_set_index(nd, 42) >= 0); + assert_se(sd_icmp6_nd_set_mac(nd, &mac_addr) >= 0); + assert_se(sd_icmp6_nd_set_callback(nd, test_rs_done, e) >= 0); - assert(sd_event_add_time(e, &test_hangcheck, clock_boottime_or_monotonic(), + assert_se(sd_event_add_time(e, &test_hangcheck, clock_boottime_or_monotonic(), time_now + 2 *USEC_PER_SEC, 0, test_rs_hangcheck, NULL) >= 0); - assert(sd_icmp6_nd_stop(nd) >= 0); - assert(sd_icmp6_router_solicitation_start(nd) >= 0); - assert(sd_icmp6_nd_stop(nd) >= 0); + assert_se(sd_icmp6_nd_stop(nd) >= 0); + assert_se(sd_icmp6_router_solicitation_start(nd) >= 0); + assert_se(sd_icmp6_nd_stop(nd) >= 0); - assert(sd_icmp6_router_solicitation_start(nd) >= 0); + assert_se(sd_icmp6_router_solicitation_start(nd) >= 0); sd_event_loop(e); test_hangcheck = sd_event_source_unref(test_hangcheck); nd = sd_icmp6_nd_unref(nd); - assert(!nd); + assert_se(!nd); close(test_fd[1]); } @@ -146,7 +146,7 @@ static void test_rs(sd_event *e) { int main(int argc, char *argv[]) { sd_event *e; - assert(sd_event_new(&e) >= 0); + assert_se(sd_event_new(&e) >= 0); log_set_max_level(LOG_DEBUG); log_parse_environment(); diff --git a/src/libsystemd-network/test-pppoe.c b/src/libsystemd-network/test-pppoe.c new file mode 100644 index 0000000000..0d419aa172 --- /dev/null +++ b/src/libsystemd-network/test-pppoe.c @@ -0,0 +1,181 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright (C) 2014 Tom Gundersen <teg@jklm.no> + + 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 <stdlib.h> +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <unistd.h> + +#include <linux/veth.h> +#include <net/if.h> + +#include "util.h" +#include "socket-util.h" +#include "sd-event.h" +#include "event-util.h" +#include "sd-rtnl.h" +#include "rtnl-util.h" +#include "sd-pppoe.h" + +static void pppoe_handler(sd_pppoe *ppp, int event, void *userdata) { + static int pppoe_state = -1; + sd_event *e = userdata; + + assert_se(ppp); + assert_se(e); + + switch (event) { + case PPPOE_EVENT_RUNNING: + assert_se(pppoe_state == -1); + log_info("running"); + break; + case PPPOE_EVENT_STOPPED: + assert_se(pppoe_state == PPPOE_EVENT_RUNNING); + log_info("stopped"); + assert_se(sd_event_exit(e, 0) >= 0); + break; + default: + assert_not_reached("invalid pppoe event"); + } + + pppoe_state = event; +} + +static int client_run(const char *client_name, sd_event *e) { + sd_pppoe *pppoe; + int client_ifindex; + + client_ifindex = (int) if_nametoindex(client_name); + assert_se(client_ifindex > 0); + + assert_se(sd_pppoe_new(&pppoe) >= 0); + assert_se(sd_pppoe_attach_event(pppoe, e, 0) >= 0); + + assert_se(sd_pppoe_set_ifname(pppoe, "pppoe-client") >= 0); + assert_se(sd_pppoe_set_ifindex(pppoe, client_ifindex) >= 0); + assert_se(sd_pppoe_set_callback(pppoe, pppoe_handler, e) >= 0); + + log_info("starting PPPoE client, it will exit when the server times out and sends PADT"); + + assert_se(sd_pppoe_start(pppoe) >= 0); + + assert_se(sd_event_loop(e) >= 0); + + assert_se(!sd_pppoe_unref(pppoe)); + + return EXIT_SUCCESS; +} + +static int test_pppoe_server(sd_event *e) { + sd_rtnl *rtnl; + sd_rtnl_message *m; + pid_t pid; + int r, client_ifindex, server_ifindex; + + r = unshare(CLONE_NEWNET); + if (r < 0 && errno == EPERM) + return EXIT_TEST_SKIP; + + assert_se(r >= 0); + + assert_se(sd_rtnl_open(&rtnl, 0) >= 0); + assert_se(sd_rtnl_attach_event(rtnl, e, 0) >= 0); + + assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0) >= 0); + assert_se(sd_rtnl_message_append_string(m, IFLA_IFNAME, "pppoe-server") >= 0); + assert_se(sd_rtnl_message_open_container(m, IFLA_LINKINFO) >= 0); + assert_se(sd_rtnl_message_open_container_union(m, IFLA_INFO_DATA, "veth") >= 0); + assert_se(sd_rtnl_message_open_container(m, VETH_INFO_PEER) >= 0); + assert_se(sd_rtnl_message_append_string(m, IFLA_IFNAME, "pppoe-client") >= 0); + assert_se(sd_rtnl_message_close_container(m) >= 0); + assert_se(sd_rtnl_message_close_container(m) >= 0); + assert_se(sd_rtnl_message_close_container(m) >= 0); + assert_se(sd_rtnl_call(rtnl, m, 0, NULL) >= 0); + + client_ifindex = (int) if_nametoindex("pppoe-client"); + assert_se(client_ifindex > 0); + server_ifindex = (int) if_nametoindex("pppoe-server"); + assert_se(server_ifindex > 0); + + m = sd_rtnl_message_unref(m); + assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, client_ifindex) >= 0); + assert_se(sd_rtnl_message_link_set_flags(m, IFF_UP, IFF_UP) >= 0); + assert_se(sd_rtnl_call(rtnl, m, 0, NULL) >= 0); + + m = sd_rtnl_message_unref(m); + assert_se(sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, server_ifindex) >= 0); + assert_se(sd_rtnl_message_link_set_flags(m, IFF_UP, IFF_UP) >= 0); + assert_se(sd_rtnl_call(rtnl, m, 0, NULL) >= 0); + + pid = fork(); + assert_se(pid >= 0); + if (pid == 0) { + /* let the client send some discover messages before the server is started */ + sleep(2); + + /* TODO: manage pppoe-server-options */ + execlp("pppoe-server", "pppoe-server", "-F", + "-I", "pppoe-server", + "-C", "Test-AC", + "-S", "Service-Default", + "-S", "Service-First-Auxillary", + "-S", "Service-Second-Auxillary", + NULL); + assert_not_reached("failed to execute pppoe-server. not installed?"); + } + + client_run("pppoe-client", e); + + assert_se(kill(pid, SIGTERM) >= 0); + assert_se(wait_for_terminate(pid, NULL) >= 0); + + assert_se(!sd_rtnl_message_unref(m)); + assert_se(!sd_rtnl_unref(rtnl)); + + return EXIT_SUCCESS; +} + +int main(int argc, char *argv[]) { + _cleanup_event_unref_ sd_event *e = NULL; + + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + + assert_se(sd_event_new(&e) >= 0); + + if (argc == 1) { + log_info("running PPPoE client against local server"); + + return test_pppoe_server(e); + } else if (argc == 2) { + log_info("running PPPoE client over '%s'", argv[1]); + + return client_run(argv[1], e); + } else { + log_error("This program takes one or no arguments.\n" + "\t %s [<ifname>]", program_invocation_short_name); + return EXIT_FAILURE; + } +} diff --git a/src/libsystemd-terminal/evcat.c b/src/libsystemd-terminal/evcat.c index 9e8a262b79..f4c25b5556 100644 --- a/src/libsystemd-terminal/evcat.c +++ b/src/libsystemd-terminal/evcat.c @@ -137,16 +137,12 @@ static int evcat_new(Evcat **out) { return log_oom(); r = sd_pid_get_session(getpid(), &e->session); - if (r < 0) { - log_error("Cannot retrieve logind session: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Cannot retrieve logind session: %m"); r = sd_session_get_seat(e->session, &e->seat); - if (r < 0) { - log_error("Cannot retrieve seat of logind session: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Cannot retrieve seat of logind session: %m"); e->managed = is_managed(e->session); @@ -318,17 +314,13 @@ static int evcat_sysview_fn(sysview_context *c, void *userdata, sysview_event *e name, evcat_idev_fn, e); - if (r < 0) { - log_error("Cannot create idev session: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Cannot create idev session: %m"); if (e->managed) { r = sysview_session_take_control(ev->session_add.session); - if (r < 0) { - log_error("Cannot request session control: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Cannot request session control: %m"); } idev_session_enable(e->idev_session); @@ -345,10 +337,8 @@ static int evcat_sysview_fn(sysview_context *c, void *userdata, sysview_event *e type = sysview_device_get_type(d); if (type == SYSVIEW_DEVICE_EVDEV) { r = idev_session_add_evdev(e->idev_session, sysview_device_get_ud(d)); - if (r < 0) { - log_error("Cannot add evdev device to idev: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Cannot add evdev device to idev: %m"); } break; @@ -357,31 +347,23 @@ static int evcat_sysview_fn(sysview_context *c, void *userdata, sysview_event *e type = sysview_device_get_type(d); if (type == SYSVIEW_DEVICE_EVDEV) { r = idev_session_remove_evdev(e->idev_session, sysview_device_get_ud(d)); - if (r < 0) { - log_error("Cannot remove evdev device from idev: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Cannot remove evdev device from idev: %m"); } break; case SYSVIEW_EVENT_SESSION_CONTROL: r = ev->session_control.error; - if (r < 0) { - log_error("Cannot acquire session control: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Cannot acquire session control: %m"); r = ioctl(1, KDSKBMODE, K_UNICODE); - if (r < 0) { - log_error("Cannot set K_UNICODE on stdout: %m"); - return -errno; - } + if (r < 0) + return log_error_errno(errno, "Cannot set K_UNICODE on stdout: %m"); r = ioctl(1, KDSETMODE, KD_TEXT); - if (r < 0) { - log_error("Cannot set KD_TEXT on stdout: %m"); - return -errno; - } + if (r < 0) + return log_error_errno(errno, "Cannot set KD_TEXT on stdout: %m"); printf("\n"); diff --git a/src/libsystemd-terminal/grdev-drm.c b/src/libsystemd-terminal/grdev-drm.c index dba6db2691..2df63537f0 100644 --- a/src/libsystemd-terminal/grdev-drm.c +++ b/src/libsystemd-terminal/grdev-drm.c @@ -468,9 +468,11 @@ static int grdrm_plane_resync(grdrm_plane *plane) { if (r == -ENOENT) { card->async_hotplug = true; r = 0; - log_debug("grdrm: %s: plane %u removed during resync", card->base.name, plane->object.id); + log_debug("grdrm: %s: plane %u removed during resync", + card->base.name, plane->object.id); } else { - log_debug("grdrm: %s: cannot retrieve plane %u: %m", card->base.name, plane->object.id); + log_debug_errno(errno, "grdrm: %s: cannot retrieve plane %u: %m", + card->base.name, plane->object.id); } return r; @@ -625,9 +627,11 @@ static int grdrm_connector_resync(grdrm_connector *connector) { if (r == -ENOENT) { card->async_hotplug = true; r = 0; - log_debug("grdrm: %s: connector %u removed during resync", card->base.name, connector->object.id); + log_debug("grdrm: %s: connector %u removed during resync", + card->base.name, connector->object.id); } else { - log_debug("grdrm: %s: cannot retrieve connector %u: %m", card->base.name, connector->object.id); + log_debug_errno(errno, "grdrm: %s: cannot retrieve connector %u: %m", + card->base.name, connector->object.id); } return r; @@ -783,9 +787,11 @@ static int grdrm_encoder_resync(grdrm_encoder *encoder) { if (r == -ENOENT) { card->async_hotplug = true; r = 0; - log_debug("grdrm: %s: encoder %u removed during resync", card->base.name, encoder->object.id); + log_debug("grdrm: %s: encoder %u removed during resync", + card->base.name, encoder->object.id); } else { - log_debug("grdrm: %s: cannot retrieve encoder %u: %m", card->base.name, encoder->object.id); + log_debug_errno(errno, "grdrm: %s: cannot retrieve encoder %u: %m", + card->base.name, encoder->object.id); } return r; @@ -916,9 +922,11 @@ static int grdrm_crtc_resync(grdrm_crtc *crtc) { if (r == -ENOENT) { card->async_hotplug = true; r = 0; - log_debug("grdrm: %s: crtc %u removed during resync", card->base.name, crtc->object.id); + log_debug("grdrm: %s: crtc %u removed during resync", + card->base.name, crtc->object.id); } else { - log_debug("grdrm: %s: cannot retrieve crtc %u: %m", card->base.name, crtc->object.id); + log_debug_errno(errno, "grdrm: %s: cannot retrieve crtc %u: %m", + card->base.name, crtc->object.id); } return r; @@ -1119,8 +1127,8 @@ static void grdrm_crtc_commit_deep(grdrm_crtc *crtc, grdev_fb *basefb) { r = ioctl(card->fd, DRM_IOCTL_MODE_SETCRTC, &set_crtc); if (r < 0) { r = -errno; - log_debug("grdrm: %s: cannot set crtc %" PRIu32 ": %m", - card->base.name, crtc->object.id); + log_debug_errno(errno, "grdrm: %s: cannot set crtc %" PRIu32 ": %m", + card->base.name, crtc->object.id); grdrm_card_async(card, r); return; @@ -1188,8 +1196,8 @@ static int grdrm_crtc_commit_flip(grdrm_crtc *crtc, grdev_fb *basefb) { * possible to see whether cards support page-flipping, so * avoid logging on each frame. */ if (r != -EINVAL) - log_debug("grdrm: %s: cannot schedule page-flip on crtc %" PRIu32 ": %m", - card->base.name, crtc->object.id); + log_debug_errno(errno, "grdrm: %s: cannot schedule page-flip on crtc %" PRIu32 ": %m", + card->base.name, crtc->object.id); if (grdrm_card_async(card, r)) return r; @@ -1241,8 +1249,8 @@ static void grdrm_crtc_commit(grdrm_crtc *crtc) { r = ioctl(card->fd, DRM_IOCTL_MODE_SETCRTC, &set_crtc); if (r < 0) { r = -errno; - log_debug("grdrm: %s: cannot shutdown crtc %" PRIu32 ": %m", - card->base.name, crtc->object.id); + log_debug_errno(errno, "grdrm: %s: cannot shutdown crtc %" PRIu32 ": %m", + card->base.name, crtc->object.id); grdrm_card_async(card, r); return; @@ -1298,8 +1306,8 @@ static void grdrm_crtc_restore(grdrm_crtc *crtc) { r = ioctl(card->fd, DRM_IOCTL_MODE_SETCRTC, &set_crtc); if (r < 0) { r = -errno; - log_debug("grdrm: %s: cannot restore crtc %" PRIu32 ": %m", - card->base.name, crtc->object.id); + log_debug_errno(errno, "grdrm: %s: cannot restore crtc %" PRIu32 ": %m", + card->base.name, crtc->object.id); grdrm_card_async(card, r); return; @@ -1393,9 +1401,9 @@ static int grdrm_fb_new(grdrm_fb **out, grdrm_card *card, const struct drm_mode_ r = ioctl(card->fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb); if (r < 0) { - r = -errno; - log_debug("grdrm: %s: cannot create dumb buffer %" PRIu32 "x%" PRIu32": %m", - card->base.name, fb->base.width, fb->base.height); + r = negative_errno(); + log_debug_errno(errno, "grdrm: %s: cannot create dumb buffer %" PRIu32 "x%" PRIu32": %m", + card->base.name, fb->base.width, fb->base.height); return r; } @@ -1407,17 +1415,17 @@ static int grdrm_fb_new(grdrm_fb **out, grdrm_card *card, const struct drm_mode_ r = ioctl(card->fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb); if (r < 0) { - r = -errno; - log_debug("grdrm: %s: cannot map dumb buffer %" PRIu32 "x%" PRIu32": %m", - card->base.name, fb->base.width, fb->base.height); + r = negative_errno(); + log_debug_errno(errno, "grdrm: %s: cannot map dumb buffer %" PRIu32 "x%" PRIu32": %m", + card->base.name, fb->base.width, fb->base.height); return r; } fb->base.maps[0] = mmap(0, fb->sizes[0], PROT_WRITE, MAP_SHARED, card->fd, map_dumb.offset); if (fb->base.maps[0] == MAP_FAILED) { - r = -errno; - log_debug("grdrm: %s: cannot memory-map dumb buffer %" PRIu32 "x%" PRIu32": %m", - card->base.name, fb->base.width, fb->base.height); + r = negative_errno(); + log_debug_errno(errno, "grdrm: %s: cannot memory-map dumb buffer %" PRIu32 "x%" PRIu32": %m", + card->base.name, fb->base.width, fb->base.height); return r; } @@ -1433,9 +1441,9 @@ static int grdrm_fb_new(grdrm_fb **out, grdrm_card *card, const struct drm_mode_ r = ioctl(card->fd, DRM_IOCTL_MODE_ADDFB2, &add_fb); if (r < 0) { - r = -errno; - log_debug("grdrm: %s: cannot add framebuffer %" PRIu32 "x%" PRIu32": %m", - card->base.name, fb->base.width, fb->base.height); + r = negative_errno(); + log_debug_errno(errno, "grdrm: %s: cannot add framebuffer %" PRIu32 "x%" PRIu32": %m", + card->base.name, fb->base.width, fb->base.height); return r; } @@ -1461,8 +1469,8 @@ grdrm_fb *grdrm_fb_free(grdrm_fb *fb) { if (fb->id > 0 && fb->card->fd >= 0) { r = ioctl(fb->card->fd, DRM_IOCTL_MODE_RMFB, fb->id); if (r < 0) - log_debug("grdrm: %s: cannot delete framebuffer %" PRIu32 ": %m", - fb->card->base.name, fb->id); + log_debug_errno(errno, "grdrm: %s: cannot delete framebuffer %" PRIu32 ": %m", + fb->card->base.name, fb->id); } for (i = 0; i < ELEMENTSOF(fb->handles); ++i) { @@ -1475,8 +1483,8 @@ grdrm_fb *grdrm_fb_free(grdrm_fb *fb) { destroy_dumb.handle = fb->handles[i]; r = ioctl(fb->card->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_dumb); if (r < 0) - log_debug("grdrm: %s: cannot destroy dumb-buffer %" PRIu32 ": %m", - fb->card->base.name, fb->handles[i]); + log_debug_errno(errno, "grdrm: %s: cannot destroy dumb-buffer %" PRIu32 ": %m", + fb->card->base.name, fb->handles[i]); } } @@ -1777,7 +1785,8 @@ static int grdrm_card_resync(grdrm_card *card) { r = ioctl(card->fd, DRM_IOCTL_MODE_GETRESOURCES, &res); if (r < 0) { r = -errno; - log_debug("grdrm: %s: cannot retrieve drm resources: %m", card->base.name); + log_debug_errno(errno, "grdrm: %s: cannot retrieve drm resources: %m", + card->base.name); return r; } @@ -1788,7 +1797,8 @@ static int grdrm_card_resync(grdrm_card *card) { r = ioctl(card->fd, DRM_IOCTL_MODE_GETPLANERESOURCES, &pres); if (r < 0) { r = -errno; - log_debug("grdrm: %s: cannot retrieve drm plane-resources: %m", card->base.name); + log_debug_errno(errno, "grdrm: %s: cannot retrieve drm plane-resources: %m", + card->base.name); return r; } @@ -1799,7 +1809,8 @@ static int grdrm_card_resync(grdrm_card *card) { n = ALIGN_POWER2(max); if (!n || n > UINT16_MAX) { - log_debug("grdrm: %s: excessive DRM resource limit: %" PRIu32, card->base.name, max); + log_debug("grdrm: %s: excessive DRM resource limit: %" PRIu32, + card->base.name, max); return -ERANGE; } @@ -2186,8 +2197,8 @@ static void grdrm_card_hotplug(grdrm_card *card) { card->ready = false; r = grdrm_card_resync(card); if (r < 0) { - log_debug("grdrm: %s/%s: cannot re-sync card: %s", - card->base.session->name, card->base.name, strerror(-r)); + log_debug_errno(r, "grdrm: %s/%s: cannot re-sync card: %m", + card->base.session->name, card->base.name); return; } @@ -2228,7 +2239,8 @@ static int grdrm_card_io_fn(sd_event_source *s, int fd, uint32_t revents, void * if (errno == EAGAIN || errno == EINTR) return 0; - log_debug("grdrm: %s/%s: read error: %m", card->base.session->name, card->base.name); + log_debug_errno(errno, "grdrm: %s/%s: read error: %m", + card->base.session->name, card->base.name); grdrm_card_close(card); return 0; } @@ -2237,7 +2249,8 @@ static int grdrm_card_io_fn(sd_event_source *s, int fd, uint32_t revents, void * event = (void*)buf; if (len < sizeof(*event) || len < event->length) { - log_debug("grdrm: %s/%s: truncated event", card->base.session->name, card->base.name); + log_debug("grdrm: %s/%s: truncated event", + card->base.session->name, card->base.name); break; } @@ -2245,7 +2258,8 @@ static int grdrm_card_io_fn(sd_event_source *s, int fd, uint32_t revents, void * case DRM_EVENT_FLIP_COMPLETE: vblank = (void*)event; if (event->length < sizeof(*vblank)) { - log_debug("grdrm: %s/%s: truncated vblank event", card->base.session->name, card->base.name); + log_debug("grdrm: %s/%s: truncated vblank event", + card->base.session->name, card->base.name); break; } @@ -2415,8 +2429,8 @@ static int grdrm_card_open(grdrm_card *card, int dev_fd) { r = ioctl(card->fd, DRM_IOCTL_GET_CAP, &cap); card->cap_dumb = r >= 0 && cap.value; if (r < 0) - log_debug("grdrm: %s/%s: cannot retrieve DUMB_BUFFER capability: %s", - card->base.session->name, card->base.name, strerror(-r)); + log_debug_errno(r, "grdrm: %s/%s: cannot retrieve DUMB_BUFFER capability: %m", + card->base.session->name, card->base.name); else if (!card->cap_dumb) log_debug("grdrm: %s/%s: DUMB_BUFFER capability not supported", card->base.session->name, card->base.name); @@ -2427,8 +2441,8 @@ static int grdrm_card_open(grdrm_card *card, int dev_fd) { r = ioctl(card->fd, DRM_IOCTL_GET_CAP, &cap); card->cap_monotonic = r >= 0 && cap.value; if (r < 0) - log_debug("grdrm: %s/%s: cannot retrieve TIMESTAMP_MONOTONIC capability: %s", - card->base.session->name, card->base.name, strerror(-r)); + log_debug_errno(r, "grdrm: %s/%s: cannot retrieve TIMESTAMP_MONOTONIC capability: %m", + card->base.session->name, card->base.name); else if (!card->cap_monotonic) log_debug("grdrm: %s/%s: TIMESTAMP_MONOTONIC is disabled globally, fix this NOW!", card->base.session->name, card->base.name); @@ -2498,8 +2512,8 @@ static void unmanaged_card_enable(grdev_card *basecard) { fd = open(cu->devnode, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK); if (fd < 0) { /* not fatal; simply ignore the device */ - log_debug("grdrm: %s/%s: cannot open node %s: %m", - basecard->session->name, basecard->name, cu->devnode); + log_debug_errno(errno, "grdrm: %s/%s: cannot open node %s: %m", + basecard->session->name, basecard->name, cu->devnode); return; } @@ -2507,16 +2521,16 @@ static void unmanaged_card_enable(grdev_card *basecard) { r = grdrm_card_open(&cu->card, fd); if (r < 0) { - log_debug("grdrm: %s/%s: cannot open: %s", - basecard->session->name, basecard->name, strerror(-r)); + log_debug_errno(r, "grdrm: %s/%s: cannot open: %m", + basecard->session->name, basecard->name); return; } } r = ioctl(cu->card.fd, DRM_IOCTL_SET_MASTER, 0); if (r < 0) { - log_debug("grdrm: %s/%s: cannot acquire DRM-Master: %m", - basecard->session->name, basecard->name); + log_debug_errno(errno, "grdrm: %s/%s: cannot acquire DRM-Master: %m", + basecard->session->name, basecard->name); return; } @@ -2566,8 +2580,8 @@ static int unmanaged_card_new(grdev_card **out, grdev_session *session, struct u fd = open(cu->devnode, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK); if (fd < 0) { /* not fatal; allow uaccess based control on activation */ - log_debug("grdrm: %s/%s: cannot open node %s: %m", - basecard->session->name, basecard->name, cu->devnode); + log_debug_errno(errno, "grdrm: %s/%s: cannot open node %s: %m", + basecard->session->name, basecard->name, cu->devnode); } else { /* We might get DRM-Master implicitly on open(); drop it immediately * so we acquire it only once we're actually enabled. We don't @@ -2575,13 +2589,13 @@ static int unmanaged_card_new(grdev_card **out, grdev_session *session, struct u * weird errors, anyway. */ r = ioctl(fd, DRM_IOCTL_DROP_MASTER, 0); if (r < 0 && errno != EACCES && errno != EINVAL) - log_debug("grdrm: %s/%s: cannot drop DRM-Master: %m", - basecard->session->name, basecard->name); + log_debug_errno(errno, "grdrm: %s/%s: cannot drop DRM-Master: %m", + basecard->session->name, basecard->name); r = grdrm_card_open(&cu->card, fd); if (r < 0) - log_debug("grdrm: %s/%s: cannot open: %s", - basecard->session->name, basecard->name, strerror(-r)); + log_debug_errno(r, "grdrm: %s/%s: cannot open: %m", + basecard->session->name, basecard->name); } if (out) @@ -2724,8 +2738,8 @@ static int managed_card_pause_device_fn(sd_bus *bus, } if (r < 0) - log_debug("grdrm: %s/%s: cannot send PauseDeviceComplete: %s", - session->name, cm->card.base.name, strerror(-r)); + log_debug_errno(r, "grdrm: %s/%s: cannot send PauseDeviceComplete: %m", + session->name, cm->card.base.name); } return 0; @@ -2771,15 +2785,15 @@ static int managed_card_resume_device_fn(sd_bus *bus, * and our code works fine this way. */ fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); if (fd < 0) { - log_debug("grdrm: %s/%s: cannot duplicate fd: %m", - session->name, cm->card.base.name); + log_debug_errno(errno, "grdrm: %s/%s: cannot duplicate fd: %m", + session->name, cm->card.base.name); return 0; } r = grdrm_card_open(&cm->card, fd); if (r < 0) { - log_debug("grdrm: %s/%s: cannot open: %s", - session->name, cm->card.base.name, strerror(-r)); + log_debug_errno(r, "grdrm: %s/%s: cannot open: %m", + session->name, cm->card.base.name); return 0; } } @@ -2863,15 +2877,15 @@ static int managed_card_take_device_fn(sd_bus *bus, fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); if (fd < 0) { - log_debug("grdrm: %s/%s: cannot duplicate fd: %m", - session->name, cm->card.base.name); + log_debug_errno(errno, "grdrm: %s/%s: cannot duplicate fd: %m", + session->name, cm->card.base.name); return 0; } r = grdrm_card_open(&cm->card, fd); if (r < 0) { - log_debug("grdrm: %s/%s: cannot open: %s", - session->name, cm->card.base.name, strerror(-r)); + log_debug_errno(r, "grdrm: %s/%s: cannot open: %m", + session->name, cm->card.base.name); return 0; } @@ -2912,8 +2926,8 @@ static void managed_card_take_device(managed_card *cm) { return; error: - log_debug("grdrm: %s/%s: cannot send TakeDevice request: %s", - session->name, cm->card.base.name, strerror(-r)); + log_debug_errno(r, "grdrm: %s/%s: cannot send TakeDevice request: %m", + session->name, cm->card.base.name); } static void managed_card_release_device(managed_card *cm) { @@ -2955,8 +2969,8 @@ static void managed_card_release_device(managed_card *cm) { } if (r < 0 && r != -ENOTCONN) - log_debug("grdrm: %s/%s: cannot send ReleaseDevice: %s", - session->name, cm->card.base.name, strerror(-r)); + log_debug_errno(r, "grdrm: %s/%s: cannot send ReleaseDevice: %m", + session->name, cm->card.base.name); } static int managed_card_new(grdev_card **out, grdev_session *session, struct udev_device *ud) { diff --git a/src/libsystemd-terminal/grdev.c b/src/libsystemd-terminal/grdev.c index 0c21eac551..db87ede762 100644 --- a/src/libsystemd-terminal/grdev.c +++ b/src/libsystemd-terminal/grdev.c @@ -577,8 +577,8 @@ static bool display_cache(grdev_display *display) { out: if (r < 0) - log_debug("grdev: %s/%s: cannot cache pipes: %s", - display->session->name, display->name, strerror(-r)); + log_debug_errno(r, "grdev: %s/%s: cannot cache pipes: %m", + display->session->name, display->name); return true; } @@ -772,8 +772,8 @@ void grdev_pipe_schedule(grdev_pipe *pipe, uint64_t frames) { return; error: - log_debug("grdev: %s/%s/%s: cannot schedule vsync timer: %s", - pipe->card->session->name, pipe->card->name, pipe->name, strerror(-r)); + log_debug_errno(r, "grdev: %s/%s/%s: cannot schedule vsync timer: %m", + pipe->card->session->name, pipe->card->name, pipe->name); } /* @@ -1176,8 +1176,8 @@ void grdev_session_add_drm(grdev_session *session, struct udev_device *ud) { r = grdev_drm_card_new(&card, session, ud); if (r < 0) { - log_debug("grdev: %s: cannot add DRM device for %s: %s", - session->name, udev_device_get_syspath(ud), strerror(-r)); + log_debug_errno(r, "grdev: %s: cannot add DRM device for %s: %m", + session->name, udev_device_get_syspath(ud)); return; } @@ -1269,8 +1269,8 @@ static void session_configure(grdev_session *session) { } else if (!display) { r = grdev_display_new(&display, session, pipe->name); if (r < 0) { - log_debug("grdev: %s/%s: cannot create display for pipe %s: %s", - session->name, card->name, pipe->name, strerror(-r)); + log_debug_errno(r, "grdev: %s/%s: cannot create display for pipe %s: %m", + session->name, card->name, pipe->name); continue; } } diff --git a/src/libsystemd-terminal/idev-evdev.c b/src/libsystemd-terminal/idev-evdev.c index 63fa89e47d..dfbb2d197f 100644 --- a/src/libsystemd-terminal/idev-evdev.c +++ b/src/libsystemd-terminal/idev-evdev.c @@ -222,8 +222,8 @@ static int idev_evdev_io(idev_evdev *evdev) { } if (error < 0) - log_debug("idev-evdev: %s/%s: error on data event: %s", - e->session->name, e->name, strerror(-error)); + log_debug_errno(error, "idev-evdev: %s/%s: error on data event: %m", + e->session->name, e->name); return error; error: @@ -426,15 +426,15 @@ static void unmanaged_evdev_resume(idev_element *e) { fd = open(eu->devnode, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK); if (fd < 0) { if (errno != EACCES && errno != EPERM) { - log_debug("idev-evdev: %s/%s: cannot open node %s: %m", - e->session->name, e->name, eu->devnode); + log_debug_errno(errno, "idev-evdev: %s/%s: cannot open node %s: %m", + e->session->name, e->name, eu->devnode); return; } fd = open(eu->devnode, O_RDONLY | O_CLOEXEC | O_NOCTTY | O_NONBLOCK); if (fd < 0) { - log_debug("idev-evdev: %s/%s: cannot open node %s: %m", - e->session->name, e->name, eu->devnode); + log_debug_errno(errno, "idev-evdev: %s/%s: cannot open node %s: %m", + e->session->name, e->name, eu->devnode); return; } @@ -448,8 +448,8 @@ static void unmanaged_evdev_resume(idev_element *e) { r = idev_evdev_resume(&eu->evdev, fd); if (r < 0) - log_debug("idev-evdev: %s/%s: cannot resume: %s", - e->session->name, e->name, strerror(-r)); + log_debug_errno(r, "idev-evdev: %s/%s: cannot resume: %m", + e->session->name, e->name); } static void unmanaged_evdev_pause(idev_element *e) { @@ -565,14 +565,14 @@ static int managed_evdev_take_device_fn(sd_bus *bus, fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); if (fd < 0) { - log_debug("idev-evdev: %s/%s: cannot duplicate evdev fd: %m", s->name, e->name); + log_debug_errno(errno, "idev-evdev: %s/%s: cannot duplicate evdev fd: %m", s->name, e->name); return 0; } r = idev_evdev_resume(&em->evdev, fd); if (r < 0) - log_debug("idev-evdev: %s/%s: cannot resume: %s", - s->name, e->name, strerror(-r)); + log_debug_errno(r, "idev-evdev: %s/%s: cannot resume: %m", + s->name, e->name); return 0; } @@ -621,8 +621,8 @@ static void managed_evdev_enable(idev_element *e) { return; error: - log_debug("idev-evdev: %s/%s: cannot send TakeDevice request: %s", - s->name, e->name, strerror(-r)); + log_debug_errno(r, "idev-evdev: %s/%s: cannot send TakeDevice request: %m", + s->name, e->name); } static void managed_evdev_disable(idev_element *e) { @@ -679,8 +679,8 @@ static void managed_evdev_disable(idev_element *e) { } if (r < 0 && r != -ENOTCONN) - log_debug("idev-evdev: %s/%s: cannot send ReleaseDevice: %s", - s->name, e->name, strerror(-r)); + log_debug_errno(r, "idev-evdev: %s/%s: cannot send ReleaseDevice: %m", + s->name, e->name); } static void managed_evdev_resume(idev_element *e, int fd) { @@ -698,15 +698,15 @@ static void managed_evdev_resume(idev_element *e, int fd) { fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); if (fd < 0) { - log_debug("idev-evdev: %s/%s: cannot duplicate evdev fd: %m", - s->name, e->name); + log_debug_errno(errno, "idev-evdev: %s/%s: cannot duplicate evdev fd: %m", + s->name, e->name); return; } r = idev_evdev_resume(&em->evdev, fd); if (r < 0) - log_debug("idev-evdev: %s/%s: cannot resume: %s", - s->name, e->name, strerror(-r)); + log_debug_errno(r, "idev-evdev: %s/%s: cannot resume: %m", + s->name, e->name); return; } @@ -776,8 +776,8 @@ static void managed_evdev_pause(idev_element *e, const char *mode) { } if (r < 0) - log_debug("idev-evdev: %s/%s: cannot send PauseDeviceComplete: %s", - s->name, e->name, strerror(-r)); + log_debug_errno(r, "idev-evdev: %s/%s: cannot send PauseDeviceComplete: %m", + s->name, e->name); } } diff --git a/src/libsystemd-terminal/idev-keyboard.c b/src/libsystemd-terminal/idev-keyboard.c index 8dc1c20b14..def8ea5ebe 100644 --- a/src/libsystemd-terminal/idev-keyboard.c +++ b/src/libsystemd-terminal/idev-keyboard.c @@ -25,17 +25,25 @@ #include <systemd/sd-bus.h> #include <systemd/sd-event.h> #include <xkbcommon/xkbcommon.h> +#include <xkbcommon/xkbcommon-compose.h> #include "bus-util.h" #include "hashmap.h" #include "idev.h" #include "idev-internal.h" #include "macro.h" +#include "term-internal.h" #include "util.h" +typedef struct kbdtbl kbdtbl; typedef struct kbdmap kbdmap; typedef struct kbdctx kbdctx; typedef struct idev_keyboard idev_keyboard; +struct kbdtbl { + unsigned long ref; + struct xkb_compose_table *xkb_compose_table; +}; + struct kbdmap { unsigned long ref; struct xkb_keymap *xkb_keymap; @@ -48,10 +56,12 @@ struct kbdctx { idev_context *context; struct xkb_context *xkb_context; struct kbdmap *kbdmap; + struct kbdtbl *kbdtbl; sd_bus_slot *slot_locale_props_changed; sd_bus_slot *slot_locale_get_all; + char *locale_lang; char *locale_x11_model; char *locale_x11_layout; char *locale_x11_variant; @@ -66,8 +76,10 @@ struct idev_keyboard { idev_device device; kbdctx *kbdctx; kbdmap *kbdmap; + kbdtbl *kbdtbl; struct xkb_state *xkb_state; + struct xkb_compose_state *xkb_compose; usec_t repeat_delay; usec_t repeat_rate; @@ -76,6 +88,7 @@ struct idev_keyboard { uint32_t n_syms; idev_data evdata; idev_data repdata; + uint32_t *compose_res; bool repeating : 1; }; @@ -91,6 +104,60 @@ struct idev_keyboard { static const idev_device_vtable keyboard_vtable; static int keyboard_update_kbdmap(idev_keyboard *k); +static int keyboard_update_kbdtbl(idev_keyboard *k); + +/* + * Keyboard Compose Tables + */ + +static kbdtbl *kbdtbl_ref(kbdtbl *kt) { + if (kt) { + assert_return(kt->ref > 0, NULL); + ++kt->ref; + } + + return kt; +} + +static kbdtbl *kbdtbl_unref(kbdtbl *kt) { + if (!kt) + return NULL; + + assert_return(kt->ref > 0, NULL); + + if (--kt->ref > 0) + return NULL; + + xkb_compose_table_unref(kt->xkb_compose_table); + free(kt); + + return 0; +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(kbdtbl*, kbdtbl_unref); + +static int kbdtbl_new_from_locale(kbdtbl **out, kbdctx *kc, const char *locale) { + _cleanup_(kbdtbl_unrefp) kbdtbl *kt = NULL; + + assert_return(out, -EINVAL); + assert_return(locale, -EINVAL); + + kt = new0(kbdtbl, 1); + if (!kt) + return -ENOMEM; + + kt->ref = 1; + + kt->xkb_compose_table = xkb_compose_table_new_from_locale(kc->xkb_context, + locale, + XKB_COMPOSE_COMPILE_NO_FLAGS); + if (!kt->xkb_compose_table) + return errno > 0 ? -errno : -EFAULT; + + *out = kt; + kt = NULL; + return 0; +} /* * Keyboard Keymaps @@ -191,6 +258,49 @@ static int kbdmap_new_from_names(kbdmap **out, * Keyboard Context */ +static int kbdctx_refresh_compose_table(kbdctx *kc, const char *lang) { + kbdtbl *kt; + idev_session *s; + idev_device *d; + Iterator i, j; + int r; + + if (!lang) + lang = "C"; + + if (streq_ptr(kc->locale_lang, lang)) + return 0; + + r = free_and_strdup(&kc->locale_lang, lang); + if (r < 0) + return r; + + log_debug("idev-keyboard: new default compose table: [ %s ]", lang); + + r = kbdtbl_new_from_locale(&kt, kc, lang); + if (r < 0) { + /* TODO: We need to catch the case where no compose-file is + * available. xkb doesn't tell us so far.. so we must not treat + * it as a hard-failure but just continue. Preferably, we want + * xkb to tell us exactly whether compilation failed or whether + * there is no compose file available for this locale. */ + log_debug_errno(r, "idev-keyboard: cannot load compose-table for '%s': %m", + lang); + r = 0; + kt = NULL; + } + + kbdtbl_unref(kc->kbdtbl); + kc->kbdtbl = kt; + + HASHMAP_FOREACH(s, kc->context->session_map, i) + HASHMAP_FOREACH(d, s->device_map, j) + if (idev_is_keyboard(d)) + keyboard_update_kbdtbl(keyboard_from_device(d)); + + return 0; +} + static void move_str(char **dest, char **src) { free(*dest); *dest = *src; @@ -222,11 +332,8 @@ static int kbdctx_refresh_keymap(kbdctx *kc) { /* TODO: add a fallback keymap that's compiled-in */ r = kbdmap_new_from_names(&km, kc, kc->last_x11_model, kc->last_x11_layout, kc->last_x11_variant, kc->last_x11_options); - if (r < 0) { - log_debug("idev-keyboard: cannot create keymap from locale1: %s", - strerror(-r)); - return r; - } + if (r < 0) + return log_debug_errno(r, "idev-keyboard: cannot create keymap from locale1: %m"); kbdmap_unref(kc->kbdmap); kc->kbdmap = km; @@ -239,7 +346,41 @@ static int kbdctx_refresh_keymap(kbdctx *kc) { return 0; } +static int kbdctx_set_locale(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) { + kbdctx *kc = userdata; + const char *s, *ctype = NULL, *lang = NULL; + int r; + + r = sd_bus_message_enter_container(m, 'a', "s"); + if (r < 0) + goto error; + + while ((r = sd_bus_message_read(m, "s", &s)) > 0) { + if (!ctype) + ctype = startswith(s, "LC_CTYPE="); + if (!lang) + lang = startswith(s, "LANG="); + } + + if (r < 0) + goto error; + + r = sd_bus_message_exit_container(m); + if (r < 0) + goto error; + + kbdctx_refresh_compose_table(kc, ctype ? : lang); + r = 0; + +error: + if (r < 0) + log_debug_errno(r, "idev-keyboard: cannot parse locale property from locale1: %m"); + + return r; +} + static const struct bus_properties_map kbdctx_locale_map[] = { + { "Locale", "as", kbdctx_set_locale, 0 }, { "X11Model", "s", NULL, offsetof(kbdctx, locale_x11_model) }, { "X11Layout", "s", NULL, offsetof(kbdctx, locale_x11_layout) }, { "X11Variant", "s", NULL, offsetof(kbdctx, locale_x11_variant) }, @@ -304,8 +445,7 @@ static int kbdctx_query_locale(kbdctx *kc) { return 0; error: - log_debug("idev-keyboard: cannot send GetAll to locale1: %s", strerror(-r)); - return r; + return log_debug_errno(r, "idev-keyboard: cannot send GetAll to locale1: %m"); } static int kbdctx_locale_props_changed_fn(sd_bus *bus, @@ -336,8 +476,7 @@ static int kbdctx_locale_props_changed_fn(sd_bus *bus, return 0; error: - log_debug("idev-keyboard: cannot handle PropertiesChanged from locale1: %s", strerror(-r)); - return r; + return log_debug_errno(r, "idev-keyboard: cannot handle PropertiesChanged from locale1: %m"); } static int kbdctx_setup_bus(kbdctx *kc) { @@ -352,14 +491,33 @@ static int kbdctx_setup_bus(kbdctx *kc) { "path='/org/freedesktop/locale1'", kbdctx_locale_props_changed_fn, kc); - if (r < 0) { - log_debug("idev-keyboard: cannot setup locale1 link: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_debug_errno(r, "idev-keyboard: cannot setup locale1 link: %m"); return kbdctx_query_locale(kc); } +static void kbdctx_log_fn(struct xkb_context *ctx, enum xkb_log_level lvl, const char *format, va_list args) { + char buf[LINE_MAX]; + int sd_lvl; + + if (lvl >= XKB_LOG_LEVEL_DEBUG) + sd_lvl = LOG_DEBUG; + else if (lvl >= XKB_LOG_LEVEL_INFO) + sd_lvl = LOG_INFO; + else if (lvl >= XKB_LOG_LEVEL_WARNING) + sd_lvl = LOG_INFO; /* most XKB warnings really are informational */ + else if (lvl >= XKB_LOG_LEVEL_ERROR) + sd_lvl = LOG_ERR; + else if (lvl >= XKB_LOG_LEVEL_CRITICAL) + sd_lvl = LOG_CRIT; + else + sd_lvl = LOG_CRIT; + + snprintf(buf, sizeof(buf), "idev-xkb: %s", format); + log_internalv(sd_lvl, 0, __FILE__, __LINE__, __func__, buf, args); +} + static kbdctx *kbdctx_ref(kbdctx *kc) { assert_return(kc, NULL); assert_return(kc->ref > 0, NULL); @@ -385,8 +543,10 @@ static kbdctx *kbdctx_unref(kbdctx *kc) { free(kc->locale_x11_variant); free(kc->locale_x11_layout); free(kc->locale_x11_model); + free(kc->locale_lang); kc->slot_locale_get_all = sd_bus_slot_unref(kc->slot_locale_get_all); kc->slot_locale_props_changed = sd_bus_slot_unref(kc->slot_locale_props_changed); + kc->kbdtbl = kbdtbl_unref(kc->kbdtbl); kc->kbdmap = kbdmap_unref(kc->kbdmap); xkb_context_unref(kc->xkb_context); hashmap_remove_value(kc->context->data_map, KBDCTX_KEY, kc); @@ -412,14 +572,21 @@ static int kbdctx_new(kbdctx **out, idev_context *c) { kc->context = c; errno = 0; - kc->xkb_context = xkb_context_new(0); + kc->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); if (!kc->xkb_context) return errno > 0 ? -errno : -EFAULT; + xkb_context_set_log_fn(kc->xkb_context, kbdctx_log_fn); + xkb_context_set_log_level(kc->xkb_context, XKB_LOG_LEVEL_DEBUG); + r = kbdctx_refresh_keymap(kc); if (r < 0) return r; + r = kbdctx_refresh_compose_table(kc, NULL); + if (r < 0) + return r; + if (c->sysbus) { r = kbdctx_setup_bus(kc); if (r < 0) @@ -474,12 +641,85 @@ static int keyboard_raise_data(idev_keyboard *k, idev_data *data) { r = idev_session_raise_device_data(d->session, d, data); if (r < 0) - log_debug("idev-keyboard: %s/%s: error while raising data event: %s", - d->session->name, d->name, strerror(-r)); + log_debug_errno(r, "idev-keyboard: %s/%s: error while raising data event: %m", + d->session->name, d->name); return r; } +static int keyboard_resize_bufs(idev_keyboard *k, uint32_t n_syms) { + uint32_t *t; + + if (n_syms <= k->n_syms) + return 0; + + t = realloc(k->compose_res, sizeof(*t) * n_syms); + if (!t) + return -ENOMEM; + k->compose_res = t; + + t = realloc(k->evdata.keyboard.keysyms, sizeof(*t) * n_syms); + if (!t) + return -ENOMEM; + k->evdata.keyboard.keysyms = t; + + t = realloc(k->evdata.keyboard.codepoints, sizeof(*t) * n_syms); + if (!t) + return -ENOMEM; + k->evdata.keyboard.codepoints = t; + + t = realloc(k->repdata.keyboard.keysyms, sizeof(*t) * n_syms); + if (!t) + return -ENOMEM; + k->repdata.keyboard.keysyms = t; + + t = realloc(k->repdata.keyboard.codepoints, sizeof(*t) * n_syms); + if (!t) + return -ENOMEM; + k->repdata.keyboard.codepoints = t; + + k->n_syms = n_syms; + return 0; +} + +static unsigned int keyboard_read_compose(idev_keyboard *k, const xkb_keysym_t **out) { + _cleanup_free_ char *t = NULL; + term_utf8 u8 = { }; + char buf[256], *p; + size_t flen = 0; + int i, r; + + r = xkb_compose_state_get_utf8(k->xkb_compose, buf, sizeof(buf)); + if (r >= (int)sizeof(buf)) { + t = malloc(r + 1); + if (!t) + return 0; + + xkb_compose_state_get_utf8(k->xkb_compose, t, r + 1); + p = t; + } else { + p = buf; + } + + for (i = 0; i < r; ++i) { + uint32_t *ucs; + size_t len, j; + + len = term_utf8_decode(&u8, &ucs, p[i]); + if (len > 0) { + r = keyboard_resize_bufs(k, flen + len); + if (r < 0) + return 0; + + for (j = 0; j < len; ++j) + k->compose_res[flen++] = ucs[j]; + } + } + + *out = k->compose_res; + return flen; +} + static void keyboard_arm(idev_keyboard *k, usec_t usecs) { int r; @@ -496,6 +736,8 @@ static void keyboard_arm(idev_keyboard *k, usec_t usecs) { static int keyboard_repeat_timer_fn(sd_event_source *source, uint64_t usec, void *userdata) { idev_keyboard *k = userdata; + /* never feed REPEAT keys into COMPOSE */ + keyboard_arm(k, k->repeat_rate); return keyboard_raise_data(k, &k->repdata); } @@ -529,6 +771,14 @@ int idev_keyboard_new(idev_device **out, idev_session *s, const char *name) { if (r < 0) return r; + r = keyboard_update_kbdtbl(k); + if (r < 0) + return r; + + r = keyboard_resize_bufs(k, 8); + if (r < 0) + return r; + r = sd_event_add_time(s->context->event, &k->repeat_timer, CLOCK_MONOTONIC, @@ -557,12 +807,15 @@ int idev_keyboard_new(idev_device **out, idev_session *s, const char *name) { static void keyboard_free(idev_device *d) { idev_keyboard *k = keyboard_from_device(d); + xkb_compose_state_unref(k->xkb_compose); xkb_state_unref(k->xkb_state); free(k->repdata.keyboard.codepoints); free(k->repdata.keyboard.keysyms); free(k->evdata.keyboard.codepoints); free(k->evdata.keyboard.keysyms); + free(k->compose_res); k->repeat_timer = sd_event_source_unref(k->repeat_timer); + k->kbdtbl = kbdtbl_unref(k->kbdtbl); k->kbdmap = kbdmap_unref(k->kbdmap); k->kbdctx = kbdctx_unref(k->kbdctx); free(k); @@ -600,34 +853,13 @@ static int keyboard_fill(idev_keyboard *k, const uint32_t *keysyms) { idev_data_keyboard *kev; uint32_t i; + int r; assert(dst == &k->evdata || dst == &k->repdata); - if (n_syms > k->n_syms) { - uint32_t *t; - - t = realloc(k->evdata.keyboard.keysyms, sizeof(*t) * n_syms); - if (!t) - return -ENOMEM; - k->evdata.keyboard.keysyms = t; - - t = realloc(k->evdata.keyboard.codepoints, sizeof(*t) * n_syms); - if (!t) - return -ENOMEM; - k->evdata.keyboard.codepoints = t; - - t = realloc(k->repdata.keyboard.keysyms, sizeof(*t) * n_syms); - if (!t) - return -ENOMEM; - k->repdata.keyboard.keysyms = t; - - t = realloc(k->repdata.keyboard.codepoints, sizeof(*t) * n_syms); - if (!t) - return -ENOMEM; - k->repdata.keyboard.codepoints = t; - - k->n_syms = n_syms; - } + r = keyboard_resize_bufs(k, n_syms); + if (r < 0) + return r; dst->type = IDEV_DATA_KEYBOARD; dst->resync = resync; @@ -647,8 +879,6 @@ static int keyboard_fill(idev_keyboard *k, } for (i = 0; i < IDEV_KBDMOD_CNT; ++i) { - int r; - if (k->kbdmap->modmap[i] == XKB_MOD_INVALID) continue; @@ -715,8 +945,8 @@ static void keyboard_repeat(idev_keyboard *k) { r = keyboard_fill(k, repdata, false, evkbd->keycode, KBDKEY_REPEAT, num, keysyms); if (r < 0) { - log_debug("idev-keyboard: %s/%s: cannot set key-repeat: %s", - d->session->name, d->name, strerror(-r)); + log_debug_errno(r, "idev-keyboard: %s/%s: cannot set key-repeat: %m", + d->session->name, d->name); k->repeating = false; keyboard_arm(k, 0); } else { @@ -740,8 +970,8 @@ static void keyboard_repeat(idev_keyboard *k) { r = keyboard_fill(k, repdata, false, repkbd->keycode, KBDKEY_REPEAT, num, keysyms); if (r < 0) { - log_debug("idev-keyboard: %s/%s: cannot update key-repeat: %s", - d->session->name, d->name, strerror(-r)); + log_debug_errno(r, "idev-keyboard: %s/%s: cannot update key-repeat: %m", + d->session->name, d->name); k->repeating = false; keyboard_arm(k, 0); } @@ -751,6 +981,7 @@ static void keyboard_repeat(idev_keyboard *k) { static int keyboard_feed_evdev(idev_keyboard *k, idev_data *data) { struct input_event *ev = &data->evdev.event; enum xkb_state_component compch; + enum xkb_compose_status cstatus; const xkb_keysym_t *keysyms; idev_device *d = &k->device; int num, r; @@ -775,6 +1006,52 @@ static int keyboard_feed_evdev(idev_keyboard *k, idev_data *data) { goto error; } + if (k->xkb_compose && ev->value == KBDKEY_DOWN) { + if (num == 1 && !data->resync) { + xkb_compose_state_feed(k->xkb_compose, keysyms[0]); + cstatus = xkb_compose_state_get_status(k->xkb_compose); + } else { + cstatus = XKB_COMPOSE_CANCELLED; + } + + switch (cstatus) { + case XKB_COMPOSE_NOTHING: + /* keep produced keysyms and forward unchanged */ + break; + case XKB_COMPOSE_COMPOSING: + /* consumed by compose-state, drop keysym */ + keysyms = NULL; + num = 0; + break; + case XKB_COMPOSE_COMPOSED: + /* compose-state produced sth, replace keysym */ + num = keyboard_read_compose(k, &keysyms); + xkb_compose_state_reset(k->xkb_compose); + break; + case XKB_COMPOSE_CANCELLED: + /* canceled compose, reset, forward cancellation sym */ + xkb_compose_state_reset(k->xkb_compose); + break; + } + } else if (k->xkb_compose && + num == 1 && + keysyms[0] == XKB_KEY_Multi_key && + !data->resync && + ev->value == KBDKEY_UP) { + /* Reset compose state on Multi-Key UP events. This effectively + * requires you to hold the key during the whole sequence. I + * think it's pretty handy to avoid accidental + * Compose-sequences, but this may break Compose for disabled + * people. We really need to make this opional! (TODO) */ + xkb_compose_state_reset(k->xkb_compose); + } + + if (ev->value == KBDKEY_UP) { + /* never produce keysyms for UP */ + keysyms = NULL; + num = 0; + } + r = keyboard_fill(k, &k->evdata, data->resync, ev->code, ev->value, num, keysyms); if (r < 0) goto error; @@ -783,8 +1060,8 @@ static int keyboard_feed_evdev(idev_keyboard *k, idev_data *data) { return keyboard_raise_data(k, &k->evdata); error: - log_debug("idev-keyboard: %s/%s: cannot handle event: %s", - d->session->name, d->name, strerror(-r)); + log_debug_errno(r, "idev-keyboard: %s/%s: cannot handle event: %m", + d->session->name, d->name); k->repeating = false; keyboard_arm(k, 0); return 0; @@ -844,9 +1121,41 @@ static int keyboard_update_kbdmap(idev_keyboard *k) { return 0; error: - log_debug("idev-keyboard: %s/%s: cannot adopt new keymap: %s", - d->session->name, d->name, strerror(-r)); - return r; + return log_debug_errno(r, "idev-keyboard: %s/%s: cannot adopt new keymap: %m", + d->session->name, d->name); +} + +static int keyboard_update_kbdtbl(idev_keyboard *k) { + idev_device *d = &k->device; + struct xkb_compose_state *compose = NULL; + kbdtbl *kt; + int r; + + assert(k); + + kt = k->kbdctx->kbdtbl; + if (kt == k->kbdtbl) + return 0; + + if (kt) { + errno = 0; + compose = xkb_compose_state_new(kt->xkb_compose_table, XKB_COMPOSE_STATE_NO_FLAGS); + if (!compose) { + r = errno > 0 ? -errno : -EFAULT; + goto error; + } + } + + kbdtbl_unref(k->kbdtbl); + k->kbdtbl = kbdtbl_ref(kt); + xkb_compose_state_unref(k->xkb_compose); + k->xkb_compose = compose; + + return 0; + +error: + return log_debug_errno(r, "idev-keyboard: %s/%s: cannot adopt new compose table: %m", + d->session->name, d->name); } static const idev_device_vtable keyboard_vtable = { diff --git a/src/libsystemd-terminal/idev.c b/src/libsystemd-terminal/idev.c index e979b608b6..989683f39a 100644 --- a/src/libsystemd-terminal/idev.c +++ b/src/libsystemd-terminal/idev.c @@ -351,8 +351,8 @@ static int session_add_device(idev_session *s, idev_device *d) { error: if (r < 0) - log_debug("idev: %s: error while adding device '%s': %s", - s->name, d->name, strerror(-r)); + log_debug_errno(r, "idev: %s: error while adding device '%s': %m", + s->name, d->name); return r; } @@ -372,8 +372,8 @@ static int session_remove_device(idev_session *s, idev_device *d) { idev_device_disable(d); if (error < 0) - log_debug("idev: %s: error while removing device '%s': %s", - s->name, d->name, strerror(-error)); + log_debug_errno(error, "idev: %s: error while removing device '%s': %m", + s->name, d->name); idev_device_free(d); return error; } @@ -420,8 +420,8 @@ static int session_remove_element(idev_session *s, idev_element *e) { element_disable(e); if (error < 0) - log_debug("idev: %s: error while removing element '%s': %s", - s->name, e->name, strerror(-r)); + log_debug_errno(r, "idev: %s: error while removing element '%s': %m", + s->name, e->name); idev_element_free(e); return error; } diff --git a/src/libsystemd-terminal/modeset.c b/src/libsystemd-terminal/modeset.c index f5be38e4fa..6e13432d68 100644 --- a/src/libsystemd-terminal/modeset.c +++ b/src/libsystemd-terminal/modeset.c @@ -146,16 +146,12 @@ static int modeset_new(Modeset **out) { return log_oom(); r = sd_pid_get_session(getpid(), &m->session); - if (r < 0) { - log_error("Cannot retrieve logind session: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Cannot retrieve logind session: %m"); r = sd_session_get_seat(m->session, &m->seat); - if (r < 0) { - log_error("Cannot retrieve seat of logind session: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Cannot retrieve seat of logind session: %m"); m->my_tty = is_my_tty(m->session); m->managed = m->my_tty && geteuid() > 0; @@ -309,17 +305,13 @@ static int modeset_sysview_fn(sysview_context *c, void *userdata, sysview_event name, modeset_grdev_fn, m); - if (r < 0) { - log_error("Cannot create grdev session: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Cannot create grdev session: %m"); if (m->managed) { r = sysview_session_take_control(ev->session_add.session); - if (r < 0) { - log_error("Cannot request session control: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Cannot request session control: %m"); } grdev_session_enable(m->grdev_session); @@ -358,16 +350,12 @@ static int modeset_sysview_fn(sysview_context *c, void *userdata, sysview_event break; case SYSVIEW_EVENT_SESSION_CONTROL: r = ev->session_control.error; - if (r < 0) { - log_error("Cannot acquire session control: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Cannot acquire session control: %m"); r = ioctl(1, KDSKBMODE, K_UNICODE); - if (r < 0) { - log_error("Cannot set K_UNICODE on stdout: %m"); - return -errno; - } + if (r < 0) + return log_error_errno(errno, "Cannot set K_UNICODE on stdout: %m"); break; } @@ -480,7 +468,7 @@ int main(int argc, char *argv[]) { log_parse_environment(); log_open(); - srand(time(NULL)); + initialize_srand(); r = parse_argv(argc, argv); if (r <= 0) diff --git a/src/libsystemd-terminal/subterm.c b/src/libsystemd-terminal/subterm.c index 93c06bea83..78efc9d7c0 100644 --- a/src/libsystemd-terminal/subterm.c +++ b/src/libsystemd-terminal/subterm.c @@ -102,10 +102,8 @@ static int output_winch(Output *o) { assert_return(o, -EINVAL); r = ioctl(o->fd, TIOCGWINSZ, &wsz); - if (r < 0) { - log_error("error: cannot read window-size: %m"); - return -errno; - } + if (r < 0) + return log_error_errno(errno, "error: cannot read window-size: %m"); if (wsz.ws_col != o->width || wsz.ws_row != o->height) { o->width = wsz.ws_col; @@ -119,16 +117,14 @@ static int output_winch(Output *o) { } static int output_flush(Output *o) { - ssize_t len; + int r; if (o->n_obuf < 1) return 0; - len = loop_write(o->fd, o->obuf, o->n_obuf, false); - if (len < 0) { - log_error("error: cannot write to TTY (%zd): %s", len, strerror(-len)); - return len; - } + r = loop_write(o->fd, o->obuf, o->n_obuf, false); + if (r < 0) + return log_error_errno(r, "error: cannot write to TTY: %m"); o->n_obuf = 0; @@ -156,10 +152,8 @@ static int output_write(Output *o, const void *buf, size_t size) { return r; len = loop_write(o->fd, buf, size, false); - if (len < 0) { - log_error("error: cannot write to TTY (%zd): %s", len, strerror(-len)); - return len; - } + if (len < 0) + return log_error_errno(len, "error: cannot write to TTY (%zd): %m", len); return 0; } @@ -612,12 +606,12 @@ static int terminal_winch_fn(sd_event_source *source, const struct signalfd_sigi if (t->pty) { r = pty_resize(t->pty, t->output->in_width, t->output->in_height); if (r < 0) - log_error("error: pty_resize() (%d): %s", r, strerror(-r)); + log_error_errno(r, "error: pty_resize() (%d): %m", r); } r = term_screen_resize(t->screen, t->output->in_width, t->output->in_height); if (r < 0) - log_error("error: term_screen_resize() (%d): %s", r, strerror(-r)); + log_error_errno(r, "error: term_screen_resize() (%d): %m", r); terminal_dirty(t); @@ -656,10 +650,8 @@ static int terminal_write_tmp(Terminal *t) { if (t->pty) { for (i = 0; i < num; ++i) { r = pty_write(t->pty, vec[i].iov_base, vec[i].iov_len); - if (r < 0) { - log_error("error: cannot write to PTY (%d): %s", r, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "error: cannot write to PTY (%d): %m", r); } } @@ -713,7 +705,7 @@ static int terminal_io_fn(sd_event_source *source, int fd, uint32_t revents, voi if (errno == EAGAIN || errno == EINTR) return 0; - log_error("error: cannot read from TTY (%d): %m", -errno); + log_error_errno(errno, "error: cannot read from TTY (%d): %m", -errno); return -errno; } @@ -725,10 +717,8 @@ static int terminal_io_fn(sd_event_source *source, int fd, uint32_t revents, voi n_str = term_utf8_decode(&t->utf8, &str, buf[i]); for (j = 0; j < n_str; ++j) { type = term_parser_feed(t->parser, &seq, str[j]); - if (type < 0) { - log_error("error: term_parser_feed() (%d): %s", type, strerror(-type)); - return type; - } + if (type < 0) + return log_error_errno(type, "error: term_parser_feed() (%d): %m", type); if (!t->is_menu) { r = terminal_push_tmp(t, str[j]); @@ -777,10 +767,8 @@ static int terminal_pty_fn(Pty *pty, void *userdata, unsigned int event, const v break; case PTY_DATA: r = term_screen_feed_text(t->screen, ptr, size); - if (r < 0) { - log_error("error: term_screen_feed_text() (%d): %s", r, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "error: term_screen_feed_text() (%d): %m", r); terminal_dirty(t); break; @@ -832,16 +820,12 @@ static int terminal_new(Terminal **out, int in_fd, int out_fd) { assert_return(out, -EINVAL); r = tcgetattr(in_fd, &in_attr); - if (r < 0) { - log_error("error: tcgetattr() (%d): %m", -errno); - return -errno; - } + if (r < 0) + return log_error_errno(errno, "error: tcgetattr() (%d): %m", -errno); r = tcgetattr(out_fd, &out_attr); - if (r < 0) { - log_error("error: tcgetattr() (%d): %m", -errno); - return -errno; - } + if (r < 0) + return log_error_errno(errno, "error: tcgetattr() (%d): %m", -errno); t = new0(Terminal, 1); if (!t) @@ -857,49 +841,49 @@ static int terminal_new(Terminal **out, int in_fd, int out_fd) { r = tcsetattr(t->in_fd, TCSANOW, &in_attr); if (r < 0) { - log_error("error: tcsetattr() (%d): %s", r, strerror(-r)); + log_error_errno(r, "error: tcsetattr() (%d): %m", r); goto error; } r = tcsetattr(t->out_fd, TCSANOW, &out_attr); if (r < 0) { - log_error("error: tcsetattr() (%d): %s", r, strerror(-r)); + log_error_errno(r, "error: tcsetattr() (%d): %m", r); goto error; } r = sd_event_default(&t->event); if (r < 0) { - log_error("error: sd_event_default() (%d): %s", r, strerror(-r)); + log_error_errno(r, "error: sd_event_default() (%d): %m", r); goto error; } r = sigprocmask_many(SIG_BLOCK, SIGINT, SIGQUIT, SIGTERM, SIGWINCH, SIGCHLD, -1); if (r < 0) { - log_error("error: sigprocmask_many() (%d): %s", r, strerror(-r)); + log_error_errno(r, "error: sigprocmask_many() (%d): %m", r); goto error; } r = sd_event_add_signal(t->event, NULL, SIGINT, NULL, NULL); if (r < 0) { - log_error("error: sd_event_add_signal() (%d): %s", r, strerror(-r)); + log_error_errno(r, "error: sd_event_add_signal() (%d): %m", r); goto error; } r = sd_event_add_signal(t->event, NULL, SIGQUIT, NULL, NULL); if (r < 0) { - log_error("error: sd_event_add_signal() (%d): %s", r, strerror(-r)); + log_error_errno(r, "error: sd_event_add_signal() (%d): %m", r); goto error; } r = sd_event_add_signal(t->event, NULL, SIGTERM, NULL, NULL); if (r < 0) { - log_error("error: sd_event_add_signal() (%d): %s", r, strerror(-r)); + log_error_errno(r, "error: sd_event_add_signal() (%d): %m", r); goto error; } r = sd_event_add_signal(t->event, NULL, SIGWINCH, terminal_winch_fn, t); if (r < 0) { - log_error("error: sd_event_add_signal() (%d): %s", r, strerror(-r)); + log_error_errno(r, "error: sd_event_add_signal() (%d): %m", r); goto error; } @@ -907,7 +891,7 @@ static int terminal_new(Terminal **out, int in_fd, int out_fd) { t->is_dirty = true; r = sd_event_add_time(t->event, &t->frame_timer, CLOCK_MONOTONIC, 0, 0, terminal_frame_timer_fn, t); if (r < 0) { - log_error("error: sd_event_add_time() (%d): %s", r, strerror(-r)); + log_error_errno(r, "error: sd_event_add_time() (%d): %m", r); goto error; } @@ -929,7 +913,7 @@ static int terminal_new(Terminal **out, int in_fd, int out_fd) { r = term_screen_resize(t->screen, t->output->in_width, t->output->in_height); if (r < 0) { - log_error("error: term_screen_resize() (%d): %s", r, strerror(-r)); + log_error_errno(r, "error: term_screen_resize() (%d): %m", r); goto error; } @@ -951,10 +935,9 @@ static int terminal_run(Terminal *t) { assert_return(t, -EINVAL); pid = pty_fork(&t->pty, t->event, terminal_pty_fn, t, t->output->in_width, t->output->in_height); - if (pid < 0) { - log_error("error: cannot fork PTY (%d): %s", pid, strerror(-pid)); - return pid; - } else if (pid == 0) { + if (pid < 0) + return log_error_errno(pid, "error: cannot fork PTY (%d): %m", pid); + else if (pid == 0) { /* child */ char **argv = (char*[]){ @@ -966,7 +949,7 @@ static int terminal_run(Terminal *t) { setenv("COLORTERM", "systemd-subterm", 1); execve(argv[0], argv, environ); - log_error("error: cannot exec %s (%d): %m", argv[0], -errno); + log_error_errno(errno, "error: cannot exec %s (%d): %m", argv[0], -errno); _exit(1); } @@ -993,7 +976,7 @@ int main(int argc, char *argv[]) { out: if (r < 0) - log_error("error: terminal failed (%d): %s", r, strerror(-r)); + log_error_errno(r, "error: terminal failed (%d): %m", r); terminal_free(t); return -r; } diff --git a/src/libsystemd-terminal/sysview.c b/src/libsystemd-terminal/sysview.c index 70a6ca726c..9ee32db1bc 100644 --- a/src/libsystemd-terminal/sysview.c +++ b/src/libsystemd-terminal/sysview.c @@ -295,8 +295,8 @@ static int session_take_control_fn(sd_bus *bus, r = context_raise_session_control(session->seat->context, session, error); if (r < 0) - log_debug("sysview: callback failed while signalling session control '%d' on session '%s': %s", - error, session->name, strerror(-r)); + log_debug_errno(r, "sysview: callback failed while signalling session control '%d' on session '%s': %m", + error, session->name); return 0; } @@ -365,8 +365,8 @@ void sysview_session_release_control(sysview_session *session) { r = sd_bus_send(session->seat->context->sysbus, m, NULL); if (r < 0 && r != -ENOTCONN) - log_debug("sysview: %s: cannot send ReleaseControl: %s", - session->name, strerror(-r)); + log_debug_errno(r, "sysview: %s: cannot send ReleaseControl: %m", + session->name); } /* @@ -604,8 +604,8 @@ static void context_add_device(sysview_context *c, sysview_device *device) { r = context_raise_session_attach(c, session, device); if (r < 0) - log_debug("sysview: callback failed while attaching device '%s' to session '%s': %s", - device->name, session->name, strerror(-r)); + log_debug_errno(r, "sysview: callback failed while attaching device '%s' to session '%s': %m", + device->name, session->name); } } @@ -625,8 +625,8 @@ static void context_remove_device(sysview_context *c, sysview_device *device) { r = context_raise_session_detach(c, session, device); if (r < 0) - log_debug("sysview: callback failed while detaching device '%s' from session '%s': %s", - device->name, session->name, strerror(-r)); + log_debug_errno(r, "sysview: callback failed while detaching device '%s' from session '%s': %m", + device->name, session->name); } sysview_device_free(device); @@ -648,8 +648,8 @@ static void context_change_device(sysview_context *c, sysview_device *device, st r = context_raise_session_refresh(c, session, device, ud); if (r < 0) - log_debug("sysview: callback failed while changing device '%s' on session '%s': %s", - device->name, session->name, strerror(-r)); + log_debug_errno(r, "sysview: callback failed while changing device '%s' on session '%s': %m", + device->name, session->name); } } @@ -683,8 +683,8 @@ static void context_add_session(sysview_context *c, sysview_seat *seat, const ch session->public = true; r = context_raise_session_add(c, session); if (r < 0) { - log_debug("sysview: callback failed while adding session '%s': %s", - session->name, strerror(-r)); + log_debug_errno(r, "sysview: callback failed while adding session '%s': %m", + session->name); session->public = false; goto error; } @@ -692,8 +692,8 @@ static void context_add_session(sysview_context *c, sysview_seat *seat, const ch HASHMAP_FOREACH(device, seat->device_map, i) { r = context_raise_session_attach(c, session, device); if (r < 0) - log_debug("sysview: callback failed while attaching device '%s' to new session '%s': %s", - device->name, session->name, strerror(-r)); + log_debug_errno(r, "sysview: callback failed while attaching device '%s' to new session '%s': %m", + device->name, session->name); } } @@ -701,8 +701,8 @@ static void context_add_session(sysview_context *c, sysview_seat *seat, const ch error: if (r < 0) - log_debug("sysview: error while adding session '%s': %s", - id, strerror(-r)); + log_debug_errno(r, "sysview: error while adding session '%s': %m", + id); } static void context_remove_session(sysview_context *c, sysview_session *session) { @@ -719,15 +719,15 @@ static void context_remove_session(sysview_context *c, sysview_session *session) HASHMAP_FOREACH(device, session->seat->device_map, i) { r = context_raise_session_detach(c, session, device); if (r < 0) - log_debug("sysview: callback failed while detaching device '%s' from old session '%s': %s", - device->name, session->name, strerror(-r)); + log_debug_errno(r, "sysview: callback failed while detaching device '%s' from old session '%s': %m", + device->name, session->name); } session->public = false; r = context_raise_session_remove(c, session); if (r < 0) - log_debug("sysview: callback failed while removing session '%s': %s", - session->name, strerror(-r)); + log_debug_errno(r, "sysview: callback failed while removing session '%s': %m", + session->name); } if (!session->custom) @@ -756,8 +756,8 @@ static void context_add_seat(sysview_context *c, const char *id) { seat->public = true; r = context_raise_seat_add(c, seat); if (r < 0) { - log_debug("sysview: callback failed while adding seat '%s': %s", - seat->name, strerror(-r)); + log_debug_errno(r, "sysview: callback failed while adding seat '%s': %m", + seat->name); seat->public = false; } @@ -765,8 +765,8 @@ static void context_add_seat(sysview_context *c, const char *id) { error: if (r < 0) - log_debug("sysview: error while adding seat '%s': %s", - id, strerror(-r)); + log_debug_errno(r, "sysview: error while adding seat '%s': %m", + id); } static void context_remove_seat(sysview_context *c, sysview_seat *seat) { @@ -789,8 +789,8 @@ static void context_remove_seat(sysview_context *c, sysview_seat *seat) { seat->public = false; r = context_raise_seat_remove(c, seat); if (r < 0) - log_debug("sysview: callback failed while removing seat '%s': %s", - seat->name, strerror(-r)); + log_debug_errno(r, "sysview: callback failed while removing seat '%s': %m", + seat->name); } sysview_seat_free(seat); @@ -975,11 +975,9 @@ static int context_ud_hotplug(sysview_context *c, struct udev_device *d) { return 0; r = device_new_ud(&device, seat, type, d); - if (r < 0) { - log_debug("sysview: cannot create device for udev-device '%s': %s", - syspath, strerror(-r)); - return r; - } + if (r < 0) + return log_debug_errno(r, "sysview: cannot create device for udev-device '%s': %m", + syspath); context_add_device(c, device); } @@ -1083,8 +1081,8 @@ static int context_ud_scan(sysview_context *c) { d = udev_device_new_from_syspath(c->ud, name); if (!d) { r = errno > 0 ? -errno : -EFAULT; - log_debug("sysview: cannot create udev-device for %s: %s", - name, strerror(-r)); + log_debug_errno(r, "sysview: cannot create udev-device for %s: %m", + name); continue; } @@ -1102,11 +1100,8 @@ static int context_ld_seat_new(sysview_context *c, sd_bus_message *signal) { int r; r = sd_bus_message_read(signal, "so", &id, &path); - if (r < 0) { - log_debug("sysview: cannot parse SeatNew from logind: %s", - strerror(-r)); - return r; - } + if (r < 0) + return log_debug_errno(r, "sysview: cannot parse SeatNew from logind: %m"); context_add_seat(c, id); return 0; @@ -1118,11 +1113,8 @@ static int context_ld_seat_removed(sysview_context *c, sd_bus_message *signal) { int r; r = sd_bus_message_read(signal, "so", &id, &path); - if (r < 0) { - log_debug("sysview: cannot parse SeatRemoved from logind: %s", - strerror(-r)); - return r; - } + if (r < 0) + return log_debug_errno(r, "sysview: cannot parse SeatRemoved from logind: %m"); seat = sysview_find_seat(c, id); if (!seat) @@ -1140,11 +1132,8 @@ static int context_ld_session_new(sysview_context *c, sd_bus_message *signal) { int r; r = sd_bus_message_read(signal, "so", &id, &path); - if (r < 0) { - log_debug("sysview: cannot parse SessionNew from logind: %s", - strerror(-r)); - return r; - } + if (r < 0) + return log_debug_errno(r, "sysview: cannot parse SessionNew from logind: %m"); /* * As the dbus message didn't contain enough information, we @@ -1177,17 +1166,16 @@ static int context_ld_session_new(sysview_context *c, sd_bus_message *signal) { r = context_raise_session_filter(c, id, seatid, username, uid); if (r < 0) - log_debug("sysview: callback failed while filtering session '%s': %s", - id, strerror(-r)); + log_debug_errno(r, "sysview: callback failed while filtering session '%s': %m", + id); else if (r > 0) context_add_session(c, seat, id); return 0; error: - log_debug("sysview: failed retrieving information for new session '%s': %s", - id, strerror(-r)); - return r; + return log_debug_errno(r, "sysview: failed retrieving information for new session '%s': %m", + id); } static int context_ld_session_removed(sysview_context *c, sd_bus_message *signal) { @@ -1196,11 +1184,8 @@ static int context_ld_session_removed(sysview_context *c, sd_bus_message *signal int r; r = sd_bus_message_read(signal, "so", &id, &path); - if (r < 0) { - log_debug("sysview: cannot parse SessionRemoved from logind: %s", - strerror(-r)); - return r; - } + if (r < 0) + return log_debug_errno(r, "sysview: cannot parse SessionRemoved from logind: %m"); session = sysview_find_session(c, id); if (!session) @@ -1299,9 +1284,7 @@ static int context_ld_list_seats_fn(sd_bus *bus, return 0; error: - log_debug("sysview: erroneous ListSeats response from logind: %s", - strerror(-r)); - return r; + return log_debug_errno(r, "sysview: erroneous ListSeats response from logind: %m"); } static int context_ld_list_sessions_fn(sd_bus *bus, @@ -1344,8 +1327,8 @@ static int context_ld_list_sessions_fn(sd_bus *bus, if (seat) { r = context_raise_session_filter(c, id, seatid, username, uid); if (r < 0) - log_debug("sysview: callback failed while filtering session '%s': %s", - id, strerror(-r)); + log_debug_errno(r, "sysview: callback failed while filtering session '%s': %m", + id); else if (r > 0) context_add_session(c, seat, id); } @@ -1365,9 +1348,7 @@ static int context_ld_list_sessions_fn(sd_bus *bus, return 0; error: - log_debug("sysview: erroneous ListSessions response from logind: %s", - strerror(-r)); - return r; + return log_debug_errno(r, "sysview: erroneous ListSessions response from logind: %m"); } static int context_ld_scan(sysview_context *c) { @@ -1497,19 +1478,15 @@ static int context_scan_fn(sd_event_source *s, void *userdata) { if (!c->scanned) { r = context_ld_scan(c); - if (r < 0) { - log_debug("sysview: logind scan failed: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_debug_errno(r, "sysview: logind scan failed: %m"); } /* skip device scans if no sessions are available */ if (hashmap_size(c->session_map) > 0) { r = context_ud_scan(c); - if (r < 0) { - log_debug("sysview: udev scan failed: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_debug_errno(r, "sysview: udev scan failed: %m"); HASHMAP_FOREACH(seat, c->seat_map, i) seat->scanned = true; diff --git a/src/libsystemd-terminal/term-screen.c b/src/libsystemd-terminal/term-screen.c index acd7cc966a..51d93765e4 100644 --- a/src/libsystemd-terminal/term-screen.c +++ b/src/libsystemd-terminal/term-screen.c @@ -549,7 +549,6 @@ static int screen_LF(term_screen *screen, const term_seq *seq); static int screen_GRAPHIC(term_screen *screen, const term_seq *seq) { term_char_t ch = TERM_CHAR_NULL; - uint32_t c; if (screen->state.cursor_x + 1 == screen->page->width && screen->flags & TERM_FLAG_PENDING_WRAP @@ -560,8 +559,7 @@ static int screen_GRAPHIC(term_screen *screen, const term_seq *seq) { screen_cursor_clear_wrap(screen); - c = screen_map(screen, seq->terminator); - ch = term_char_merge(ch, screen_map(screen, c)); + ch = term_char_merge(ch, screen_map(screen, seq->terminator)); term_page_write(screen->page, screen->state.cursor_x, screen->state.cursor_y, ch, 1, &screen->state.attr, screen->age, false); if (screen->state.cursor_x + 1 == screen->page->width) diff --git a/src/libsystemd/libsystemd.sym.m4 b/src/libsystemd/libsystemd.sym.m4 index ab11448bb9..de8bec3098 100644 --- a/src/libsystemd/libsystemd.sym.m4 +++ b/src/libsystemd/libsystemd.sym.m4 @@ -179,7 +179,7 @@ global: sd_bus_set_anonymous; sd_bus_set_trusted; sd_bus_set_monitor; - sd_bus_set_name; + sd_bus_set_description; sd_bus_negotiate_fds; sd_bus_negotiate_timestamp; sd_bus_negotiate_creds; @@ -190,9 +190,9 @@ global: sd_bus_unref; sd_bus_is_open; sd_bus_can_send; - sd_bus_get_server_id; + sd_bus_get_bus_id; sd_bus_get_owner_creds; - sd_bus_get_name; + sd_bus_get_description; sd_bus_send; sd_bus_send_to; sd_bus_call; @@ -223,6 +223,8 @@ global: sd_bus_slot_get_bus; sd_bus_slot_get_userdata; sd_bus_slot_set_userdata; + sd_bus_slot_get_description; + sd_bus_slot_set_description; sd_bus_slot_get_current_message; sd_bus_message_new_signal; sd_bus_message_new_method_call; @@ -252,6 +254,7 @@ global: sd_bus_message_get_realtime_usec; sd_bus_message_get_seqnum; sd_bus_message_get_creds; + sd_bus_message_is_empty; sd_bus_message_is_signal; sd_bus_message_is_method_call; sd_bus_message_is_method_error; @@ -315,7 +318,6 @@ global: sd_bus_creds_get_uid; sd_bus_creds_get_gid; sd_bus_creds_get_pid; - sd_bus_creds_get_pid_starttime; sd_bus_creds_get_tid; sd_bus_creds_get_comm; sd_bus_creds_get_tid_comm; @@ -336,7 +338,7 @@ global: sd_bus_creds_get_audit_login_uid; sd_bus_creds_get_unique_name; sd_bus_creds_get_well_known_names; - sd_bus_creds_get_connection_name; + sd_bus_creds_get_description; sd_bus_error_free; sd_bus_error_set; sd_bus_error_setf; @@ -387,8 +389,8 @@ global: sd_event_get_watchdog; sd_event_source_ref; sd_event_source_unref; - sd_event_source_set_name; - sd_event_source_get_name; + sd_event_source_set_description; + sd_event_source_get_description; sd_event_source_set_prepare; sd_event_source_get_pending; sd_event_source_get_priority; diff --git a/src/libsystemd/sd-bus/PORTING-DBUS1 b/src/libsystemd/sd-bus/PORTING-DBUS1 index 81e94132b3..2dedb28bcf 100644 --- a/src/libsystemd/sd-bus/PORTING-DBUS1 +++ b/src/libsystemd/sd-bus/PORTING-DBUS1 @@ -14,11 +14,11 @@ GVariant compatible marshaler to your library first. After you have done that: here's the basic principle how kdbus works: -You connect to a bus by opening its bus node in /dev/kdbus/. All +You connect to a bus by opening its bus node in /sys/fs/kdbus/. All buses have a device node there, it starts with a numeric UID of the owner of the bus, followed by a dash and a string identifying the -bus. The system bus is thus called /dev/kdbus/0-system, and for user -buses the device node is /dev/kdbus/1000-user (if 1000 is your user +bus. The system bus is thus called /sys/fs/kdbus/0-system, and for user +buses the device node is /sys/fs/kdbus/1000-user (if 1000 is your user id). (Before we proceed, please always keep a copy of libsystemd next @@ -496,12 +496,12 @@ parameter. Client libraries should use the following connection string when connecting to the system bus: - kernel:path=/dev/kdbus/0-system/bus;unix:path=/var/run/dbus/system_bus_socket + kernel:path=/sys/fs/kdbus/0-system/bus;unix:path=/var/run/dbus/system_bus_socket This will ensure that kdbus is preferred over the legacy AF_UNIX socket, but compatibility is kept. For the user bus use: - kernel:path=/dev/kdbus/$UID-user/bus;unix:path=$XDG_RUNTIME_DIR/bus + kernel:path=/sys/fs/kdbus/$UID-user/bus;unix:path=$XDG_RUNTIME_DIR/bus With $UID replaced by the callers numer user ID, and $XDG_RUNTIME_DIR following the XDG basedir spec. diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c new file mode 100644 index 0000000000..3dc00b5e4a --- /dev/null +++ b/src/libsystemd/sd-bus/bus-common-errors.c @@ -0,0 +1,76 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2014 Zbigniew Jędrzejewski-Szmek + + 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 <errno.h> + +#include "sd-bus.h" +#include "bus-error.h" +#include "bus-common-errors.h" + +BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = { + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_UNIT, ENOENT), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_UNIT_FOR_PID, ESRCH), + SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_EXISTS, EEXIST), + SD_BUS_ERROR_MAP(BUS_ERROR_LOAD_FAILED, EIO), + SD_BUS_ERROR_MAP(BUS_ERROR_JOB_FAILED, EREMOTEIO), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_JOB, ENOENT), + SD_BUS_ERROR_MAP(BUS_ERROR_NOT_SUBSCRIBED, EINVAL), + SD_BUS_ERROR_MAP(BUS_ERROR_ALREADY_SUBSCRIBED, EINVAL), + SD_BUS_ERROR_MAP(BUS_ERROR_ONLY_BY_DEPENDENCY, EINVAL), + SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_JOBS_CONFLICTING, EDEADLOCK), + SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, EDEADLOCK), + SD_BUS_ERROR_MAP(BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, EDEADLOCK), + SD_BUS_ERROR_MAP(BUS_ERROR_UNIT_MASKED, ENOSYS), + SD_BUS_ERROR_MAP(BUS_ERROR_JOB_TYPE_NOT_APPLICABLE, EBADR), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_ISOLATION, EPERM), + SD_BUS_ERROR_MAP(BUS_ERROR_SHUTTING_DOWN, ECANCELED), + SD_BUS_ERROR_MAP(BUS_ERROR_SCOPE_NOT_RUNNING, EHOSTDOWN), + + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_MACHINE, ENXIO), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_MACHINE_FOR_PID, ENXIO), + SD_BUS_ERROR_MAP(BUS_ERROR_MACHINE_EXISTS, EEXIST), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_PRIVATE_NETWORKING, ENOSYS), + + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_SESSION, ENXIO), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SESSION_FOR_PID, ENXIO), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_USER, ENXIO), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_USER_FOR_PID, ENXIO), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_SEAT, ENXIO), + SD_BUS_ERROR_MAP(BUS_ERROR_SESSION_NOT_ON_SEAT, EINVAL), + SD_BUS_ERROR_MAP(BUS_ERROR_NOT_IN_CONTROL, EINVAL), + SD_BUS_ERROR_MAP(BUS_ERROR_DEVICE_IS_TAKEN, EINVAL), + SD_BUS_ERROR_MAP(BUS_ERROR_DEVICE_NOT_TAKEN, EINVAL), + SD_BUS_ERROR_MAP(BUS_ERROR_OPERATION_IN_PROGRESS, EINPROGRESS), + SD_BUS_ERROR_MAP(BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, ENOSYS), + + SD_BUS_ERROR_MAP(BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, EALREADY), + + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_PROCESS, ESRCH), + + SD_BUS_ERROR_MAP(BUS_ERROR_NO_NAME_SERVERS, EIO), + SD_BUS_ERROR_MAP(BUS_ERROR_INVALID_REPLY, EINVAL), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_SUCH_RR, ENOENT), + SD_BUS_ERROR_MAP(BUS_ERROR_NO_RESOURCES, ENOMEM), + SD_BUS_ERROR_MAP(BUS_ERROR_CNAME_LOOP, EDEADLOCK), + SD_BUS_ERROR_MAP(BUS_ERROR_ABORTED, ECANCELED), + + SD_BUS_ERROR_MAP_END +}; diff --git a/src/shared/bus-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h index 504ab1f796..5b7f41ef19 100644 --- a/src/shared/bus-errors.h +++ b/src/libsystemd/sd-bus/bus-common-errors.h @@ -21,6 +21,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include "sd-bus.h" +#include "bus-error.h" + #define BUS_ERROR_NO_SUCH_UNIT "org.freedesktop.systemd1.NoSuchUnit" #define BUS_ERROR_NO_UNIT_FOR_PID "org.freedesktop.systemd1.NoUnitForPID" #define BUS_ERROR_UNIT_EXISTS "org.freedesktop.systemd1.UnitExists" @@ -67,3 +70,5 @@ #define BUS_ERROR_CNAME_LOOP "org.freedesktop.resolve1.CNameLoop" #define BUS_ERROR_ABORTED "org.freedesktop.resolve1.Aborted" #define _BUS_ERROR_DNS "org.freedesktop.resolve1.DnsError." + +BUS_ERROR_MAP_ELF_USE(bus_common_errors); diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c index 5a052d455e..813c97f650 100644 --- a/src/libsystemd/sd-bus/bus-control.c +++ b/src/libsystemd/sd-bus/bus-control.c @@ -62,7 +62,7 @@ static int bus_request_name_kernel(sd_bus *bus, const char *name, uint64_t flags size = offsetof(struct kdbus_cmd_name, items) + KDBUS_ITEM_SIZE(l); n = alloca0_align(size, 8); n->size = size; - kdbus_translate_request_name_flags(flags, (uint64_t *) &n->flags); + n->flags = request_name_flags_to_kdbus(flags); n->items[0].size = KDBUS_ITEM_HEADER_SIZE + l; n->items[0].type = KDBUS_ITEM_NAME; @@ -223,23 +223,6 @@ _public_ int sd_bus_release_name(sd_bus *bus, const char *name) { return bus_release_name_dbus1(bus, name); } -static int kernel_cmd_free(sd_bus *bus, uint64_t offset) -{ - struct kdbus_cmd_free cmd; - int r; - - assert(bus); - - cmd.flags = 0; - cmd.offset = offset; - - r = ioctl(bus->input_fd, KDBUS_CMD_FREE, &cmd); - if (r < 0) - return -errno; - - return 0; -} - static int kernel_get_list(sd_bus *bus, uint64_t flags, char ***x) { struct kdbus_cmd_name_list cmd = {}; struct kdbus_name_list *name_list; @@ -278,8 +261,8 @@ static int kernel_get_list(sd_bus *bus, uint64_t flags, char ***x) { } KDBUS_ITEM_FOREACH(item, name, items) - if (item->type == KDBUS_ITEM_NAME) - entry_name = item->str; + if (item->type == KDBUS_ITEM_OWNED_NAME) + entry_name = item->name.name; if (entry_name && service_name_is_valid(entry_name)) { r = strv_extend(x, entry_name); @@ -293,7 +276,7 @@ static int kernel_get_list(sd_bus *bus, uint64_t flags, char ***x) { r = 0; fail: - kernel_cmd_free(bus, cmd.offset); + bus_kernel_cmd_free(bus, cmd.offset); return r; } @@ -392,46 +375,87 @@ _public_ int sd_bus_list_names(sd_bus *bus, char ***acquired, char ***activatabl return bus_list_names_dbus1(bus, acquired, activatable); } -static int bus_populate_creds_from_items(sd_bus *bus, - struct kdbus_info *info, - uint64_t mask, - sd_bus_creds *c) { +static int bus_populate_creds_from_items( + sd_bus *bus, + struct kdbus_info *info, + uint64_t mask, + sd_bus_creds *c) { struct kdbus_item *item; uint64_t m; int r; + assert(bus); + assert(info); + assert(c); + KDBUS_ITEM_FOREACH(item, info, items) { switch (item->type) { + case KDBUS_ITEM_PIDS: + + if (mask & SD_BUS_CREDS_PID && item->pids.pid > 0) { + c->pid = (pid_t) item->pids.pid; + c->mask |= SD_BUS_CREDS_PID; + } + + if (mask & SD_BUS_CREDS_TID && item->pids.tid > 0) { + c->tid = (pid_t) item->pids.tid; + c->mask |= SD_BUS_CREDS_TID; + } + + break; + case KDBUS_ITEM_CREDS: - m = (SD_BUS_CREDS_UID | SD_BUS_CREDS_GID | SD_BUS_CREDS_PID) & mask; - if (m) { + if (mask & SD_BUS_CREDS_UID && (uid_t) item->creds.uid != UID_INVALID) { c->uid = (uid_t) item->creds.uid; - c->pid = (pid_t) item->creds.pid; + c->mask |= SD_BUS_CREDS_UID; + } + + if (mask & SD_BUS_CREDS_EUID && (uid_t) item->creds.euid != UID_INVALID) { + c->euid = (uid_t) item->creds.euid; + c->mask |= SD_BUS_CREDS_EUID; + } + + if (mask & SD_BUS_CREDS_SUID && (uid_t) item->creds.suid != UID_INVALID) { + c->suid = (uid_t) item->creds.suid; + c->mask |= SD_BUS_CREDS_SUID; + } + + if (mask & SD_BUS_CREDS_FSUID && (uid_t) item->creds.fsuid != UID_INVALID) { + c->fsuid = (uid_t) item->creds.fsuid; + c->mask |= SD_BUS_CREDS_FSUID; + } + + if (mask & SD_BUS_CREDS_GID && (gid_t) item->creds.gid != GID_INVALID) { c->gid = (gid_t) item->creds.gid; - c->mask |= m; + c->mask |= SD_BUS_CREDS_GID; } - if (mask & SD_BUS_CREDS_TID && item->creds.tid > 0) { - c->tid = (pid_t) item->creds.tid; - c->mask |= SD_BUS_CREDS_TID; + if (mask & SD_BUS_CREDS_EGID && (gid_t) item->creds.egid != GID_INVALID) { + c->egid = (gid_t) item->creds.egid; + c->mask |= SD_BUS_CREDS_EGID; + } + + if (mask & SD_BUS_CREDS_SGID && (gid_t) item->creds.sgid != GID_INVALID) { + c->sgid = (gid_t) item->creds.sgid; + c->mask |= SD_BUS_CREDS_SGID; } - if (mask & SD_BUS_CREDS_PID_STARTTIME && item->creds.starttime > 0) { - c->pid_starttime = item->creds.starttime; - c->mask |= SD_BUS_CREDS_PID_STARTTIME; + if (mask & SD_BUS_CREDS_FSGID && (gid_t) item->creds.fsgid != GID_INVALID) { + c->fsgid = (gid_t) item->creds.fsgid; + c->mask |= SD_BUS_CREDS_FSGID; } break; case KDBUS_ITEM_PID_COMM: if (mask & SD_BUS_CREDS_COMM) { - c->comm = strdup(item->str); - if (!c->comm) - return -ENOMEM; + r = free_and_strdup(&c->comm, item->str); + if (r < 0) + return r; c->mask |= SD_BUS_CREDS_COMM; } @@ -439,9 +463,9 @@ static int bus_populate_creds_from_items(sd_bus *bus, case KDBUS_ITEM_TID_COMM: if (mask & SD_BUS_CREDS_TID_COMM) { - c->tid_comm = strdup(item->str); - if (!c->tid_comm) - return -ENOMEM; + r = free_and_strdup(&c->tid_comm, item->str); + if (r < 0) + return r; c->mask |= SD_BUS_CREDS_TID_COMM; } @@ -449,9 +473,9 @@ static int bus_populate_creds_from_items(sd_bus *bus, case KDBUS_ITEM_EXE: if (mask & SD_BUS_CREDS_EXE) { - c->exe = strdup(item->str); - if (!c->exe) - return -ENOMEM; + r = free_and_strdup(&c->exe, item->str); + if (r < 0) + return r; c->mask |= SD_BUS_CREDS_EXE; } @@ -459,7 +483,7 @@ static int bus_populate_creds_from_items(sd_bus *bus, case KDBUS_ITEM_CMDLINE: if (mask & SD_BUS_CREDS_CMDLINE) { - c->cmdline_size = item->size - KDBUS_ITEM_HEADER_SIZE; + c->cmdline_size = item->size - offsetof(struct kdbus_item, data); c->cmdline = memdup(item->data, c->cmdline_size); if (!c->cmdline) return -ENOMEM; @@ -474,17 +498,17 @@ static int bus_populate_creds_from_items(sd_bus *bus, SD_BUS_CREDS_SESSION | SD_BUS_CREDS_OWNER_UID) & mask; if (m) { - c->cgroup = strdup(item->str); - if (!c->cgroup) - return -ENOMEM; + r = free_and_strdup(&c->cgroup, item->str); + if (r < 0) + return r; r = bus_get_root_path(bus); if (r < 0) return r; - c->cgroup_root = strdup(bus->cgroup_root); - if (!c->cgroup_root) - return -ENOMEM; + r = free_and_strdup(&c->cgroup_root, bus->cgroup_root); + if (r < 0) + return r; c->mask |= m; } @@ -506,25 +530,27 @@ static int bus_populate_creds_from_items(sd_bus *bus, case KDBUS_ITEM_SECLABEL: if (mask & SD_BUS_CREDS_SELINUX_CONTEXT) { - c->label = strdup(item->str); - if (!c->label) - return -ENOMEM; + r = free_and_strdup(&c->label, item->str); + if (r < 0) + return r; c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; } break; case KDBUS_ITEM_AUDIT: - m = (SD_BUS_CREDS_AUDIT_SESSION_ID | SD_BUS_CREDS_AUDIT_LOGIN_UID) & mask; + if (mask & SD_BUS_CREDS_AUDIT_SESSION_ID && (uint32_t) item->audit.sessionid != (uint32_t) -1) { + c->audit_session_id = (uint32_t) item->audit.sessionid; + c->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID; + } - if (m) { - c->audit_session_id = item->audit.sessionid; - c->audit_login_uid = item->audit.loginuid; - c->mask |= m; + if (mask & SD_BUS_CREDS_AUDIT_LOGIN_UID && (uid_t) item->audit.loginuid != UID_INVALID) { + c->audit_login_uid = (uid_t) item->audit.loginuid; + c->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID; } break; - case KDBUS_ITEM_NAME: + case KDBUS_ITEM_OWNED_NAME: if ((mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) && service_name_is_valid(item->name.name)) { r = strv_extend(&c->well_known_names, item->name.name); if (r < 0) @@ -534,13 +560,33 @@ static int bus_populate_creds_from_items(sd_bus *bus, } break; - case KDBUS_ITEM_CONN_NAME: - if ((mask & SD_BUS_CREDS_CONNECTION_NAME)) { - c->conn_name = strdup(item->str); - if (!c->conn_name) + case KDBUS_ITEM_CONN_DESCRIPTION: + if (mask & SD_BUS_CREDS_DESCRIPTION) { + r = free_and_strdup(&c->description, item->str); + if (r < 0) + return r; + + c->mask |= SD_BUS_CREDS_DESCRIPTION; + } + break; + + case KDBUS_ITEM_AUXGROUPS: + if (mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) { + size_t n; + uid_t *g; + + assert_cc(sizeof(gid_t) == sizeof(uint32_t)); + + n = (item->size - offsetof(struct kdbus_item, data32)) / sizeof(uint32_t); + g = newdup(gid_t, item->data32, n); + if (!g) return -ENOMEM; - c->mask |= SD_BUS_CREDS_CONNECTION_NAME; + free(c->supplementary_gids); + c->supplementary_gids = g; + c->n_supplementary_gids = n; + + c->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; } break; } @@ -549,10 +595,11 @@ static int bus_populate_creds_from_items(sd_bus *bus, return 0; } -static int bus_get_name_creds_kdbus( +int bus_get_name_creds_kdbus( sd_bus *bus, const char *name, uint64_t mask, + bool allow_activator, sd_bus_creds **creds) { _cleanup_bus_creds_unref_ sd_bus_creds *c = NULL; @@ -579,7 +626,20 @@ static int bus_get_name_creds_kdbus( } cmd->size = size; - kdbus_translate_attach_flags(mask, (uint64_t*) &cmd->flags); + cmd->flags = attach_flags_to_kdbus(mask); + + /* If augmentation is on, and the bus doesn't didn't allow us + * to get the bits we want, then ask for the PID/TID so that we + * can read the rest from /proc. */ + if ((mask & SD_BUS_CREDS_AUGMENT) && + (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| + SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| + SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE| + SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID| + SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS| + SD_BUS_CREDS_SELINUX_CONTEXT| + SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID))) + cmd->flags |= KDBUS_ATTACH_PIDS; r = ioctl(bus->input_fd, KDBUS_CMD_CONN_INFO, cmd); if (r < 0) @@ -588,7 +648,7 @@ static int bus_get_name_creds_kdbus( conn_info = (struct kdbus_info *) ((uint8_t *) bus->kdbus_buffer + cmd->offset); /* Non-activated names are considered not available */ - if (conn_info->flags & KDBUS_HELLO_ACTIVATOR) { + if (!allow_activator && (conn_info->flags & KDBUS_HELLO_ACTIVATOR)) { if (name[0] == ':') r = -ENXIO; else @@ -611,10 +671,21 @@ static int bus_get_name_creds_kdbus( c->mask |= SD_BUS_CREDS_UNIQUE_NAME; } + /* If KDBUS_ITEM_OWNED_NAME is requested then we'll get 0 of + them in case the service has no names. This does not mean + however that the list of owned names could not be + acquired. Hence, let's explicitly clarify that the data is + complete. */ + c->mask |= mask & SD_BUS_CREDS_WELL_KNOWN_NAMES; + r = bus_populate_creds_from_items(bus, conn_info, mask, c); if (r < 0) goto fail; + r = bus_creds_add_more(c, mask, 0, 0); + if (r < 0) + goto fail; + if (creds) { *creds = c; c = NULL; @@ -623,7 +694,7 @@ static int bus_get_name_creds_kdbus( r = 0; fail: - kernel_cmd_free(bus, cmd->offset); + bus_kernel_cmd_free(bus, cmd->offset); return r; } @@ -673,11 +744,16 @@ static int bus_get_name_creds_dbus1( c->mask |= SD_BUS_CREDS_UNIQUE_NAME; } - if (mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_PID_STARTTIME|SD_BUS_CREDS_GID| - SD_BUS_CREDS_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE| - SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID| - SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS| - SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID)) { + if ((mask & SD_BUS_CREDS_PID) || + ((mask & SD_BUS_CREDS_AUGMENT) && + (mask & (SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| + SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| + SD_BUS_CREDS_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE| + SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID| + SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS| + SD_BUS_CREDS_SELINUX_CONTEXT| + SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID)))) { + uint32_t u; r = sd_bus_call_method( @@ -733,6 +809,7 @@ static int bus_get_name_creds_dbus1( } if (mask & SD_BUS_CREDS_SELINUX_CONTEXT) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; const void *p = NULL; size_t sz = 0; @@ -742,22 +819,24 @@ static int bus_get_name_creds_dbus1( "/org/freedesktop/DBus", "org.freedesktop.DBus", "GetConnectionSELinuxSecurityContext", - NULL, + &error, &reply, "s", unique ? unique : name); - if (r < 0) - return r; - - r = sd_bus_message_read_array(reply, 'y', &p, &sz); - if (r < 0) - return r; + if (r < 0) { + if (!sd_bus_error_has_name(&error, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown")) + return r; + } else { + r = sd_bus_message_read_array(reply, 'y', &p, &sz); + if (r < 0) + return r; - c->label = strndup(p, sz); - if (!c->label) - return -ENOMEM; + c->label = strndup(p, sz); + if (!c->label) + return -ENOMEM; - c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; + c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; + } } r = bus_creds_add_more(c, mask, pid, 0); @@ -781,7 +860,7 @@ _public_ int sd_bus_get_name_creds( assert_return(bus, -EINVAL); assert_return(name, -EINVAL); - assert_return(mask <= _SD_BUS_CREDS_ALL, -ENOTSUP); + assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -ENOTSUP); assert_return(mask == 0 || creds, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); assert_return(service_name_is_valid(name), -EINVAL); @@ -791,23 +870,63 @@ _public_ int sd_bus_get_name_creds( return -ENOTCONN; if (bus->is_kernel) - return bus_get_name_creds_kdbus(bus, name, mask, creds); + return bus_get_name_creds_kdbus(bus, name, mask, false, creds); else return bus_get_name_creds_dbus1(bus, name, mask, creds); } -_public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) { +static int bus_get_owner_creds_kdbus(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) { _cleanup_bus_creds_unref_ sd_bus_creds *c = NULL; + struct kdbus_cmd_info cmd = { + .size = sizeof(struct kdbus_cmd_info) + }; + struct kdbus_info *creator_info; pid_t pid = 0; int r; - assert_return(bus, -EINVAL); - assert_return(mask <= _SD_BUS_CREDS_ALL, -ENOTSUP); - assert_return(ret, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); + c = bus_creds_new(); + if (!c) + return -ENOMEM; - if (!BUS_IS_OPEN(bus->state)) - return -ENOTCONN; + cmd.flags = attach_flags_to_kdbus(mask); + + /* If augmentation is on, and the bus doesn't didn't allow us + * to get the bits we want, then ask for the PID/TID so that we + * can read the rest from /proc. */ + if ((mask & SD_BUS_CREDS_AUGMENT) && + (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| + SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| + SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM|SD_BUS_CREDS_EXE|SD_BUS_CREDS_CMDLINE| + SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID| + SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS| + SD_BUS_CREDS_SELINUX_CONTEXT| + SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID))) + cmd.flags |= KDBUS_ATTACH_PIDS; + + r = ioctl(bus->input_fd, KDBUS_CMD_BUS_CREATOR_INFO, &cmd); + if (r < 0) + return -errno; + + creator_info = (struct kdbus_info *) ((uint8_t *) bus->kdbus_buffer + cmd.offset); + + r = bus_populate_creds_from_items(bus, creator_info, mask, c); + bus_kernel_cmd_free(bus, cmd.offset); + if (r < 0) + return r; + + r = bus_creds_add_more(c, mask, pid, 0); + if (r < 0) + return r; + + *ret = c; + c = NULL; + return 0; +} + +static int bus_get_owner_creds_dbus1(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) { + _cleanup_bus_creds_unref_ sd_bus_creds *c = NULL; + pid_t pid = 0; + int r; if (!bus->ucred_valid && !isempty(bus->label)) return -ENODATA; @@ -817,11 +936,20 @@ _public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **r return -ENOMEM; if (bus->ucred_valid) { - pid = c->pid = bus->ucred.pid; - c->uid = bus->ucred.uid; - c->gid = bus->ucred.gid; + if (bus->ucred.pid > 0) { + pid = c->pid = bus->ucred.pid; + c->mask |= SD_BUS_CREDS_PID & mask; + } + + if (bus->ucred.uid != UID_INVALID) { + c->uid = bus->ucred.uid; + c->mask |= SD_BUS_CREDS_UID & mask; + } - c->mask |= (SD_BUS_CREDS_UID | SD_BUS_CREDS_PID | SD_BUS_CREDS_GID) & mask; + if (bus->ucred.gid != GID_INVALID) { + c->gid = bus->ucred.gid; + c->mask |= SD_BUS_CREDS_GID & mask; + } } if (!isempty(bus->label) && (mask & SD_BUS_CREDS_SELINUX_CONTEXT)) { @@ -832,33 +960,30 @@ _public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **r c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; } - if (bus->is_kernel) { - struct kdbus_cmd_info cmd = {}; - struct kdbus_info *creator_info; - - cmd.size = sizeof(cmd); - r = ioctl(bus->input_fd, KDBUS_CMD_BUS_CREATOR_INFO, &cmd); - if (r < 0) - return -errno; - - creator_info = (struct kdbus_info *) ((uint8_t *) bus->kdbus_buffer + cmd.offset); - - r = bus_populate_creds_from_items(bus, creator_info, mask, c); - kernel_cmd_free(bus, cmd.offset); - - if (r < 0) - return r; - } else { - r = bus_creds_add_more(c, mask, pid, 0); - if (r < 0) - return r; - } + r = bus_creds_add_more(c, mask, pid, 0); + if (r < 0) + return r; *ret = c; c = NULL; return 0; } +_public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **ret) { + assert_return(bus, -EINVAL); + assert_return((mask & ~SD_BUS_CREDS_AUGMENT) <= _SD_BUS_CREDS_ALL, -ENOTSUP); + assert_return(ret, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (!BUS_IS_OPEN(bus->state)) + return -ENOTCONN; + + if (bus->is_kernel) + return bus_get_owner_creds_kdbus(bus, mask, ret); + else + return bus_get_owner_creds_dbus1(bus, mask, ret); +} + static int add_name_change_match(sd_bus *bus, uint64_t cookie, const char *name, @@ -1049,6 +1174,10 @@ int bus_add_match_internal_kernel( assert(bus); + /* Monitor streams don't support matches, make this a NOP */ + if (bus->hello_flags & KDBUS_HELLO_MONITOR) + return 0; + bloom = alloca0(bus->bloom_size); sz = ALIGN8(offsetof(struct kdbus_cmd_match, items)); @@ -1261,6 +1390,10 @@ int bus_remove_match_internal_kernel( assert(bus); + /* Monitor streams don't support matches, make this a NOP */ + if (bus->hello_flags & KDBUS_HELLO_MONITOR) + return 0; + zero(m); m.size = offsetof(struct kdbus_cmd_match, items); m.cookie = cookie; diff --git a/src/libsystemd/sd-bus/bus-control.h b/src/libsystemd/sd-bus/bus-control.h index aa290edac7..5009ca8e61 100644 --- a/src/libsystemd/sd-bus/bus-control.h +++ b/src/libsystemd/sd-bus/bus-control.h @@ -29,3 +29,5 @@ int bus_remove_match_internal(sd_bus *bus, const char *match, uint64_t cookie); int bus_add_match_internal_kernel(sd_bus *bus, struct bus_match_component *components, unsigned n_components, uint64_t cookie); int bus_remove_match_internal_kernel(sd_bus *bus, uint64_t cookie); + +int bus_get_name_creds_kdbus(sd_bus *bus, const char *name, uint64_t mask, bool allow_activator, sd_bus_creds **creds); diff --git a/src/libsystemd/sd-bus/bus-convenience.c b/src/libsystemd/sd-bus/bus-convenience.c index 8081a2f9c0..ae0f4fa217 100644 --- a/src/libsystemd/sd-bus/bus-convenience.c +++ b/src/libsystemd/sd-bus/bus-convenience.c @@ -476,6 +476,7 @@ _public_ int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_b _public_ int sd_bus_query_sender_privilege(sd_bus_message *call, int capability) { _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL; uid_t our_uid; + bool know_caps = false; int r; assert_return(call, -EINVAL); @@ -486,21 +487,21 @@ _public_ int sd_bus_query_sender_privilege(sd_bus_message *call, int capability) if (!BUS_IS_OPEN(call->bus->state)) return -ENOTCONN; - /* We only trust the effective capability set if this is - * kdbus. On classic dbus1 we cannot retrieve the value - * without races. Since this function is supposed to be useful - * for authentication decision we hence avoid requesting and - * using that information. */ - if (call->bus->is_kernel && capability >= 0) { - r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EFFECTIVE_CAPS, &creds); + if (capability >= 0) { + r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS, &creds); if (r < 0) return r; + /* Note that not even on kdbus we might have the caps + * field, due to faked identities, or namespace + * translation issues. */ r = sd_bus_creds_has_effective_cap(creds, capability); if (r > 0) return 1; + if (r == 0) + know_caps = true; } else { - r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID, &creds); + r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID, &creds); if (r < 0) return r; } @@ -508,10 +509,14 @@ _public_ int sd_bus_query_sender_privilege(sd_bus_message *call, int capability) /* Now, check the UID, but only if the capability check wasn't * sufficient */ our_uid = getuid(); - if (our_uid != 0 || !call->bus->is_kernel || capability < 0) { + if (our_uid != 0 || !know_caps || capability < 0) { uid_t sender_uid; - r = sd_bus_creds_get_uid(creds, &sender_uid); + /* Try to use the EUID, if we have it. */ + r = sd_bus_creds_get_euid(creds, &sender_uid); + if (r < 0) + r = sd_bus_creds_get_uid(creds, &sender_uid); + if (r >= 0) { /* Sender has same UID as us, then let's grant access */ if (sender_uid == our_uid) diff --git a/src/libsystemd/sd-bus/bus-creds.c b/src/libsystemd/sd-bus/bus-creds.c index 26c25452bb..9978ddfa38 100644 --- a/src/libsystemd/sd-bus/bus-creds.c +++ b/src/libsystemd/sd-bus/bus-creds.c @@ -49,10 +49,16 @@ void bus_creds_done(sd_bus_creds *c) { free(c->unit); free(c->user_unit); free(c->slice); - free(c->unescaped_conn_name); + free(c->unescaped_description); + + free(c->well_known_names); /* note that this is an strv, but + * we only free the array, not the + * strings the array points to. The + * full strv we only free if + * c->allocated is set, see + * below. */ strv_free(c->cmdline_array); - strv_free(c->well_known_names); } _public_ sd_bus_creds *sd_bus_creds_ref(sd_bus_creds *c) { @@ -83,8 +89,6 @@ _public_ sd_bus_creds *sd_bus_creds_unref(sd_bus_creds *c) { c->n_ref--; if (c->n_ref == 0) { - bus_creds_done(c); - free(c->comm); free(c->tid_comm); free(c->exe); @@ -94,7 +98,14 @@ _public_ sd_bus_creds *sd_bus_creds_unref(sd_bus_creds *c) { free(c->label); free(c->unique_name); free(c->cgroup_root); - free(c->conn_name); + free(c->description); + free(c->supplementary_gids); + + strv_free(c->well_known_names); + c->well_known_names = NULL; + + bus_creds_done(c); + free(c); } } else { @@ -141,7 +152,7 @@ _public_ int sd_bus_creds_new_from_pid(sd_bus_creds **ret, pid_t pid, uint64_t m if (!c) return -ENOMEM; - r = bus_creds_add_more(c, mask, pid, 0); + r = bus_creds_add_more(c, mask | SD_BUS_CREDS_AUGMENT, pid, 0); if (r < 0) { sd_bus_creds_unref(c); return r; @@ -169,6 +180,40 @@ _public_ int sd_bus_creds_get_uid(sd_bus_creds *c, uid_t *uid) { return 0; } +_public_ int sd_bus_creds_get_euid(sd_bus_creds *c, uid_t *euid) { + assert_return(c, -EINVAL); + assert_return(euid, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_EUID)) + return -ENODATA; + + *euid = c->euid; + return 0; +} + +_public_ int sd_bus_creds_get_suid(sd_bus_creds *c, uid_t *suid) { + assert_return(c, -EINVAL); + assert_return(suid, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_SUID)) + return -ENODATA; + + *suid = c->suid; + return 0; +} + + +_public_ int sd_bus_creds_get_fsuid(sd_bus_creds *c, uid_t *fsuid) { + assert_return(c, -EINVAL); + assert_return(fsuid, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_FSUID)) + return -ENODATA; + + *fsuid = c->fsuid; + return 0; +} + _public_ int sd_bus_creds_get_gid(sd_bus_creds *c, gid_t *gid) { assert_return(c, -EINVAL); assert_return(gid, -EINVAL); @@ -180,6 +225,51 @@ _public_ int sd_bus_creds_get_gid(sd_bus_creds *c, gid_t *gid) { return 0; } + +_public_ int sd_bus_creds_get_egid(sd_bus_creds *c, gid_t *egid) { + assert_return(c, -EINVAL); + assert_return(egid, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_EGID)) + return -ENODATA; + + *egid = c->egid; + return 0; +} + +_public_ int sd_bus_creds_get_sgid(sd_bus_creds *c, gid_t *sgid) { + assert_return(c, -EINVAL); + assert_return(sgid, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_SGID)) + return -ENODATA; + + *sgid = c->sgid; + return 0; +} + +_public_ int sd_bus_creds_get_fsgid(sd_bus_creds *c, gid_t *fsgid) { + assert_return(c, -EINVAL); + assert_return(fsgid, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_FSGID)) + return -ENODATA; + + *fsgid = c->fsgid; + return 0; +} + +_public_ int sd_bus_creds_get_supplementary_gids(sd_bus_creds *c, const gid_t **gids) { + assert_return(c, -EINVAL); + assert_return(gids, -EINVAL); + + if (!(c->mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS)) + return -ENODATA; + + *gids = c->supplementary_gids; + return (int) c->n_supplementary_gids; +} + _public_ int sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid) { assert_return(c, -EINVAL); assert_return(pid, -EINVAL); @@ -204,18 +294,6 @@ _public_ int sd_bus_creds_get_tid(sd_bus_creds *c, pid_t *tid) { return 0; } -_public_ int sd_bus_creds_get_pid_starttime(sd_bus_creds *c, uint64_t *usec) { - assert_return(c, -EINVAL); - assert_return(usec, -EINVAL); - - if (!(c->mask & SD_BUS_CREDS_PID_STARTTIME)) - return -ENODATA; - - assert(c->pid_starttime > 0); - *usec = c->pid_starttime; - return 0; -} - _public_ int sd_bus_creds_get_selinux_context(sd_bus_creds *c, const char **ret) { assert_return(c, -EINVAL); @@ -461,26 +539,48 @@ _public_ int sd_bus_creds_get_well_known_names(sd_bus_creds *c, char ***well_kno if (!(c->mask & SD_BUS_CREDS_WELL_KNOWN_NAMES)) return -ENODATA; + /* As a special hack we return the bus driver as well-known + * names list when this is requested. */ + if (c->well_known_names_driver) { + static const char* const wkn[] = { + "org.freedesktop.DBus", + NULL + }; + + *well_known_names = (char**) wkn; + return 0; + } + + if (c->well_known_names_local) { + static const char* const wkn[] = { + "org.freedesktop.DBus.Local", + NULL + }; + + *well_known_names = (char**) wkn; + return 0; + } + *well_known_names = c->well_known_names; return 0; } -_public_ int sd_bus_creds_get_connection_name(sd_bus_creds *c, const char **ret) { +_public_ int sd_bus_creds_get_description(sd_bus_creds *c, const char **ret) { assert_return(c, -EINVAL); assert_return(ret, -EINVAL); - if (!(c->mask & SD_BUS_CREDS_CONNECTION_NAME)) + if (!(c->mask & SD_BUS_CREDS_DESCRIPTION)) return -ENODATA; - assert(c->conn_name); + assert(c->description); - if (!c->unescaped_conn_name) { - c->unescaped_conn_name = bus_label_unescape(c->conn_name); - if (!c->unescaped_conn_name) + if (!c->unescaped_description) { + c->unescaped_description = bus_label_unescape(c->description); + if (!c->unescaped_description) return -ENOMEM; } - *ret = c->unescaped_conn_name; + *ret = c->unescaped_description; return 0; } @@ -581,6 +681,9 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { assert(c); assert(c->allocated); + if (!(mask & SD_BUS_CREDS_AUGMENT)) + return 0; + missing = mask & ~c->mask; if (missing == 0) return 0; @@ -596,139 +699,182 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { if (pid <= 0) return 0; - if (missing & (SD_BUS_CREDS_UID | SD_BUS_CREDS_GID | + if (pid > 0) { + c->pid = pid; + c->mask |= SD_BUS_CREDS_PID; + } + + if (tid > 0) { + c->tid = tid; + c->mask |= SD_BUS_CREDS_TID; + } + + if (missing & (SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_SUID | SD_BUS_CREDS_FSUID | + SD_BUS_CREDS_GID | SD_BUS_CREDS_EGID | SD_BUS_CREDS_SGID | SD_BUS_CREDS_FSGID | + SD_BUS_CREDS_SUPPLEMENTARY_GIDS | SD_BUS_CREDS_EFFECTIVE_CAPS | SD_BUS_CREDS_INHERITABLE_CAPS | SD_BUS_CREDS_PERMITTED_CAPS | SD_BUS_CREDS_BOUNDING_CAPS)) { _cleanup_fclose_ FILE *f = NULL; - char line[LINE_MAX]; const char *p; p = procfs_file_alloca(pid, "status"); f = fopen(p, "re"); - if (!f) - return errno == ENOENT ? -ESRCH : -errno; + if (!f) { + if (errno == ENOENT) + return -ESRCH; + else if (errno != EPERM && errno != EACCES) + return -errno; + } else { + char line[LINE_MAX]; + + FOREACH_LINE(line, f, return -errno) { + truncate_nl(line); + + if (missing & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID)) { + p = startswith(line, "Uid:"); + if (p) { + unsigned long uid, euid, suid, fsuid; + + p += strspn(p, WHITESPACE); + if (sscanf(p, "%lu %lu %lu %lu", &uid, &euid, &suid, &fsuid) != 4) + return -EIO; + + c->uid = (uid_t) uid; + c->euid = (uid_t) euid; + c->suid = (uid_t) suid; + c->fsuid = (uid_t) fsuid; + c->mask |= missing & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID); + continue; + } + } - FOREACH_LINE(line, f, return -errno) { - truncate_nl(line); + if (missing & (SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID)) { + p = startswith(line, "Gid:"); + if (p) { + unsigned long gid, egid, sgid, fsgid; + + p += strspn(p, WHITESPACE); + if (sscanf(p, "%lu %lu %lu %lu", &gid, &egid, &sgid, &fsgid) != 4) + return -EIO; + + c->gid = (gid_t) gid; + c->egid = (gid_t) egid; + c->sgid = (gid_t) sgid; + c->fsgid = (gid_t) fsgid; + c->mask |= missing & (SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID); + continue; + } + } - if (missing & SD_BUS_CREDS_UID) { - p = startswith(line, "Uid:"); - if (p) { - unsigned long uid; + if (missing & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) { + p = startswith(line, "Groups:"); + if (p) { + size_t allocated = 0; - p += strspn(p, WHITESPACE); - if (sscanf(p, "%lu", &uid) != 1) - return -EIO; + for (;;) { + unsigned long g; + int n = 0; - c->uid = (uid_t) uid; - c->mask |= SD_BUS_CREDS_UID; - continue; - } - } + p += strspn(p, WHITESPACE); + if (*p == 0) + break; - if (missing & SD_BUS_CREDS_GID) { - p = startswith(line, "Gid:"); - if (p) { - unsigned long gid; + if (sscanf(p, "%lu%n", &g, &n) != 1) + return -EIO; - p += strspn(p, WHITESPACE); - if (sscanf(p, "%lu", &gid) != 1) - return -EIO; + if (!GREEDY_REALLOC(c->supplementary_gids, allocated, c->n_supplementary_gids+1)) + return -ENOMEM; - c->gid = (uid_t) gid; - c->mask |= SD_BUS_CREDS_GID; - continue; + c->supplementary_gids[c->n_supplementary_gids++] = (gid_t) g; + p += n; + } + + c->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; + continue; + } } - } - if (missing & SD_BUS_CREDS_EFFECTIVE_CAPS) { - p = startswith(line, "CapEff:"); - if (p) { - r = parse_caps(c, CAP_OFFSET_EFFECTIVE, p); - if (r < 0) - return r; + if (missing & SD_BUS_CREDS_EFFECTIVE_CAPS) { + p = startswith(line, "CapEff:"); + if (p) { + r = parse_caps(c, CAP_OFFSET_EFFECTIVE, p); + if (r < 0) + return r; - c->mask |= SD_BUS_CREDS_EFFECTIVE_CAPS; - continue; + c->mask |= SD_BUS_CREDS_EFFECTIVE_CAPS; + continue; + } } - } - if (missing & SD_BUS_CREDS_PERMITTED_CAPS) { - p = startswith(line, "CapPrm:"); - if (p) { - r = parse_caps(c, CAP_OFFSET_PERMITTED, p); - if (r < 0) - return r; + if (missing & SD_BUS_CREDS_PERMITTED_CAPS) { + p = startswith(line, "CapPrm:"); + if (p) { + r = parse_caps(c, CAP_OFFSET_PERMITTED, p); + if (r < 0) + return r; - c->mask |= SD_BUS_CREDS_PERMITTED_CAPS; - continue; + c->mask |= SD_BUS_CREDS_PERMITTED_CAPS; + continue; + } } - } - if (missing & SD_BUS_CREDS_INHERITABLE_CAPS) { - p = startswith(line, "CapInh:"); - if (p) { - r = parse_caps(c, CAP_OFFSET_INHERITABLE, p); - if (r < 0) - return r; + if (missing & SD_BUS_CREDS_INHERITABLE_CAPS) { + p = startswith(line, "CapInh:"); + if (p) { + r = parse_caps(c, CAP_OFFSET_INHERITABLE, p); + if (r < 0) + return r; - c->mask |= SD_BUS_CREDS_INHERITABLE_CAPS; - continue; + c->mask |= SD_BUS_CREDS_INHERITABLE_CAPS; + continue; + } } - } - if (missing & SD_BUS_CREDS_BOUNDING_CAPS) { - p = startswith(line, "CapBnd:"); - if (p) { - r = parse_caps(c, CAP_OFFSET_BOUNDING, p); - if (r < 0) - return r; + if (missing & SD_BUS_CREDS_BOUNDING_CAPS) { + p = startswith(line, "CapBnd:"); + if (p) { + r = parse_caps(c, CAP_OFFSET_BOUNDING, p); + if (r < 0) + return r; - c->mask |= SD_BUS_CREDS_BOUNDING_CAPS; - continue; + c->mask |= SD_BUS_CREDS_BOUNDING_CAPS; + continue; + } } } } } - if (missing & (SD_BUS_CREDS_PID_STARTTIME)) { - unsigned long long st; - - r = get_starttime_of_pid(pid, &st); - if (r < 0) - return r; - - c->pid_starttime = ((usec_t) st * USEC_PER_SEC) / (usec_t) sysconf(_SC_CLK_TCK); - c->mask |= SD_BUS_CREDS_PID_STARTTIME; - } - if (missing & SD_BUS_CREDS_SELINUX_CONTEXT) { const char *p; p = procfs_file_alloca(pid, "attr/current"); r = read_one_line_file(p, &c->label); - if (r < 0 && r != -ENOENT && r != -EINVAL) - return r; - else if (r >= 0) + if (r < 0) { + if (r != -ENOENT && r != -EINVAL && r != -EPERM && r != -EACCES) + return r; + } else c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; } if (missing & SD_BUS_CREDS_COMM) { r = get_process_comm(pid, &c->comm); - if (r < 0) - return r; - - c->mask |= SD_BUS_CREDS_COMM; + if (r < 0) { + if (r != -EPERM && r != -EACCES) + return r; + } else + c->mask |= SD_BUS_CREDS_COMM; } if (missing & SD_BUS_CREDS_EXE) { r = get_process_exe(pid, &c->exe); - if (r < 0) - return r; - - c->mask |= SD_BUS_CREDS_EXE; + if (r < 0) { + if (r != -EPERM && r != -EACCES) + return r; + } else + c->mask |= SD_BUS_CREDS_EXE; } if (missing & SD_BUS_CREDS_CMDLINE) { @@ -736,14 +882,18 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { p = procfs_file_alloca(pid, "cmdline"); r = read_full_file(p, &c->cmdline, &c->cmdline_size); - if (r < 0) - return r; - - if (c->cmdline_size == 0) { - free(c->cmdline); - c->cmdline = NULL; - } else - c->mask |= SD_BUS_CREDS_CMDLINE; + if (r < 0) { + if (r == -ENOENT) + return -ESRCH; + if (r != -EPERM && r != -EACCES) + return r; + } else { + if (c->cmdline_size == 0) { + free(c->cmdline); + c->cmdline = NULL; + } else + c->mask |= SD_BUS_CREDS_CMDLINE; + } } if (tid > 0 && (missing & SD_BUS_CREDS_TID_COMM)) { @@ -753,38 +903,45 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) { return -ENOMEM; r = read_one_line_file(p, &c->tid_comm); - if (r < 0) - return r == -ENOENT ? -ESRCH : r; - - c->mask |= SD_BUS_CREDS_TID_COMM; + if (r < 0) { + if (r == -ENOENT) + return -ESRCH; + if (r != -EPERM && r != -EACCES) + return r; + } else + c->mask |= SD_BUS_CREDS_TID_COMM; } if (missing & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID)) { r = cg_pid_get_path(NULL, pid, &c->cgroup); - if (r < 0) - return r; - - r = cg_get_root_path(&c->cgroup_root); - if (r < 0) - return r; - - c->mask |= missing & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID); + if (r < 0) { + if (r != -EPERM && r != -EACCES) + return r; + } else { + r = cg_get_root_path(&c->cgroup_root); + if (r < 0) + return r; + + c->mask |= missing & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID); + } } if (missing & SD_BUS_CREDS_AUDIT_SESSION_ID) { r = audit_session_from_pid(pid, &c->audit_session_id); - if (r < 0 && r != -ENOTSUP && r != -ENXIO && r != -ENOENT) - return r; - else if (r >= 0) + if (r < 0) { + if (r != -ENOTSUP && r != -ENXIO && r != -ENOENT && r != -EPERM && r != -EACCES) + return r; + } else c->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID; } if (missing & SD_BUS_CREDS_AUDIT_LOGIN_UID) { r = audit_loginuid_from_pid(pid, &c->audit_login_uid); - if (r < 0 && r != -ENOTSUP && r != -ENXIO && r != -ENOENT) - return r; - else if (r >= 0) + if (r < 0) { + if (r != -ENOTSUP && r != -ENXIO && r != -ENOENT && r != -EPERM && r != -EACCES) + return r; + } else c->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID; } @@ -798,8 +955,9 @@ int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret) assert(c); assert(ret); - if ((mask & ~c->mask) == 0) { - /* There's already all data we need. */ + if ((mask & ~c->mask) == 0 || (!(mask & SD_BUS_CREDS_AUGMENT))) { + /* There's already all data we need, or augmentation + * wasn't turned on. */ *ret = sd_bus_creds_ref(c); return 0; @@ -816,11 +974,49 @@ int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret) n->mask |= SD_BUS_CREDS_UID; } + if (c->mask & mask & SD_BUS_CREDS_EUID) { + n->euid = c->euid; + n->mask |= SD_BUS_CREDS_EUID; + } + + if (c->mask & mask & SD_BUS_CREDS_SUID) { + n->suid = c->suid; + n->mask |= SD_BUS_CREDS_SUID; + } + + if (c->mask & mask & SD_BUS_CREDS_FSUID) { + n->fsuid = c->fsuid; + n->mask |= SD_BUS_CREDS_FSUID; + } + if (c->mask & mask & SD_BUS_CREDS_GID) { n->gid = c->gid; n->mask |= SD_BUS_CREDS_GID; } + if (c->mask & mask & SD_BUS_CREDS_EGID) { + n->egid = c->egid; + n->mask |= SD_BUS_CREDS_EGID; + } + + if (c->mask & mask & SD_BUS_CREDS_SGID) { + n->sgid = c->sgid; + n->mask |= SD_BUS_CREDS_SGID; + } + + if (c->mask & mask & SD_BUS_CREDS_FSGID) { + n->fsgid = c->fsgid; + n->mask |= SD_BUS_CREDS_FSGID; + } + + if (c->mask & mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) { + n->supplementary_gids = newdup(gid_t, c->supplementary_gids, c->n_supplementary_gids); + if (!n->supplementary_gids) + return -ENOMEM; + n->n_supplementary_gids = c->n_supplementary_gids; + n->mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; + } + if (c->mask & mask & SD_BUS_CREDS_PID) { n->pid = c->pid; n->mask |= SD_BUS_CREDS_PID; @@ -831,11 +1027,6 @@ int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret) n->mask |= SD_BUS_CREDS_TID; } - if (c->mask & mask & SD_BUS_CREDS_PID_STARTTIME) { - n->pid_starttime = c->pid_starttime; - n->mask |= SD_BUS_CREDS_PID_STARTTIME; - } - if (c->mask & mask & SD_BUS_CREDS_COMM) { n->comm = strdup(c->comm); if (!n->comm) @@ -890,11 +1081,17 @@ int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret) n->mask |= c->mask & mask & (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS); } + if (c->mask & mask & SD_BUS_CREDS_SELINUX_CONTEXT) { + n->label = strdup(c->label); + if (!n->label) + return -ENOMEM; + n->mask |= SD_BUS_CREDS_SELINUX_CONTEXT; + } + if (c->mask & mask & SD_BUS_CREDS_AUDIT_SESSION_ID) { n->audit_session_id = c->audit_session_id; n->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID; } - if (c->mask & mask & SD_BUS_CREDS_AUDIT_LOGIN_UID) { n->audit_login_uid = c->audit_login_uid; n->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID; @@ -904,12 +1101,21 @@ int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret) n->unique_name = strdup(c->unique_name); if (!n->unique_name) return -ENOMEM; + n->mask |= SD_BUS_CREDS_UNIQUE_NAME; } if (c->mask & mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) { n->well_known_names = strv_copy(c->well_known_names); if (!n->well_known_names) return -ENOMEM; + n->mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES; + } + + if (c->mask & mask & SD_BUS_CREDS_DESCRIPTION) { + n->description = strdup(c->description); + if (!n->description) + return -ENOMEM; + n->mask |= SD_BUS_CREDS_DESCRIPTION; } /* Get more data */ diff --git a/src/libsystemd/sd-bus/bus-creds.h b/src/libsystemd/sd-bus/bus-creds.h index 81b852a596..48453e2afd 100644 --- a/src/libsystemd/sd-bus/bus-creds.h +++ b/src/libsystemd/sd-bus/bus-creds.h @@ -32,9 +32,18 @@ struct sd_bus_creds { uint64_t mask; uid_t uid; + uid_t euid; + uid_t suid; + uid_t fsuid; gid_t gid; + gid_t egid; + gid_t sgid; + gid_t fsgid; + + gid_t *supplementary_gids; + unsigned n_supplementary_gids; + pid_t pid; - usec_t pid_starttime; pid_t tid; char *comm; @@ -62,10 +71,12 @@ struct sd_bus_creds { char *unique_name; char **well_known_names; + bool well_known_names_driver:1; + bool well_known_names_local:1; char *cgroup_root; - char *conn_name, *unescaped_conn_name; + char *description, *unescaped_description; }; sd_bus_creds* bus_creds_new(void); diff --git a/src/libsystemd/sd-bus/bus-dump.c b/src/libsystemd/sd-bus/bus-dump.c index 8b70b20e80..33d0ed2df6 100644 --- a/src/libsystemd/sd-bus/bus-dump.c +++ b/src/libsystemd/sd-bus/bus-dump.c @@ -23,27 +23,42 @@ #include "capability.h" #include "strv.h" #include "audit.h" +#include "macro.h" +#include "cap-list.h" #include "bus-message.h" #include "bus-internal.h" #include "bus-type.h" #include "bus-dump.h" -static char *indent(unsigned level) { +static char *indent(unsigned level, unsigned flags) { char *p; + unsigned n, i = 0; - p = new(char, 2 + level + 1); + n = 0; + + if (flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY && level > 0) + level -= 1; + + if (flags & BUS_MESSAGE_DUMP_WITH_HEADER) + n += 2; + + p = new(char, n + level*8 + 1); if (!p) return NULL; - p[0] = p[1] = ' '; - memset(p + 2, '\t', level); - p[2 + level] = 0; + if (flags & BUS_MESSAGE_DUMP_WITH_HEADER) { + p[i++] = ' '; + p[i++] = ' '; + } + + memset(p + i, ' ', level*8); + p[i + level*8] = 0; return p; } -int bus_message_dump(sd_bus_message *m, FILE *f, bool with_header) { +int bus_message_dump(sd_bus_message *m, FILE *f, unsigned flags) { unsigned level = 1; int r; @@ -52,7 +67,7 @@ int bus_message_dump(sd_bus_message *m, FILE *f, bool with_header) { if (!f) f = stdout; - if (with_header) { + if (flags & BUS_MESSAGE_DUMP_WITH_HEADER) { fprintf(f, "%s%s%s Type=%s%s%s Endian=%c Flags=%u Version=%u Priority=%lli", m->header->type == SD_BUS_MESSAGE_METHOD_ERROR ? ansi_highlight_red() : @@ -107,16 +122,15 @@ int bus_message_dump(sd_bus_message *m, FILE *f, bool with_header) { if (m->monotonic != 0 || m->realtime != 0 || m->seqnum != 0) fputs("\n", f); - bus_creds_dump(&m->creds, f); + bus_creds_dump(&m->creds, f, true); } - r = sd_bus_message_rewind(m, true); - if (r < 0) { - log_error("Failed to rewind: %s", strerror(-r)); - return r; - } + r = sd_bus_message_rewind(m, !(flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY)); + if (r < 0) + return log_error_errno(r, "Failed to rewind: %m"); - fprintf(f, " MESSAGE \"%s\" {\n", strempty(m->root_container.signature)); + if (!(flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY)) + fprintf(f, "%sMESSAGE \"%s\" {\n", indent(0, flags), strempty(m->root_container.signature)); for (;;) { _cleanup_free_ char *prefix = NULL; @@ -136,24 +150,20 @@ int bus_message_dump(sd_bus_message *m, FILE *f, bool with_header) { } basic; r = sd_bus_message_peek_type(m, &type, &contents); - if (r < 0) { - log_error("Failed to peek type: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to peek type: %m"); if (r == 0) { if (level <= 1) break; r = sd_bus_message_exit_container(m); - if (r < 0) { - log_error("Failed to exit container: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to exit container: %m"); level--; - prefix = indent(level); + prefix = indent(level, flags); if (!prefix) return log_oom(); @@ -161,16 +171,14 @@ int bus_message_dump(sd_bus_message *m, FILE *f, bool with_header) { continue; } - prefix = indent(level); + prefix = indent(level, flags); if (!prefix) return log_oom(); if (bus_type_is_container(type) > 0) { r = sd_bus_message_enter_container(m, type, contents); - if (r < 0) { - log_error("Failed to enter container: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to enter container: %m"); if (type == SD_BUS_TYPE_ARRAY) fprintf(f, "%sARRAY \"%s\" {\n", prefix, contents); @@ -187,10 +195,8 @@ int bus_message_dump(sd_bus_message *m, FILE *f, bool with_header) { } r = sd_bus_message_read_basic(m, type, &basic); - if (r < 0) { - log_error("Failed to get basic: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get basic: %m"); assert(r > 0); @@ -253,7 +259,9 @@ int bus_message_dump(sd_bus_message *m, FILE *f, bool with_header) { } } - fprintf(f, " };\n\n"); + if (!(flags & BUS_MESSAGE_DUMP_SUBTREE_ONLY)) + fprintf(f, "%s};\n\n", indent(0, flags)); + return 0; } @@ -261,6 +269,7 @@ static void dump_capabilities( sd_bus_creds *c, FILE *f, const char *name, + bool terse, int (*has)(sd_bus_creds *c, int capability)) { unsigned long i, last_cap; @@ -277,20 +286,18 @@ static void dump_capabilities( if (r < 0) return; - fprintf(f, " %s=", name); + fprintf(f, "%s%s=%s", terse ? " " : "", name, terse ? "" : ansi_highlight()); last_cap = cap_last_cap(); for (;;) { if (r > 0) { - _cleanup_cap_free_charp_ char *t; if (n > 0) fputc(' ', f); if (n % 4 == 3) - fputs("\n ", f); + fprintf(f, terse ? "\n " : "\n "); - t = cap_to_name(i); - fprintf(f, "%s", t); + fprintf(f, "%s", strna(capability_to_name(i))); n++; } @@ -303,14 +310,18 @@ static void dump_capabilities( } fputs("\n", f); + + if (!terse) + fputs(ansi_highlight_off(), f); } -int bus_creds_dump(sd_bus_creds *c, FILE *f) { +int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse) { bool audit_sessionid_is_set = false, audit_loginuid_is_set = false; const char *u = NULL, *uu = NULL, *s = NULL, *sl = NULL; uid_t owner, audit_loginuid; uint32_t audit_sessionid; char **cmdline = NULL, **well_known = NULL; + const char *prefix, *color, *suffix; int r; assert(c); @@ -318,45 +329,76 @@ int bus_creds_dump(sd_bus_creds *c, FILE *f) { if (!f) f = stdout; + if (terse) { + prefix = " "; + suffix = ""; + color = ""; + } else { + const char *off; + + prefix = ""; + color = ansi_highlight(); + + off = ansi_highlight_off(); + suffix = strappenda(off, "\n"); + } + if (c->mask & SD_BUS_CREDS_PID) - fprintf(f, " PID="PID_FMT, c->pid); - if (c->mask & SD_BUS_CREDS_PID_STARTTIME) - fprintf(f, " PIDStartTime="USEC_FMT, c->pid_starttime); + fprintf(f, "%sPID=%s"PID_FMT"%s", prefix, color, c->pid, suffix); if (c->mask & SD_BUS_CREDS_TID) - fprintf(f, " TID="PID_FMT, c->tid); + fprintf(f, "%sTID=%s"PID_FMT"%s", prefix, color, c->tid, suffix); + + if (terse && ((c->mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_TID)))) + fputs("\n", f); + if (c->mask & SD_BUS_CREDS_UID) - fprintf(f, " UID="UID_FMT, c->uid); + fprintf(f, "%sUID=%s"UID_FMT"%s", prefix, color, c->uid, suffix); + if (c->mask & SD_BUS_CREDS_EUID) + fprintf(f, "%sEUID=%s"UID_FMT"%s", prefix, color, c->euid, suffix); + if (c->mask & SD_BUS_CREDS_SUID) + fprintf(f, "%sSUID=%s"UID_FMT"%s", prefix, color, c->suid, suffix); + if (c->mask & SD_BUS_CREDS_FSUID) + fprintf(f, "%sFSUID=%s"UID_FMT"%s", prefix, color, c->fsuid, suffix); r = sd_bus_creds_get_owner_uid(c, &owner); if (r >= 0) - fprintf(f, " OwnerUID="UID_FMT, owner); + fprintf(f, "%sOwnerUID=%s"UID_FMT"%s", prefix, color, owner, suffix); if (c->mask & SD_BUS_CREDS_GID) - fprintf(f, " GID="GID_FMT, c->gid); + fprintf(f, "%sGID=%s"GID_FMT"%s", prefix, color, c->gid, suffix); + if (c->mask & SD_BUS_CREDS_EGID) + fprintf(f, "%sEGID=%s"GID_FMT"%s", prefix, color, c->egid, suffix); + if (c->mask & SD_BUS_CREDS_SGID) + fprintf(f, "%sSGID=%s"GID_FMT"%s", prefix, color, c->sgid, suffix); + if (c->mask & SD_BUS_CREDS_FSGID) + fprintf(f, "%sFSGID=%s"GID_FMT"%s", prefix, color, c->fsgid, suffix); + + if (c->mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) { + unsigned i; + + fprintf(f, "%sSupplementaryGIDs=%s", prefix, color); + for (i = 0; i < c->n_supplementary_gids; i++) + fprintf(f, "%s" GID_FMT, i > 0 ? " " : "", c->supplementary_gids[i]); + fprintf(f, "%s", suffix); + } - if ((c->mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_PID_STARTTIME|SD_BUS_CREDS_TID|SD_BUS_CREDS_UID|SD_BUS_CREDS_GID)) || r >= 0) + if (terse && ((c->mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| + SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID| + SD_BUS_CREDS_SUPPLEMENTARY_GIDS)) || r >= 0)) fputs("\n", f); - if (c->mask & SD_BUS_CREDS_EXE) - fprintf(f, " Exe=%s", c->exe); if (c->mask & SD_BUS_CREDS_COMM) - fprintf(f, " Comm=%s", c->comm); + fprintf(f, "%sComm=%s%s%s", prefix, color, c->comm, suffix); if (c->mask & SD_BUS_CREDS_TID_COMM) - fprintf(f, " TIDComm=%s", c->tid_comm); - - if (c->mask & (SD_BUS_CREDS_EXE|SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM)) - fputs("\n", f); - - if (c->mask & SD_BUS_CREDS_SELINUX_CONTEXT) - fprintf(f, " Label=%s", c->label); - if (c->mask & SD_BUS_CREDS_CONNECTION_NAME) - fprintf(f, " ConnectionName=%s", c->conn_name); + fprintf(f, "%sTIDComm=%s%s%s", prefix, color, c->tid_comm, suffix); + if (c->mask & SD_BUS_CREDS_EXE) + fprintf(f, "%sExe=%s%s%s", prefix, color, c->exe, suffix); - if (c->mask & (SD_BUS_CREDS_SELINUX_CONTEXT|SD_BUS_CREDS_CONNECTION_NAME)) + if (terse && (c->mask & (SD_BUS_CREDS_EXE|SD_BUS_CREDS_COMM|SD_BUS_CREDS_TID_COMM))) fputs("\n", f); if (sd_bus_creds_get_cmdline(c, &cmdline) >= 0) { char **i; - fputs(" CommandLine={", f); + fprintf(f, "%sCommandLine=%s", prefix, color); STRV_FOREACH(i, cmdline) { if (i != cmdline) fputc(' ', f); @@ -364,46 +406,54 @@ int bus_creds_dump(sd_bus_creds *c, FILE *f) { fputs(*i, f); } - fputs("}\n", f); + fprintf(f, "%s", suffix); } + if (c->mask & SD_BUS_CREDS_SELINUX_CONTEXT) + fprintf(f, "%sLabel=%s%s%s", prefix, color, c->label, suffix); + if (c->mask & SD_BUS_CREDS_DESCRIPTION) + fprintf(f, "%sDescription=%s%s%s", prefix, color, c->description, suffix); + + if (terse && (c->mask & (SD_BUS_CREDS_SELINUX_CONTEXT|SD_BUS_CREDS_DESCRIPTION))) + fputs("\n", f); + if (c->mask & SD_BUS_CREDS_CGROUP) - fprintf(f, " CGroup=%s", c->cgroup); + fprintf(f, "%sCGroup=%s%s%s", prefix, color, c->cgroup, suffix); sd_bus_creds_get_unit(c, &u); if (u) - fprintf(f, " Unit=%s", u); + fprintf(f, "%sUnit=%s%s%s", prefix, color, u, suffix); sd_bus_creds_get_user_unit(c, &uu); if (uu) - fprintf(f, " UserUnit=%s", uu); + fprintf(f, "%sUserUnit=%s%s%s", prefix, color, uu, suffix); sd_bus_creds_get_slice(c, &sl); if (sl) - fprintf(f, " Slice=%s", sl); + fprintf(f, "%sSlice=%s%s%s", prefix, color, sl, suffix); sd_bus_creds_get_session(c, &s); if (s) - fprintf(f, " Session=%s", s); + fprintf(f, "%sSession=%s%s%s", prefix, color, s, suffix); - if ((c->mask & SD_BUS_CREDS_CGROUP) || u || uu || sl || s) + if (terse && ((c->mask & SD_BUS_CREDS_CGROUP) || u || uu || sl || s)) fputs("\n", f); if (sd_bus_creds_get_audit_login_uid(c, &audit_loginuid) >= 0) { audit_loginuid_is_set = true; - fprintf(f, " AuditLoginUID="UID_FMT, audit_loginuid); + fprintf(f, "%sAuditLoginUID=%s"UID_FMT"%s", prefix, color, audit_loginuid, suffix); } if (sd_bus_creds_get_audit_session_id(c, &audit_sessionid) >= 0) { audit_sessionid_is_set = true; - fprintf(f, " AuditSessionID=%"PRIu32, audit_sessionid); + fprintf(f, "%sAuditSessionID=%s%"PRIu32"%s", prefix, color, audit_sessionid, suffix); } - if (audit_loginuid_is_set || audit_sessionid_is_set) + if (terse && (audit_loginuid_is_set || audit_sessionid_is_set)) fputs("\n", f); if (c->mask & SD_BUS_CREDS_UNIQUE_NAME) - fprintf(f, " UniqueName=%s", c->unique_name); + fprintf(f, "%sUniqueName=%s%s%s", prefix, color, c->unique_name, suffix); if (sd_bus_creds_get_well_known_names(c, &well_known) >= 0) { char **i; - fputs(" WellKnownNames={", f); + fprintf(f, "%sWellKnownNames=%s", prefix, color); STRV_FOREACH(i, well_known) { if (i != well_known) fputc(' ', f); @@ -411,16 +461,111 @@ int bus_creds_dump(sd_bus_creds *c, FILE *f) { fputs(*i, f); } - fputc('}', f); + fprintf(f, "%s", suffix); } - if (c->mask & SD_BUS_CREDS_UNIQUE_NAME || well_known) + if (terse && (c->mask & SD_BUS_CREDS_UNIQUE_NAME || well_known)) fputc('\n', f); - dump_capabilities(c, f, "EffectiveCapabilities", sd_bus_creds_has_effective_cap); - dump_capabilities(c, f, "PermittedCapabilities", sd_bus_creds_has_permitted_cap); - dump_capabilities(c, f, "InheritableCapabilities", sd_bus_creds_has_inheritable_cap); - dump_capabilities(c, f, "BoundingCapabilities", sd_bus_creds_has_bounding_cap); + dump_capabilities(c, f, "EffectiveCapabilities", terse, sd_bus_creds_has_effective_cap); + dump_capabilities(c, f, "PermittedCapabilities", terse, sd_bus_creds_has_permitted_cap); + dump_capabilities(c, f, "InheritableCapabilities", terse, sd_bus_creds_has_inheritable_cap); + dump_capabilities(c, f, "BoundingCapabilities", terse, sd_bus_creds_has_bounding_cap); + + return 0; +} + +/* + * For details about the file format, see: + * + * http://wiki.wireshark.org/Development/LibpcapFileFormat + */ + +typedef struct _packed_ pcap_hdr_s { + uint32_t magic_number; /* magic number */ + uint16_t version_major; /* major version number */ + uint16_t version_minor; /* minor version number */ + int32_t thiszone; /* GMT to local correction */ + uint32_t sigfigs; /* accuracy of timestamps */ + uint32_t snaplen; /* max length of captured packets, in octets */ + uint32_t network; /* data link type */ +} pcap_hdr_t ; + +typedef struct _packed_ pcaprec_hdr_s { + uint32_t ts_sec; /* timestamp seconds */ + uint32_t ts_usec; /* timestamp microseconds */ + uint32_t incl_len; /* number of octets of packet saved in file */ + uint32_t orig_len; /* actual length of packet */ +} pcaprec_hdr_t; + +int bus_pcap_header(size_t snaplen, FILE *f) { + + pcap_hdr_t hdr = { + .magic_number = 0xa1b2c3d4U, + .version_major = 2, + .version_minor = 4, + .thiszone = 0, /* UTC */ + .sigfigs = 0, + .network = 231, /* D-Bus */ + }; + + if (!f) + f = stdout; + + assert(snaplen > 0); + assert((size_t) (uint32_t) snaplen == snaplen); + + hdr.snaplen = (uint32_t) snaplen; + + fwrite(&hdr, 1, sizeof(hdr), f); + fflush(f); + + return 0; +} + +int bus_message_pcap_frame(sd_bus_message *m, size_t snaplen, FILE *f) { + struct bus_body_part *part; + pcaprec_hdr_t hdr = {}; + struct timeval tv; + unsigned i; + size_t w; + + if (!f) + f = stdout; + + assert(m); + assert(snaplen > 0); + assert((size_t) (uint32_t) snaplen == snaplen); + + if (m->realtime != 0) + timeval_store(&tv, m->realtime); + else + assert_se(gettimeofday(&tv, NULL) >= 0); + + hdr.ts_sec = tv.tv_sec; + hdr.ts_usec = tv.tv_usec; + hdr.orig_len = BUS_MESSAGE_SIZE(m); + hdr.incl_len = MIN(hdr.orig_len, snaplen); + + /* write the pcap header */ + fwrite(&hdr, 1, sizeof(hdr), f); + + /* write the dbus header */ + w = MIN(BUS_MESSAGE_BODY_BEGIN(m), snaplen); + fwrite(m->header, 1, w, f); + snaplen -= w; + + /* write the dbus body */ + MESSAGE_FOREACH_PART(part, i, m) { + if (snaplen <= 0) + break; + + w = MIN(part->size, snaplen); + fwrite(part->data, 1, w, f); + snaplen -= w; + } + + fflush(f); return 0; } diff --git a/src/libsystemd/sd-bus/bus-dump.h b/src/libsystemd/sd-bus/bus-dump.h index bb1d25dc42..d2522edeba 100644 --- a/src/libsystemd/sd-bus/bus-dump.h +++ b/src/libsystemd/sd-bus/bus-dump.h @@ -26,6 +26,14 @@ #include "sd-bus.h" -int bus_message_dump(sd_bus_message *m, FILE *f, bool with_header); +enum { + BUS_MESSAGE_DUMP_WITH_HEADER = 1, + BUS_MESSAGE_DUMP_SUBTREE_ONLY = 2, +}; -int bus_creds_dump(sd_bus_creds *c, FILE *f); +int bus_message_dump(sd_bus_message *m, FILE *f, unsigned flags); + +int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse); + +int bus_pcap_header(size_t snaplen, FILE *f); +int bus_message_pcap_frame(sd_bus_message *m, size_t snaplen, FILE *f); diff --git a/src/libsystemd/sd-bus/bus-error-mapping.gperf b/src/libsystemd/sd-bus/bus-error-mapping.gperf deleted file mode 100644 index 59eaa3554b..0000000000 --- a/src/libsystemd/sd-bus/bus-error-mapping.gperf +++ /dev/null @@ -1,50 +0,0 @@ -%{ -#include <errno.h> -#include "bus-error.h" -%} -name_error_mapping; -%null_strings -%language=ANSI-C -%define slot-name name -%define hash-function-name bus_error_mapping_hash -%define lookup-function-name bus_error_mapping_lookup -%readonly-tables -%omit-struct-type -%struct-type -%includes -%% -org.freedesktop.DBus.Error.Failed, EACCES -org.freedesktop.DBus.Error.NoMemory, ENOMEM -org.freedesktop.DBus.Error.ServiceUnknown, EHOSTUNREACH -org.freedesktop.DBus.Error.NameHasNoOwner, ENXIO -org.freedesktop.DBus.Error.NoReply, ETIMEDOUT -org.freedesktop.DBus.Error.IOError, EIO -org.freedesktop.DBus.Error.BadAddress, EADDRNOTAVAIL -org.freedesktop.DBus.Error.NotSupported, ENOTSUP -org.freedesktop.DBus.Error.LimitsExceeded, ENOBUFS -org.freedesktop.DBus.Error.AccessDenied, EACCES -org.freedesktop.DBus.Error.AuthFailed, EACCES -org.freedesktop.DBus.Error.InteractiveAuthorizationRequired EACCES -org.freedesktop.DBus.Error.NoServer, EHOSTDOWN -org.freedesktop.DBus.Error.Timeout, ETIMEDOUT -org.freedesktop.DBus.Error.NoNetwork, ENONET -org.freedesktop.DBus.Error.AddressInUse, EADDRINUSE -org.freedesktop.DBus.Error.Disconnected, ECONNRESET -org.freedesktop.DBus.Error.InvalidArgs, EINVAL -org.freedesktop.DBus.Error.FileNotFound, ENOENT -org.freedesktop.DBus.Error.FileExists, EEXIST -org.freedesktop.DBus.Error.UnknownMethod, EBADR -org.freedesktop.DBus.Error.UnknownObject, EBADR -org.freedesktop.DBus.Error.UnknownInterface, EBADR -org.freedesktop.DBus.Error.UnknownProperty, EBADR -org.freedesktop.DBus.Error.PropertyReadOnly, EROFS -org.freedesktop.DBus.Error.UnixProcessIdUnknown, ESRCH -org.freedesktop.DBus.Error.InvalidSignature, EINVAL -org.freedesktop.DBus.Error.InconsistentMessage, EBADMSG -# -org.freedesktop.DBus.Error.TimedOut, ETIMEDOUT -org.freedesktop.DBus.Error.MatchRuleInvalid, EINVAL -org.freedesktop.DBus.Error.InvalidFileContent, EINVAL -org.freedesktop.DBus.Error.MatchRuleNotFound, ENOENT -org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown, ESRCH -org.freedesktop.DBus.Error.ObjectPathInUse, EBUSY diff --git a/src/libsystemd/sd-bus/bus-error.c b/src/libsystemd/sd-bus/bus-error.c index af83c12d53..2955d9dd2b 100644 --- a/src/libsystemd/sd-bus/bus-error.c +++ b/src/libsystemd/sd-bus/bus-error.c @@ -32,13 +32,56 @@ #include "sd-bus.h" #include "bus-error.h" -#define BUS_ERROR_OOM SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_MEMORY, "Out of memory") -#define BUS_ERROR_FAILED SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FAILED, "Operation failed") +BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_standard_errors[] = { + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Failed", EACCES), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoMemory", ENOMEM), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.ServiceUnknown", EHOSTUNREACH), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NameHasNoOwner", ENXIO), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoReply", ETIMEDOUT), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.IOError", EIO), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.BadAddress", EADDRNOTAVAIL), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NotSupported", ENOTSUP), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.LimitsExceeded", ENOBUFS), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AccessDenied", EACCES), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AuthFailed", EACCES), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InteractiveAuthorizationRequired", EACCES), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoServer", EHOSTDOWN), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Timeout", ETIMEDOUT), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoNetwork", ENONET), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AddressInUse", EADDRINUSE), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Disconnected", ECONNRESET), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidArgs", EINVAL), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.FileNotFound", ENOENT), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.FileExists", EEXIST), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownMethod", EBADR), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownObject", EBADR), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownInterface", EBADR), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownProperty", EBADR), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.PropertyReadOnly", EROFS), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnixProcessIdUnknown", ESRCH), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidSignature", EINVAL), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InconsistentMessage", EBADMSG), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.TimedOut", ETIMEDOUT), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.MatchRuleInvalid", EINVAL), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidFileContent", EINVAL), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.MatchRuleNotFound", ENOENT), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown", ESRCH), + SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.ObjectPathInUse", EBUSY), + SD_BUS_ERROR_MAP_END +}; + +/* GCC maps this magically to the beginning and end of the BUS_ERROR_MAP section */ +extern const sd_bus_error_map __start_BUS_ERROR_MAP[]; +extern const sd_bus_error_map __stop_BUS_ERROR_MAP[]; + +/* Additional maps registered with sd_bus_error_add_map() are in this + * NULL terminated array */ +static const sd_bus_error_map **additional_error_maps = NULL; static int bus_error_name_to_errno(const char *name) { + const sd_bus_error_map **map, *m; const char *p; int r; - const name_error_mapping *m; if (!name) return EINVAL; @@ -52,9 +95,37 @@ static int bus_error_name_to_errno(const char *name) { return r; } - m = bus_error_mapping_lookup(name, strlen(name)); - if (m) - return m->code; + if (additional_error_maps) { + for (map = additional_error_maps; *map; map++) { + for (m = *map;; m++) { + /* For additional error maps the end marker is actually the end marker */ + if (m->code == BUS_ERROR_MAP_END_MARKER) + break; + + if (streq(m->name, name)) + return m->code; + } + } + } + + m = __start_BUS_ERROR_MAP; + while (m < __stop_BUS_ERROR_MAP) { + /* For magic ELF error maps, the end marker might + * appear in the middle of things, since multiple maps + * might appear in the same section. Hence, let's skip + * over it, but realign the pointer to the netx 8byte + * boundary, which is the selected alignment for the + * arrays. */ + if (m->code == BUS_ERROR_MAP_END_MARKER) { + m = ALIGN8_PTR(m+1); + continue; + } + + if (streq(m->name, name)) + return m->code; + + m++; + } return EIO; } @@ -398,6 +469,7 @@ _public_ int sd_bus_error_set_errno(sd_bus_error *e, int error) { } int bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) { + PROTECT_ERRNO; int r; if (error < 0) @@ -431,8 +503,9 @@ int bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_lis if (format) { char *m; - /* First, let's try to fill in the supplied message */ + /* Then, let's try to fill in the supplied message */ + errno = error; /* Make sure that %m resolves to the specified error */ r = vasprintf(&m, format, ap); if (r >= 0) { @@ -504,3 +577,31 @@ const char *bus_error_message(const sd_bus_error *e, int error) { return strerror(error); } + +_public_ int sd_bus_error_add_map(const sd_bus_error_map *map) { + const sd_bus_error_map **maps = NULL; + unsigned n = 0; + + assert_return(map, -EINVAL); + + if (additional_error_maps) { + for (;; n++) { + if (additional_error_maps[n] == NULL) + break; + + if (additional_error_maps[n] == map) + return 0; + } + } + + maps = realloc_multiply(additional_error_maps, sizeof(struct sd_bus_error_map*), n + 2); + if (!maps) + return -ENOMEM; + + + maps[n] = map; + maps[n+1] = NULL; + + additional_error_maps = maps; + return 1; +} diff --git a/src/libsystemd/sd-bus/bus-error.h b/src/libsystemd/sd-bus/bus-error.h index cf0ad9d545..fb0199c948 100644 --- a/src/libsystemd/sd-bus/bus-error.h +++ b/src/libsystemd/sd-bus/bus-error.h @@ -26,17 +26,40 @@ #include "sd-bus.h" #include "macro.h" -struct name_error_mapping { - const char* name; - int code; -}; -typedef struct name_error_mapping name_error_mapping; - -const name_error_mapping* bus_error_mapping_lookup(const char *str, unsigned int len); - bool bus_error_is_dirty(sd_bus_error *e); const char *bus_error_message(const sd_bus_error *e, int error); int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap) _printf_(3,0); int bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) _printf_(3,0); + +#define BUS_ERROR_OOM SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NO_MEMORY, "Out of memory") +#define BUS_ERROR_FAILED SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FAILED, "Operation failed") + +/* + * There are two ways to register error maps with the error translation + * logic: by using BUS_ERROR_MAP_ELF_REGISTER, which however only + * works when linked into the same ELF module, or via + * sd_bus_error_add_map() which is the official, external API, that + * works from any module. + * + * Note that BUS_ERROR_MAP_ELF_REGISTER has to be used as decorator in + * the bus error table, and BUS_ERROR_MAP_ELF_USE has to be used at + * least once per compilation unit (i.e. per library), to ensure that + * the error map is really added to the final binary. + */ + +#define BUS_ERROR_MAP_ELF_REGISTER \ + __attribute__ ((__section__("BUS_ERROR_MAP"))) \ + __attribute__ ((__used__)) \ + __attribute__ ((aligned(8))) + +#define BUS_ERROR_MAP_ELF_USE(errors) \ + extern const sd_bus_error_map errors[]; \ + __attribute__ ((used)) static const sd_bus_error_map * const CONCATENATE(errors ## _copy_, __COUNTER__) = errors; + +/* We use something exotic as end marker, to ensure people build the + * maps using the macsd-ros. */ +#define BUS_ERROR_MAP_END_MARKER -'x' + +BUS_ERROR_MAP_ELF_USE(bus_standard_errors); diff --git a/src/libsystemd/sd-bus/bus-internal.c b/src/libsystemd/sd-bus/bus-internal.c index 0bea8cac49..91b288cd25 100644 --- a/src/libsystemd/sd-bus/bus-internal.c +++ b/src/libsystemd/sd-bus/bus-internal.c @@ -166,6 +166,26 @@ bool service_name_is_valid(const char *p) { return true; } +char* service_name_startswith(const char *a, const char *b) { + const char *p; + + if (!service_name_is_valid(a) || + !service_name_is_valid(b)) + return NULL; + + p = startswith(a, b); + if (!p) + return NULL; + + if (*p == 0) + return (char*) p; + + if (*p == '.') + return (char*) p + 1; + + return NULL; +} + bool member_name_is_valid(const char *p) { const char *q; diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h index 57247d7df9..977b3407ba 100644 --- a/src/libsystemd/sd-bus/bus-internal.h +++ b/src/libsystemd/sd-bus/bus-internal.h @@ -142,6 +142,7 @@ struct sd_bus_slot { void *userdata; BusSlotType type:5; bool floating:1; + char *description; LIST_FIELDS(sd_bus_slot, slots); @@ -205,6 +206,7 @@ struct sd_bus { bool nodes_modified:1; bool trusted:1; bool fake_creds_valid:1; + bool fake_pids_valid:1; bool manual_peer_interface:1; bool is_system:1; bool is_user:1; @@ -305,11 +307,12 @@ struct sd_bus { pid_t tid; struct kdbus_creds fake_creds; + struct kdbus_pids fake_pids; char *fake_label; char *cgroup_root; - char *connection_name; + char *description; size_t bloom_size; unsigned bloom_n_hash; @@ -339,6 +342,7 @@ struct sd_bus { bool interface_name_is_valid(const char *p) _pure_; bool service_name_is_valid(const char *p) _pure_; +char* service_name_startswith(const char *a, const char *b); bool member_name_is_valid(const char *p) _pure_; bool object_path_is_valid(const char *p) _pure_; char *object_path_startswith(const char *a, const char *b) _pure_; diff --git a/src/libsystemd/sd-bus/bus-kernel.c b/src/libsystemd/sd-bus/bus-kernel.c index 0327614742..752c63adb3 100644 --- a/src/libsystemd/sd-bus/bus-kernel.c +++ b/src/libsystemd/sd-bus/bus-kernel.c @@ -31,6 +31,9 @@ #include "util.h" #include "strv.h" +#include "memfd-util.h" +#include "cgroup-util.h" +#include "fileio.h" #include "bus-internal.h" #include "bus-message.h" @@ -38,7 +41,6 @@ #include "bus-bloom.h" #include "bus-util.h" #include "bus-label.h" -#include "cgroup-util.h" #define UNIQUE_NAME_MAX (3+DECIMAL_STR_MAX(uint64_t)) @@ -76,7 +78,7 @@ static void append_payload_vec(struct kdbus_item **d, const void *p, size_t sz) *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size); } -static void append_payload_memfd(struct kdbus_item **d, int memfd, size_t sz) { +static void append_payload_memfd(struct kdbus_item **d, int memfd, size_t start, size_t sz) { assert(d); assert(memfd >= 0); assert(sz > 0); @@ -85,6 +87,7 @@ static void append_payload_memfd(struct kdbus_item **d, int memfd, size_t sz) { (*d)->size = offsetof(struct kdbus_item, memfd) + sizeof(struct kdbus_memfd); (*d)->type = KDBUS_ITEM_PAYLOAD_MEMFD; (*d)->memfd.fd = memfd; + (*d)->memfd.start = start; (*d)->memfd.size = sz; *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size); @@ -133,6 +136,32 @@ static void append_fds(struct kdbus_item **d, const int fds[], unsigned n_fds) { *d = (struct kdbus_item *) ((uint8_t*) *d + (*d)->size); } +static void add_bloom_arg(void *data, size_t size, unsigned n_hash, unsigned i, const char *t) { + char buf[sizeof("arg")-1 + 2 + sizeof("-slash-prefix")]; + char *e; + + assert(data); + assert(size > 0); + assert(i < 64); + assert(t); + + e = stpcpy(buf, "arg"); + if (i < 10) + *(e++) = '0' + (char) i; + else { + *(e++) = '0' + (char) (i / 10); + *(e++) = '0' + (char) (i % 10); + } + + *e = 0; + bloom_add_pair(data, size, n_hash, buf, t); + + strcpy(e, "-dot-prefix"); + bloom_add_prefixes(data, size, n_hash, buf, t, '.'); + strcpy(e, "-slash-prefix"); + bloom_add_prefixes(data, size, n_hash, buf, t, '/'); +} + static int bus_message_setup_bloom(sd_bus_message *m, struct kdbus_bloom_filter *bloom) { void *data; unsigned i; @@ -162,39 +191,42 @@ static int bus_message_setup_bloom(sd_bus_message *m, struct kdbus_bloom_filter return r; for (i = 0; i < 64; i++) { + const char *t, *contents; char type; - const char *t; - char buf[sizeof("arg")-1 + 2 + sizeof("-slash-prefix")]; - char *e; - r = sd_bus_message_peek_type(m, &type, NULL); + r = sd_bus_message_peek_type(m, &type, &contents); if (r < 0) return r; - if (type != SD_BUS_TYPE_STRING && - type != SD_BUS_TYPE_OBJECT_PATH && - type != SD_BUS_TYPE_SIGNATURE) - break; + if (IN_SET(type, SD_BUS_TYPE_STRING, SD_BUS_TYPE_OBJECT_PATH, SD_BUS_TYPE_SIGNATURE)) { - r = sd_bus_message_read_basic(m, type, &t); - if (r < 0) - return r; + /* The bloom filter includes simple strings of any kind */ + r = sd_bus_message_read_basic(m, type, &t); + if (r < 0) + return r; - e = stpcpy(buf, "arg"); - if (i < 10) - *(e++) = '0' + (char) i; - else { - *(e++) = '0' + (char) (i / 10); - *(e++) = '0' + (char) (i % 10); - } + add_bloom_arg(data, m->bus->bloom_size, m->bus->bloom_n_hash, i, t); + } if (type == SD_BUS_TYPE_ARRAY && STR_IN_SET(contents, "s", "o", "g")) { - *e = 0; - bloom_add_pair(data, m->bus->bloom_size, m->bus->bloom_n_hash, buf, t); + /* As well as array of simple strings of any kinds */ + r = sd_bus_message_enter_container(m, type, contents); + if (r < 0) + return r; + + while ((r = sd_bus_message_read_basic(m, contents[0], &t)) > 0) + add_bloom_arg(data, m->bus->bloom_size, m->bus->bloom_n_hash, i, t); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; - strcpy(e, "-dot-prefix"); - bloom_add_prefixes(data, m->bus->bloom_size, m->bus->bloom_n_hash, buf, t, '.'); - strcpy(e, "-slash-prefix"); - bloom_add_prefixes(data, m->bus->bloom_size, m->bus->bloom_n_hash, buf, t, '/'); + } else + /* Stop adding to bloom filter as soon as we + * run into the first argument we cannot add + * to it. */ + break; } return 0; @@ -203,6 +235,7 @@ static int bus_message_setup_bloom(sd_bus_message *m, struct kdbus_bloom_filter static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) { struct bus_body_part *part; struct kdbus_item *d; + const char *destination; bool well_known; uint64_t unique; size_t sz, dl; @@ -218,8 +251,10 @@ static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) { if (m->kdbus) return 0; - if (m->destination) { - r = bus_kernel_parse_unique_name(m->destination, &unique); + destination = m->destination ?: m->destination_ptr; + + if (destination) { + r = bus_kernel_parse_unique_name(destination, &unique); if (r < 0) return r; @@ -229,12 +264,10 @@ static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) { sz = offsetof(struct kdbus_msg, items); - assert_cc(ALIGN8(offsetof(struct kdbus_item, vec) + sizeof(struct kdbus_vec)) == - ALIGN8(offsetof(struct kdbus_item, memfd) + sizeof(struct kdbus_memfd))); - /* Add in fixed header, fields header and payload */ - sz += (1 + m->n_body_parts) * - ALIGN8(offsetof(struct kdbus_item, vec) + sizeof(struct kdbus_vec)); + sz += (1 + m->n_body_parts) * ALIGN8(offsetof(struct kdbus_item, vec) + + MAX(sizeof(struct kdbus_vec), + sizeof(struct kdbus_memfd))); /* Add space for bloom filter */ sz += ALIGN8(offsetof(struct kdbus_item, bloom_filter) + @@ -243,7 +276,7 @@ static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) { /* Add in well-known destination header */ if (well_known) { - dl = strlen(m->destination); + dl = strlen(destination); sz += ALIGN8(offsetof(struct kdbus_item, str) + dl + 1); } @@ -263,16 +296,23 @@ static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) { m->kdbus->flags = ((m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) ? 0 : KDBUS_MSG_FLAGS_EXPECT_REPLY) | ((m->header->flags & BUS_MESSAGE_NO_AUTO_START) ? KDBUS_MSG_FLAGS_NO_AUTO_START : 0); - m->kdbus->dst_id = - well_known ? 0 : - m->destination ? unique : KDBUS_DST_ID_BROADCAST; + + if (well_known) + /* verify_destination_id will usually be 0, which makes the kernel driver only look + * at the provided well-known name. Otherwise, the kernel will make sure the provided + * destination id matches the owner of the provided weel-known-name, and fail if they + * differ. Currently, this is only needed for bus-proxyd. */ + m->kdbus->dst_id = m->verify_destination_id; + else + m->kdbus->dst_id = destination ? unique : KDBUS_DST_ID_BROADCAST; + m->kdbus->payload_type = KDBUS_PAYLOAD_DBUS; m->kdbus->cookie = (uint64_t) m->header->serial; m->kdbus->priority = m->priority; - if (m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) { + if (m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) m->kdbus->cookie_reply = m->reply_cookie; - } else { + else { struct timespec now; assert_se(clock_gettime(CLOCK_MONOTONIC_COARSE, &now) == 0); @@ -283,7 +323,7 @@ static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) { d = m->kdbus->items; if (well_known) - append_destination(&d, m->destination, dl); + append_destination(&d, destination, dl); append_payload_vec(&d, m->header, BUS_MESSAGE_BODY_BEGIN(m)); @@ -298,11 +338,11 @@ static int bus_message_setup_kmsg(sd_bus *b, sd_bus_message *m) { continue; } - if (part->memfd >= 0 && part->sealed && m->destination) { + if (part->memfd >= 0 && part->sealed && destination) { /* Try to send a memfd, if the part is * sealed and this is not a broadcast. Since we can only */ - append_payload_memfd(&d, part->memfd, part->size); + append_payload_memfd(&d, part->memfd, part->memfd_offset, part->size); continue; } @@ -337,6 +377,15 @@ fail: return r; } +static void bus_message_set_sender_driver(sd_bus *bus, sd_bus_message *m) { + assert(bus); + assert(m); + + m->sender = m->creds.unique_name = (char*) "org.freedesktop.DBus"; + m->creds.well_known_names_driver = true; + m->creds.mask |= (SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_WELL_KNOWN_NAMES) & bus->creds_mask; +} + static void unset_memfds(struct sd_bus_message *m) { struct bus_body_part *part; unsigned i; @@ -372,7 +421,7 @@ static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) { case KDBUS_ITEM_PAYLOAD_OFF: if (!h) { - h = (struct bus_header *)((uint8_t *)k + d->vec.offset); + h = (struct bus_header *)((uint8_t *)bus->kdbus_buffer + d->vec.offset); if (!bus_header_is_complete(h, d->vec.size)) return -EBADMSG; @@ -464,11 +513,11 @@ static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) { if (idx >= begin_body) { if (!part->is_zero) - part->data = (uint8_t *)k + d->vec.offset; + part->data = (uint8_t *)bus->kdbus_buffer + d->vec.offset; part->size = d->vec.size; } else { if (!part->is_zero) - part->data = (uint8_t *)k + d->vec.offset + (begin_body - idx); + part->data = (uint8_t *)bus->kdbus_buffer + d->vec.offset + (begin_body - idx); part->size = d->vec.size - (begin_body - idx); } @@ -494,6 +543,7 @@ static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) { } part->memfd = d->memfd.fd; + part->memfd_offset = d->memfd.start; part->size = d->memfd.size; part->sealed = true; @@ -501,28 +551,70 @@ static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) { break; } - case KDBUS_ITEM_CREDS: - /* UID/GID/PID are always valid */ - m->creds.uid = (uid_t) d->creds.uid; - m->creds.gid = (gid_t) d->creds.gid; - m->creds.pid = (pid_t) d->creds.pid; - m->creds.mask |= (SD_BUS_CREDS_UID|SD_BUS_CREDS_GID|SD_BUS_CREDS_PID) & bus->creds_mask; - - /* The PID starttime/TID might be missing - * however, when the data is faked by some - * data bus proxy and it lacks that - * information about the real client since - * SO_PEERCRED is used for that */ - - if (d->creds.starttime > 0) { - m->creds.pid_starttime = d->creds.starttime / NSEC_PER_USEC; - m->creds.mask |= SD_BUS_CREDS_PID_STARTTIME & bus->creds_mask; + case KDBUS_ITEM_PIDS: + + /* The PID/TID might be missing, when the data + * is faked by some data bus proxy and it + * lacks that information about the real + * client since SO_PEERCRED is used for + * that. */ + + if (d->pids.pid > 0) { + m->creds.pid = (pid_t) d->pids.pid; + m->creds.mask |= SD_BUS_CREDS_PID & bus->creds_mask; } - if (d->creds.tid > 0) { - m->creds.tid = (pid_t) d->creds.tid; + if (d->pids.tid > 0) { + m->creds.tid = (pid_t) d->pids.tid; m->creds.mask |= SD_BUS_CREDS_TID & bus->creds_mask; } + + break; + + case KDBUS_ITEM_CREDS: + + /* EUID/SUID/FSUID/EGID/SGID/FSGID might be missing too (see above). */ + + if ((uid_t) d->creds.uid != UID_INVALID) { + m->creds.uid = (uid_t) d->creds.uid; + m->creds.mask |= SD_BUS_CREDS_UID & bus->creds_mask; + } + + if ((uid_t) d->creds.euid != UID_INVALID) { + m->creds.euid = (uid_t) d->creds.euid; + m->creds.mask |= SD_BUS_CREDS_EUID & bus->creds_mask; + } + + if ((uid_t) d->creds.suid != UID_INVALID) { + m->creds.suid = (uid_t) d->creds.suid; + m->creds.mask |= SD_BUS_CREDS_SUID & bus->creds_mask; + } + + if ((uid_t) d->creds.fsuid != UID_INVALID) { + m->creds.fsuid = (uid_t) d->creds.fsuid; + m->creds.mask |= SD_BUS_CREDS_FSUID & bus->creds_mask; + } + + if ((gid_t) d->creds.gid != GID_INVALID) { + m->creds.gid = (gid_t) d->creds.gid; + m->creds.mask |= SD_BUS_CREDS_GID & bus->creds_mask; + } + + if ((gid_t) d->creds.egid != GID_INVALID) { + m->creds.egid = (gid_t) d->creds.egid; + m->creds.mask |= SD_BUS_CREDS_EGID & bus->creds_mask; + } + + if ((gid_t) d->creds.sgid != GID_INVALID) { + m->creds.sgid = (gid_t) d->creds.sgid; + m->creds.mask |= SD_BUS_CREDS_SGID & bus->creds_mask; + } + + if ((gid_t) d->creds.fsgid != GID_INVALID) { + m->creds.fsgid = (gid_t) d->creds.fsgid; + m->creds.mask |= SD_BUS_CREDS_FSGID & bus->creds_mask; + } + break; case KDBUS_ITEM_TIMESTAMP: @@ -569,9 +661,15 @@ static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) { break; case KDBUS_ITEM_AUDIT: - m->creds.audit_session_id = (uint32_t) d->audit.sessionid; - m->creds.audit_login_uid = (uid_t) d->audit.loginuid; - m->creds.mask |= (SD_BUS_CREDS_AUDIT_SESSION_ID|SD_BUS_CREDS_AUDIT_LOGIN_UID) & bus->creds_mask; + if ((uint32_t) d->audit.sessionid != (uint32_t) -1) { + m->creds.audit_session_id = (uint32_t) d->audit.sessionid; + m->creds.mask |= SD_BUS_CREDS_AUDIT_SESSION_ID & bus->creds_mask; + } + + if ((uid_t) d->audit.loginuid != UID_INVALID) { + m->creds.audit_login_uid = (uid_t) d->audit.loginuid; + m->creds.mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID & bus->creds_mask; + } break; case KDBUS_ITEM_CAPS: @@ -581,24 +679,58 @@ static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) { break; case KDBUS_ITEM_DST_NAME: - if (!service_name_is_valid(d->str)) - return -EBADMSG; + if (!service_name_is_valid(d->str)) { + r = -EBADMSG; + goto fail; + } destination = d->str; break; - case KDBUS_ITEM_NAME: - if (!service_name_is_valid(d->name.name)) - return -EBADMSG; - - r = strv_extend(&m->creds.well_known_names, d->name.name); - if (r < 0) + case KDBUS_ITEM_OWNED_NAME: + if (!service_name_is_valid(d->name.name)) { + r = -EBADMSG; goto fail; + } + + if (bus->creds_mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) { + char **wkn; + size_t n; + + /* We just extend the array here, but + * do not allocate the strings inside + * of it, instead we just point to our + * buffer directly. */ + n = strv_length(m->creds.well_known_names); + wkn = realloc(m->creds.well_known_names, (n + 2) * sizeof(char*)); + if (!wkn) { + r = -ENOMEM; + goto fail; + } + + wkn[n] = d->name.name; + wkn[n+1] = NULL; + m->creds.well_known_names = wkn; + + m->creds.mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES; + } break; - case KDBUS_ITEM_CONN_NAME: - m->creds.conn_name = d->str; - m->creds.mask |= SD_BUS_CREDS_CONNECTION_NAME & bus->creds_mask; + case KDBUS_ITEM_CONN_DESCRIPTION: + m->creds.description = d->str; + m->creds.mask |= SD_BUS_CREDS_DESCRIPTION & bus->creds_mask; + break; + + case KDBUS_ITEM_AUXGROUPS: + + if (bus->creds_mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) { + assert_cc(sizeof(gid_t) == sizeof(uint32_t)); + + m->creds.n_supplementary_gids = (d->size - offsetof(struct kdbus_item, data32)) / sizeof(uint32_t); + m->creds.supplementary_gids = (gid_t*) d->data32; + m->creds.mask |= SD_BUS_CREDS_SUPPLEMENTARY_GIDS; + } + break; case KDBUS_ITEM_FDS: @@ -610,13 +742,45 @@ static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k) { } } + /* If we requested the list of well-known names to be appended + * and the sender had none no item for it will be + * attached. However, this does *not* mean that we the kernel + * didn't want to provide this information to us. Hence, let's + * explicitly mark this information as available if it was + * requested. */ + m->creds.mask |= bus->creds_mask & SD_BUS_CREDS_WELL_KNOWN_NAMES; + r = bus_message_parse_fields(m); if (r < 0) goto fail; + /* Refuse messages if kdbus and dbus1 cookie doesn't match up */ + if ((uint64_t) m->header->serial != k->cookie) { + r = -EBADMSG; + goto fail; + } + + /* Refuse messages where the reply flag doesn't match up */ + if (!(m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) != !!(k->flags & KDBUS_MSG_FLAGS_EXPECT_REPLY)) { + r = -EBADMSG; + goto fail; + } + + /* Refuse reply messages where the reply cookie doesn't match up */ + if ((m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED) && m->reply_cookie != k->cookie_reply) { + r = -EBADMSG; + goto fail; + } + + /* Refuse messages where the autostart flag doesn't match up */ + if (!(m->header->flags & BUS_MESSAGE_NO_AUTO_START) != !(k->flags & KDBUS_MSG_FLAGS_NO_AUTO_START)) { + r = -EBADMSG; + goto fail; + } + /* Override information from the user header with data from the kernel */ if (k->src_id == KDBUS_SRC_ID_KERNEL) - m->sender = m->creds.unique_name = (char*) "org.freedesktop.DBus"; + bus_message_set_sender_driver(bus, m); else { snprintf(m->sender_buffer, sizeof(m->sender_buffer), ":1.%llu", (unsigned long long) k->src_id); m->sender = m->creds.unique_name = m->sender_buffer; @@ -665,8 +829,8 @@ int bus_kernel_take_fd(sd_bus *b) { b->use_memfd = 1; - if (b->connection_name) { - g = bus_label_escape(b->connection_name); + if (b->description) { + g = bus_label_escape(b->description); if (!g) return -ENOMEM; @@ -699,8 +863,8 @@ int bus_kernel_take_fd(sd_bus *b) { name = g; } - b->connection_name = bus_label_unescape(name); - if (!b->connection_name) + b->description = bus_label_unescape(name); + if (!b->description) return -ENOMEM; } @@ -712,6 +876,9 @@ int bus_kernel_take_fd(sd_bus *b) { if (b->fake_creds_valid) sz += ALIGN8(offsetof(struct kdbus_item, creds) + sizeof(struct kdbus_creds)); + if (b->fake_pids_valid) + sz += ALIGN8(offsetof(struct kdbus_item, pids) + sizeof(struct kdbus_pids)); + if (b->fake_label) { l = strlen(b->fake_label); sz += ALIGN8(offsetof(struct kdbus_item, str) + l + 1); @@ -720,13 +887,14 @@ int bus_kernel_take_fd(sd_bus *b) { hello = alloca0_align(sz, 8); hello->size = sz; hello->flags = b->hello_flags; - hello->attach_flags = b->attach_flags; + hello->attach_flags_send = _KDBUS_ATTACH_ANY; + hello->attach_flags_recv = b->attach_flags; hello->pool_size = KDBUS_POOL_SIZE; item = hello->items; item->size = offsetof(struct kdbus_item, str) + m + 1; - item->type = KDBUS_ITEM_CONN_NAME; + item->type = KDBUS_ITEM_CONN_DESCRIPTION; memcpy(item->str, name, m + 1); item = KDBUS_ITEM_NEXT(item); @@ -738,6 +906,14 @@ int bus_kernel_take_fd(sd_bus *b) { item = KDBUS_ITEM_NEXT(item); } + if (b->fake_pids_valid) { + item->size = offsetof(struct kdbus_item, pids) + sizeof(struct kdbus_pids); + item->type = KDBUS_ITEM_PIDS; + item->pids = b->fake_pids; + + item = KDBUS_ITEM_NEXT(item); + } + if (b->fake_label) { item->size = offsetof(struct kdbus_item, str) + l + 1; item->type = KDBUS_ITEM_SECLABEL; @@ -802,25 +978,37 @@ int bus_kernel_connect(sd_bus *b) { return bus_kernel_take_fd(b); } +int bus_kernel_cmd_free(sd_bus *bus, uint64_t offset) { + struct kdbus_cmd_free cmd = { + .flags = 0, + .offset = offset, + }; + int r; + + assert(bus); + assert(bus->is_kernel); + + r = ioctl(bus->input_fd, KDBUS_CMD_FREE, &cmd); + if (r < 0) + return -errno; + + return 0; +} + static void close_kdbus_msg(sd_bus *bus, struct kdbus_msg *k) { - struct kdbus_cmd_free cmd; struct kdbus_item *d; assert(bus); assert(k); - cmd.flags = 0; - cmd.offset = (uint8_t *)k - (uint8_t *)bus->kdbus_buffer; - KDBUS_ITEM_FOREACH(d, k, items) { - if (d->type == KDBUS_ITEM_FDS) close_many(d->fds, (d->size - offsetof(struct kdbus_item, fds)) / sizeof(int)); else if (d->type == KDBUS_ITEM_PAYLOAD_MEMFD) safe_close(d->memfd.fd); } - (void) ioctl(bus->input_fd, KDBUS_CMD_FREE, &cmd); + bus_kernel_cmd_free(bus, (uint8_t*) k - (uint8_t*) bus->kdbus_buffer); } int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m, bool hint_sync_call) { @@ -908,7 +1096,7 @@ int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m, bool hint_sync_call /* Anybody can send us invalid messages, let's just drop them. */ if (r == -EBADMSG || r == -EPROTOTYPE) - log_debug("Ignoring invalid message: %s", strerror(-r)); + log_debug_errno(r, "Ignoring invalid message: %m"); else return r; } @@ -940,7 +1128,7 @@ static int push_name_owner_changed(sd_bus *bus, const char *name, const char *ol if (r < 0) return r; - m->sender = "org.freedesktop.DBus"; + bus_message_set_sender_driver(bus, m); r = bus_seal_synthetic_message(bus, m); if (r < 0) @@ -1009,7 +1197,7 @@ static int translate_reply(sd_bus *bus, struct kdbus_msg *k, struct kdbus_item * if (r < 0) return r; - m->sender = "org.freedesktop.DBus"; + bus_message_set_sender_driver(bus, m); r = bus_seal_synthetic_message(bus, m); if (r < 0) @@ -1078,6 +1266,11 @@ int bus_kernel_read_message(sd_bus *bus, bool hint_priority, int64_t priority) { if (errno == EAGAIN) return 0; + if (errno == EOVERFLOW) { + log_debug("%s: kdbus reports %" PRIu64 " dropped broadcast messages, ignoring.", strna(bus->description), (uint64_t) recv.dropped_msgs); + return 0; + } + return -errno; } @@ -1087,7 +1280,7 @@ int bus_kernel_read_message(sd_bus *bus, bool hint_priority, int64_t priority) { /* Anybody can send us invalid messages, let's just drop them. */ if (r == -EBADMSG || r == -EPROTOTYPE) { - log_debug("Ignoring invalid message: %s", strerror(-r)); + log_debug_errno(r, "Ignoring invalid message: %m"); r = 0; } @@ -1118,20 +1311,13 @@ int bus_kernel_pop_memfd(sd_bus *bus, void **address, size_t *mapped, size_t *al assert_se(pthread_mutex_lock(&bus->memfd_cache_mutex) >= 0); if (bus->n_memfd_cache <= 0) { - _cleanup_free_ char *g = NULL; int r; assert_se(pthread_mutex_unlock(&bus->memfd_cache_mutex) >= 0); - assert(bus->connection_name); - - g = bus_label_escape(bus->connection_name); - if (!g) - return -ENOMEM; - - r = memfd_create(g, MFD_ALLOW_SEALING); + r = memfd_new(bus->description); if (r < 0) - return -errno; + return r; *address = NULL; *mapped = 0; @@ -1188,7 +1374,7 @@ void bus_kernel_push_memfd(sd_bus *bus, int fd, void *address, size_t mapped, si /* If overly long, let's return a bit to the OS */ if (mapped > max_mapped) { - assert_se(ftruncate(fd, max_mapped) >= 0); + assert_se(memfd_set_size(fd, max_mapped) >= 0); assert_se(munmap((uint8_t*) address + max_mapped, PAGE_ALIGN(mapped - max_mapped)) >= 0); c->mapped = c->allocated = max_mapped; } else { @@ -1208,11 +1394,9 @@ void bus_kernel_flush_memfd(sd_bus *b) { close_and_munmap(b->memfd_cache[i].fd, b->memfd_cache[i].address, b->memfd_cache[i].mapped); } -int kdbus_translate_request_name_flags(uint64_t flags, uint64_t *kdbus_flags) { +uint64_t request_name_flags_to_kdbus(uint64_t flags) { uint64_t f = 0; - assert(kdbus_flags); - if (flags & SD_BUS_NAME_ALLOW_REPLACEMENT) f |= KDBUS_NAME_ALLOW_REPLACEMENT; @@ -1222,18 +1406,19 @@ int kdbus_translate_request_name_flags(uint64_t flags, uint64_t *kdbus_flags) { if (flags & SD_BUS_NAME_QUEUE) f |= KDBUS_NAME_QUEUE; - *kdbus_flags = f; - return 0; + return f; } -int kdbus_translate_attach_flags(uint64_t mask, uint64_t *kdbus_mask) { +uint64_t attach_flags_to_kdbus(uint64_t mask) { uint64_t m = 0; - assert(kdbus_mask); - - if (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_GID|SD_BUS_CREDS_PID|SD_BUS_CREDS_PID_STARTTIME|SD_BUS_CREDS_TID)) + if (mask & (SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_SUID|SD_BUS_CREDS_FSUID| + SD_BUS_CREDS_GID|SD_BUS_CREDS_EGID|SD_BUS_CREDS_SGID|SD_BUS_CREDS_FSGID)) m |= KDBUS_ATTACH_CREDS; + if (mask & (SD_BUS_CREDS_PID|SD_BUS_CREDS_TID)) + m |= KDBUS_ATTACH_PIDS; + if (mask & SD_BUS_CREDS_COMM) m |= KDBUS_ATTACH_PID_COMM; @@ -1261,38 +1446,42 @@ int kdbus_translate_attach_flags(uint64_t mask, uint64_t *kdbus_mask) { if (mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) m |= KDBUS_ATTACH_NAMES; - if (mask & SD_BUS_CREDS_CONNECTION_NAME) - m |= KDBUS_ATTACH_CONN_NAME; + if (mask & SD_BUS_CREDS_DESCRIPTION) + m |= KDBUS_ATTACH_CONN_DESCRIPTION; - *kdbus_mask = m; - return 0; + if (mask & SD_BUS_CREDS_SUPPLEMENTARY_GIDS) + m |= KDBUS_ATTACH_AUXGROUPS; + + return m; } int bus_kernel_create_bus(const char *name, bool world, char **s) { struct kdbus_cmd_make *make; struct kdbus_item *n; + size_t l; int fd; assert(name); assert(s); - fd = open("/dev/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC); + fd = open("/sys/fs/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC); if (fd < 0) return -errno; - make = alloca0_align(ALIGN8(offsetof(struct kdbus_cmd_make, items) + - offsetof(struct kdbus_item, data64) + sizeof(uint64_t) + - offsetof(struct kdbus_item, str) + - DECIMAL_STR_MAX(uid_t) + 1 + strlen(name) + 1), + l = strlen(name); + make = alloca0_align(offsetof(struct kdbus_cmd_make, items) + + ALIGN8(offsetof(struct kdbus_item, bloom_parameter) + sizeof(struct kdbus_bloom_parameter)) + + ALIGN8(offsetof(struct kdbus_item, data64) + sizeof(uint64_t)) + + ALIGN8(offsetof(struct kdbus_item, str) + DECIMAL_STR_MAX(uid_t) + 1 + l + 1), 8); make->size = offsetof(struct kdbus_cmd_make, items); + /* Set the bloom parameters */ n = make->items; n->size = offsetof(struct kdbus_item, bloom_parameter) + sizeof(struct kdbus_bloom_parameter); n->type = KDBUS_ITEM_BLOOM_PARAMETER; - n->bloom_parameter.size = DEFAULT_BLOOM_SIZE; n->bloom_parameter.n_hash = DEFAULT_BLOOM_N_HASH; @@ -1301,6 +1490,15 @@ int bus_kernel_create_bus(const char *name, bool world, char **s) { make->size += ALIGN8(n->size); + /* The busses we create make no restrictions on what metadata + * peers can read from incoming messages. */ + n = KDBUS_ITEM_NEXT(n); + n->type = KDBUS_ITEM_ATTACH_FLAGS_RECV; + n->size = offsetof(struct kdbus_item, data64) + sizeof(uint64_t); + n->data64[0] = _KDBUS_ATTACH_ANY; + make->size += ALIGN8(n->size); + + /* Set the a good name */ n = KDBUS_ITEM_NEXT(n); sprintf(n->str, UID_FMT "-%s", getuid(), name); n->size = offsetof(struct kdbus_item, str) + strlen(n->str) + 1; @@ -1317,7 +1515,7 @@ int bus_kernel_create_bus(const char *name, bool world, char **s) { if (s) { char *p; - p = strjoin("/dev/kdbus/", n->str, "/bus", NULL); + p = strjoin("/sys/fs/kdbus/", n->str, "/bus", NULL); if (!p) { safe_close(fd); return -ENOMEM; @@ -1397,20 +1595,29 @@ int bus_kernel_open_bus_fd(const char *bus, char **path) { int fd; size_t len; - len = strlen("/dev/kdbus/") + DECIMAL_STR_MAX(uid_t) + 1 + strlen(bus) + strlen("/bus") + 1; + assert(bus); + + len = strlen("/sys/fs/kdbus/") + DECIMAL_STR_MAX(uid_t) + 1 + strlen(bus) + strlen("/bus") + 1; if (path) { - p = malloc(len); + p = new(char, len); if (!p) return -ENOMEM; - *path = p; } else - p = alloca(len); - sprintf(p, "/dev/kdbus/" UID_FMT "-%s/bus", getuid(), bus); + p = newa(char, len); + + sprintf(p, "/sys/fs/kdbus/" UID_FMT "-%s/bus", getuid(), bus); fd = open(p, O_RDWR|O_NOCTTY|O_CLOEXEC); - if (fd < 0) + if (fd < 0) { + if (path) + free(p); + return -errno; + } + + if (path) + *path = p; return fd; } @@ -1419,25 +1626,25 @@ int bus_kernel_create_endpoint(const char *bus_name, const char *ep_name, char * _cleanup_free_ char *path = NULL; struct kdbus_cmd_make *make; struct kdbus_item *n; - size_t size; + const char *name; int fd; fd = bus_kernel_open_bus_fd(bus_name, &path); if (fd < 0) return fd; - size = ALIGN8(offsetof(struct kdbus_cmd_make, items)); - size += ALIGN8(offsetof(struct kdbus_item, str) + strlen(ep_name) + 1); - - make = alloca0_align(size, 8); - make->size = size; + make = alloca0_align(ALIGN8(offsetof(struct kdbus_cmd_make, items)) + + ALIGN8(offsetof(struct kdbus_item, str) + DECIMAL_STR_MAX(uid_t) + 1 + strlen(ep_name) + 1), + 8); + make->size = ALIGN8(offsetof(struct kdbus_cmd_make, items)); make->flags = KDBUS_MAKE_ACCESS_WORLD; n = make->items; - + sprintf(n->str, UID_FMT "-%s", getuid(), ep_name); + n->size = offsetof(struct kdbus_item, str) + strlen(n->str) + 1; n->type = KDBUS_ITEM_MAKE_NAME; - n->size = offsetof(struct kdbus_item, str) + strlen(ep_name) + 1; - strcpy(n->str, ep_name); + make->size += ALIGN8(n->size); + name = n->str; if (ioctl(fd, KDBUS_CMD_ENDPOINT_MAKE, make) < 0) { safe_close(fd); @@ -1447,7 +1654,7 @@ int bus_kernel_create_endpoint(const char *bus_name, const char *ep_name, char * if (ep_path) { char *p; - p = strjoin(dirname(path), "/", ep_name, NULL); + p = strjoin(dirname(path), "/", name, NULL); if (!p) { safe_close(fd); return -ENOMEM; @@ -1527,7 +1734,7 @@ int bus_kernel_make_starter( if (world_policy >= 0) policy_cnt++; - size = ALIGN8(offsetof(struct kdbus_cmd_hello, items)) + + size = offsetof(struct kdbus_cmd_hello, items) + ALIGN8(offsetof(struct kdbus_item, str) + strlen(name) + 1) + policy_cnt * ALIGN8(offsetof(struct kdbus_item, policy_access) + sizeof(struct kdbus_policy_access)); @@ -1562,7 +1769,8 @@ int bus_kernel_make_starter( (activating ? KDBUS_HELLO_ACTIVATOR : KDBUS_HELLO_POLICY_HOLDER) | (accept_fd ? KDBUS_HELLO_ACCEPT_FD : 0); hello->pool_size = KDBUS_POOL_SIZE; - hello->attach_flags = _KDBUS_ATTACH_ALL; + hello->attach_flags_send = _KDBUS_ATTACH_ANY; + hello->attach_flags_recv = _KDBUS_ATTACH_ANY; if (ioctl(fd, KDBUS_CMD_HELLO, hello) < 0) return -errno; @@ -1578,77 +1786,122 @@ int bus_kernel_make_starter( return fd; } -int bus_kernel_create_domain(const char *name, char **s) { - struct kdbus_cmd_make *make; - struct kdbus_item *n; - int fd; +int bus_kernel_try_close(sd_bus *bus) { + assert(bus); + assert(bus->is_kernel); - assert(name); - assert(s); + if (ioctl(bus->input_fd, KDBUS_CMD_BYEBYE) < 0) + return -errno; - fd = open("/dev/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC); - if (fd < 0) + return 0; +} + +int bus_kernel_drop_one(int fd) { + struct kdbus_cmd_recv recv = { + .flags = KDBUS_RECV_DROP + }; + + assert(fd >= 0); + + if (ioctl(fd, KDBUS_CMD_MSG_RECV, &recv) < 0) return -errno; - make = alloca0_align(ALIGN8(offsetof(struct kdbus_cmd_make, items) + - offsetof(struct kdbus_item, str) + - strlen(name) + 1), - 8); + return 0; +} - n = make->items; - strcpy(n->str, name); - n->size = offsetof(struct kdbus_item, str) + strlen(n->str) + 1; - n->type = KDBUS_ITEM_MAKE_NAME; +int bus_kernel_realize_attach_flags(sd_bus *bus) { + struct kdbus_cmd_update *update; + struct kdbus_item *n; - make->size = ALIGN8(offsetof(struct kdbus_cmd_make, items) + n->size); - make->flags = KDBUS_MAKE_ACCESS_WORLD; + assert(bus); + assert(bus->is_kernel); - if (ioctl(fd, KDBUS_CMD_DOMAIN_MAKE, make) < 0) { - safe_close(fd); + update = alloca0_align(offsetof(struct kdbus_cmd_update, items) + + ALIGN8(offsetof(struct kdbus_item, data64) + sizeof(uint64_t)), + 8); + + n = update->items; + n->type = KDBUS_ITEM_ATTACH_FLAGS_RECV; + n->size = offsetof(struct kdbus_item, data64) + sizeof(uint64_t); + n->data64[0] = bus->attach_flags; + + update->size = + offsetof(struct kdbus_cmd_update, items) + + ALIGN8(n->size); + + if (ioctl(bus->input_fd, KDBUS_CMD_CONN_UPDATE, update) < 0) return -errno; - } - /* The higher 32bit of the flags field are considered - * 'incompatible flags'. Refuse them all for now. */ - if (make->flags > 0xFFFFFFFFULL) { - safe_close(fd); - return -ENOTSUP; - } + return 0; +} - if (s) { - char *p; +int bus_kernel_fix_attach_mask(void) { + _cleanup_free_ char *mask = NULL; + uint64_t m = (uint64_t) -1; + char buf[2+16+2]; + int r; - p = strappend("/dev/kdbus/domain/", name); - if (!p) { - safe_close(fd); - return -ENOMEM; - } + /* By default we don't want any kdbus metadata fields to be + * suppressed, hence we reset the kernel mask for it to + * (uint64_t) -1. This is overridable via a kernel command + * line option, however. */ - *s = p; + r = get_proc_cmdline_key("systemd.kdbus_attach_flags_mask=", &mask); + if (r < 0) + return log_warning_errno(r, "Failed to read kernel command line: %m"); + + if (mask) { + const char *p = mask; + + if (startswith(p, "0x")) + p += 2; + + if (sscanf(p, "%" PRIx64, &m) != 1) + log_warning("Couldn't parse systemd.kdbus_attach_flags_mask= kernel command line parameter."); } - return fd; + sprintf(buf, "0x%" PRIx64 "\n", m); + r = write_string_file("/sys/module/kdbus/parameters/attach_flags_mask", buf); + if (r < 0) + return log_full_errno( + IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to write kdbus attach mask: %m"); + + return 0; } -int bus_kernel_try_close(sd_bus *bus) { +int bus_kernel_get_bus_name(sd_bus *bus, char **name) { + struct kdbus_cmd_info cmd = { + .size = sizeof(struct kdbus_cmd_info), + }; + struct kdbus_info *info; + struct kdbus_item *item; + char *n = NULL; + int r; + assert(bus); + assert(name); assert(bus->is_kernel); - if (ioctl(bus->input_fd, KDBUS_CMD_BYEBYE) < 0) + r = ioctl(bus->input_fd, KDBUS_CMD_BUS_CREATOR_INFO, &cmd); + if (r < 0) return -errno; - return 0; -} + info = (struct kdbus_info*) ((uint8_t*) bus->kdbus_buffer + cmd.offset); -int bus_kernel_drop_one(int fd) { - struct kdbus_cmd_recv recv = { - .flags = KDBUS_RECV_DROP - }; + KDBUS_ITEM_FOREACH(item, info, items) + if (item->type == KDBUS_ITEM_MAKE_NAME) { + r = free_and_strdup(&n, item->str); + break; + } - assert(fd >= 0); + bus_kernel_cmd_free(bus, cmd.offset); - if (ioctl(fd, KDBUS_CMD_MSG_RECV, &recv) < 0) - return -errno; + if (r < 0) + return r; + if (!n) + return -EIO; + *name = n; return 0; } diff --git a/src/libsystemd/sd-bus/bus-kernel.h b/src/libsystemd/sd-bus/bus-kernel.h index f1d832a764..2152f62d12 100644 --- a/src/libsystemd/sd-bus/bus-kernel.h +++ b/src/libsystemd/sd-bus/bus-kernel.h @@ -71,7 +71,6 @@ int bus_kernel_make_starter(int fd, const char *name, bool activating, bool acce int bus_kernel_create_bus(const char *name, bool world, char **s); int bus_kernel_create_endpoint(const char *bus_name, const char *ep_name, char **path); -int bus_kernel_create_domain(const char *name, char **s); int bus_kernel_set_endpoint_policy(int fd, uid_t uid, BusEndpoint *ep); @@ -82,9 +81,17 @@ void bus_kernel_flush_memfd(sd_bus *bus); int bus_kernel_parse_unique_name(const char *s, uint64_t *id); -int kdbus_translate_request_name_flags(uint64_t sd_bus_flags, uint64_t *kdbus_flags); -int kdbus_translate_attach_flags(uint64_t sd_bus_flags, uint64_t *kdbus_flags); +uint64_t request_name_flags_to_kdbus(uint64_t sd_bus_flags); +uint64_t attach_flags_to_kdbus(uint64_t sd_bus_flags); int bus_kernel_try_close(sd_bus *bus); int bus_kernel_drop_one(int fd); + +int bus_kernel_realize_attach_flags(sd_bus *bus); + +int bus_kernel_fix_attach_mask(void); + +int bus_kernel_get_bus_name(sd_bus *bus, char **name); + +int bus_kernel_cmd_free(sd_bus *bus, uint64_t offset); diff --git a/src/libsystemd/sd-bus/bus-match.c b/src/libsystemd/sd-bus/bus-match.c index 18afe0f12a..3a31aa0ebf 100644 --- a/src/libsystemd/sd-bus/bus-match.c +++ b/src/libsystemd/sd-bus/bus-match.c @@ -134,6 +134,7 @@ static bool value_node_test( enum bus_match_node_type parent_type, uint8_t value_u8, const char *value_str, + char **value_strv, sd_bus_message *m) { assert(node); @@ -179,17 +180,46 @@ static bool value_node_test( case BUS_MATCH_INTERFACE: case BUS_MATCH_MEMBER: case BUS_MATCH_PATH: - case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST: - return streq_ptr(node->value.str, value_str); + case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST: { + char **i; - case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST: - return namespace_simple_pattern(node->value.str, value_str); + if (value_str) + return streq_ptr(node->value.str, value_str); + + STRV_FOREACH(i, value_strv) + if (streq_ptr(node->value.str, *i)) + return true; + + return false; + } + + case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST: { + char **i; + + if (value_str) + return namespace_simple_pattern(node->value.str, value_str); + + STRV_FOREACH(i, value_strv) + if (namespace_simple_pattern(node->value.str, *i)) + return true; + return false; + } case BUS_MATCH_PATH_NAMESPACE: return path_simple_pattern(node->value.str, value_str); - case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST: - return path_complex_pattern(node->value.str, value_str); + case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST: { + char **i; + + if (value_str) + return path_complex_pattern(node->value.str, value_str); + + STRV_FOREACH(i, value_strv) + if (path_complex_pattern(node->value.str, *i)) + return true; + + return false; + } default: assert_not_reached("Invalid node type"); @@ -235,7 +265,7 @@ int bus_match_run( struct bus_match_node *node, sd_bus_message *m) { - + _cleanup_strv_free_ char **test_strv = NULL; const char *test_str = NULL; uint8_t test_u8 = 0; int r; @@ -343,15 +373,15 @@ int bus_match_run( break; case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST: - test_str = bus_message_get_arg(m, node->type - BUS_MATCH_ARG); + (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG, &test_str, &test_strv); break; case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST: - test_str = bus_message_get_arg(m, node->type - BUS_MATCH_ARG_PATH); + (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_PATH, &test_str, &test_strv); break; case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST: - test_str = bus_message_get_arg(m, node->type - BUS_MATCH_ARG_NAMESPACE); + (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_NAMESPACE, &test_str, &test_strv); break; default: @@ -365,7 +395,20 @@ int bus_match_run( if (test_str) found = hashmap_get(node->compare.children, test_str); - else if (node->type == BUS_MATCH_MESSAGE_TYPE) + else if (test_strv) { + char **i; + + STRV_FOREACH(i, test_strv) { + found = hashmap_get(node->compare.children, *i); + if (found) { + r = bus_match_run(bus, found, m); + if (r != 0) + return r; + } + } + + found = NULL; + } else if (node->type == BUS_MATCH_MESSAGE_TYPE) found = hashmap_get(node->compare.children, UINT_TO_PTR(test_u8)); else found = NULL; @@ -381,7 +424,7 @@ int bus_match_run( /* No hash table, so let's iterate manually... */ for (c = node->child; c; c = c->next) { - if (!value_node_test(c, node->type, test_u8, test_str, m)) + if (!value_node_test(c, node->type, test_u8, test_str, test_strv, m)) continue; r = bus_match_run(bus, c, m); @@ -537,7 +580,7 @@ static int bus_match_find_compare_value( else if (BUS_MATCH_CAN_HASH(t)) n = hashmap_get(c->compare.children, value_str); else { - for (n = c->child; !value_node_same(n, t, value_u8, value_str); n = n->next) + for (n = c->child; n && !value_node_same(n, t, value_u8, value_str); n = n->next) ; } @@ -748,6 +791,9 @@ int bus_match_parse( bool escaped = false, quoted; uint8_t u; + /* Avahi's match rules appear to include whitespace, skip over it */ + p += strspn(p, " "); + eq = strchr(p, '='); if (!eq) return -EINVAL; diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c index 1362a60f08..06d8d770fd 100644 --- a/src/libsystemd/sd-bus/bus-message.c +++ b/src/libsystemd/sd-bus/bus-message.c @@ -28,7 +28,7 @@ #include "strv.h" #include "time-util.h" #include "cgroup-util.h" -#include "memfd.h" +#include "memfd-util.h" #include "sd-bus.h" #include "bus-message.h" @@ -69,13 +69,13 @@ static void message_free_part(sd_bus_message *m, struct bus_body_part *part) { bus_kernel_push_memfd(m->bus, part->memfd, part->data, part->mapped, part->allocated); else { if (part->mapped > 0) - assert_se(munmap(part->data, part->mapped) == 0); + assert_se(munmap(part->mmap_begin, part->mapped) == 0); safe_close(part->memfd); } } else if (part->munmap_this) - munmap(part->data, part->mapped); + munmap(part->mmap_begin, part->mapped); else if (part->free_this) free(part->data); @@ -148,6 +148,11 @@ static void message_free(sd_bus_message *m) { if (m->iovec != m->iovec_fixed) free(m->iovec); + if (m->destination_ptr) { + free(m->destination_ptr); + m->destination_ptr = NULL; + } + message_reset_containers(m); free(m->root_container.signature); free(m->root_container.offsets); @@ -415,10 +420,20 @@ int bus_message_from_header( m->n_fds = n_fds; if (ucred) { - m->creds.uid = ucred->uid; m->creds.pid = ucred->pid; + m->creds.uid = ucred->uid; m->creds.gid = ucred->gid; - m->creds.mask |= SD_BUS_CREDS_UID | SD_BUS_CREDS_PID | SD_BUS_CREDS_GID; + + /* Due to namespace translations some data might be + * missing from this ucred record. */ + if (m->creds.pid > 0) + m->creds.mask |= SD_BUS_CREDS_PID; + + if (m->creds.uid != UID_INVALID) + m->creds.mask |= SD_BUS_CREDS_UID; + + if (m->creds.gid != GID_INVALID) + m->creds.mask |= SD_BUS_CREDS_GID; } if (label) { @@ -1097,10 +1112,10 @@ static int part_make_space( uint64_t new_allocated; new_allocated = PAGE_ALIGN(sz > 0 ? 2 * sz : 1); - r = ftruncate(part->memfd, new_allocated); + r = memfd_set_size(part->memfd, new_allocated); if (r < 0) { m->poisoned = true; - return -errno; + return r; } part->allocated = new_allocated; @@ -1113,15 +1128,16 @@ static int part_make_space( if (part->mapped <= 0) n = mmap(NULL, psz, PROT_READ|PROT_WRITE, MAP_SHARED, part->memfd, 0); else - n = mremap(part->data, part->mapped, psz, MREMAP_MAYMOVE); + n = mremap(part->mmap_begin, part->mapped, psz, MREMAP_MAYMOVE); if (n == MAP_FAILED) { m->poisoned = true; return -errno; } + part->mmap_begin = part->data = n; part->mapped = psz; - part->data = n; + part->memfd_offset = 0; } part->munmap_this = true; @@ -2048,6 +2064,7 @@ static int bus_message_close_variant(sd_bus_message *m, struct bus_container *c) assert(m); assert(c); + assert(c->signature); if (!BUS_MESSAGE_IS_GVARIANT(m)) return 0; @@ -2487,10 +2504,11 @@ _public_ int sd_bus_message_append_array_space( return 0; } -_public_ int sd_bus_message_append_array(sd_bus_message *m, - char type, - const void *ptr, - size_t size) { +_public_ int sd_bus_message_append_array( + sd_bus_message *m, + char type, + const void *ptr, + size_t size) { int r; void *p; @@ -2546,26 +2564,26 @@ _public_ int sd_bus_message_append_array_iovec( return 0; } -_public_ int sd_bus_message_append_array_memfd(sd_bus_message *m, - char type, - int memfd) { +_public_ int sd_bus_message_append_array_memfd( + sd_bus_message *m, + char type, + int memfd, + uint64_t offset, + uint64_t size) { + _cleanup_close_ int copy_fd = -1; struct bus_body_part *part; ssize_t align, sz; - uint64_t size; + uint64_t real_size; void *a; int r; - if (!m) - return -EINVAL; - if (memfd < 0) - return -EINVAL; - if (m->sealed) - return -EPERM; - if (!bus_type_is_trivial(type)) - return -EINVAL; - if (m->poisoned) - return -ESTALE; + assert_return(m, -EINVAL); + assert_return(memfd >= 0, -EINVAL); + assert_return(bus_type_is_trivial(type), -EINVAL); + assert_return(size > 0, -EINVAL); + assert_return(!m->sealed, -EPERM); + assert_return(!m->poisoned, -ESTALE); r = memfd_set_sealed(memfd); if (r < 0) @@ -2575,16 +2593,24 @@ _public_ int sd_bus_message_append_array_memfd(sd_bus_message *m, if (copy_fd < 0) return copy_fd; - r = memfd_get_size(memfd, &size); + r = memfd_get_size(memfd, &real_size); if (r < 0) return r; + if (offset == 0 && size == (uint64_t) -1) + size = real_size; + else if (offset + size > real_size) + return -EMSGSIZE; + align = bus_type_get_alignment(type); sz = bus_type_get_size(type); assert_se(align > 0); assert_se(sz > 0); + if (offset % align != 0) + return -EINVAL; + if (size % sz != 0) return -EINVAL; @@ -2604,6 +2630,7 @@ _public_ int sd_bus_message_append_array_memfd(sd_bus_message *m, return -ENOMEM; part->memfd = copy_fd; + part->memfd_offset = offset; part->sealed = true; part->size = size; copy_fd = -1; @@ -2614,16 +2641,22 @@ _public_ int sd_bus_message_append_array_memfd(sd_bus_message *m, return sd_bus_message_close_container(m); } -_public_ int sd_bus_message_append_string_memfd(sd_bus_message *m, int memfd) { +_public_ int sd_bus_message_append_string_memfd( + sd_bus_message *m, + int memfd, + uint64_t offset, + uint64_t size) { + _cleanup_close_ int copy_fd = -1; struct bus_body_part *part; struct bus_container *c; - uint64_t size; + uint64_t real_size; void *a; int r; assert_return(m, -EINVAL); assert_return(memfd >= 0, -EINVAL); + assert_return(size > 0, -EINVAL); assert_return(!m->sealed, -EPERM); assert_return(!m->poisoned, -ESTALE); @@ -2635,10 +2668,15 @@ _public_ int sd_bus_message_append_string_memfd(sd_bus_message *m, int memfd) { if (copy_fd < 0) return copy_fd; - r = memfd_get_size(memfd, &size); + r = memfd_get_size(memfd, &real_size); if (r < 0) return r; + if (offset == 0 && size == (uint64_t) -1) + size = real_size; + else if (offset + size > real_size) + return -EMSGSIZE; + /* We require this to be NUL terminated */ if (size == 0) return -EINVAL; @@ -2679,6 +2717,7 @@ _public_ int sd_bus_message_append_string_memfd(sd_bus_message *m, int memfd) { return -ENOMEM; part->memfd = copy_fd; + part->memfd_offset = offset; part->sealed = true; part->size = size; copy_fd = -1; @@ -2820,11 +2859,12 @@ int bus_message_seal(sd_bus_message *m, uint64_t cookie, usec_t timeout) { /* Then, sync up real memfd size */ sz = part->size; - if (ftruncate(part->memfd, sz) < 0) - return -errno; + r = memfd_set_size(part->memfd, sz); + if (r < 0) + return r; /* Finally, try to seal */ - if (fcntl(part->memfd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE) >= 0) + if (memfd_set_sealed(part->memfd) >= 0) part->sealed = true; } } @@ -2841,7 +2881,7 @@ int bus_message_seal(sd_bus_message *m, uint64_t cookie, usec_t timeout) { int bus_body_part_map(struct bus_body_part *part) { void *p; - size_t psz; + size_t psz, shift; assert_se(part); @@ -2858,10 +2898,11 @@ int bus_body_part_map(struct bus_body_part *part) { return 0; } - psz = PAGE_ALIGN(part->size); + shift = part->memfd_offset - ((part->memfd_offset / page_size()) * page_size()); + psz = PAGE_ALIGN(part->size + shift); if (part->memfd >= 0) - p = mmap(NULL, psz, PROT_READ, MAP_PRIVATE, part->memfd, 0); + p = mmap(NULL, psz, PROT_READ, MAP_PRIVATE, part->memfd, part->memfd_offset - shift); else if (part->is_zero) p = mmap(NULL, psz, PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); else @@ -2871,7 +2912,8 @@ int bus_body_part_map(struct bus_body_part *part) { return -errno; part->mapped = psz; - part->data = p; + part->mmap_begin = p; + part->data = (uint8_t*) p + shift; part->munmap_this = true; return 0; @@ -2884,14 +2926,15 @@ void bus_body_part_unmap(struct bus_body_part *part) { if (part->memfd < 0) return; - if (!part->data) + if (!part->mmap_begin) return; if (!part->munmap_this) return; - assert_se(munmap(part->data, part->mapped) == 0); + assert_se(munmap(part->mmap_begin, part->mapped) == 0); + part->mmap_begin = NULL; part->data = NULL; part->mapped = 0; part->munmap_this = false; @@ -4434,13 +4477,32 @@ _public_ int sd_bus_message_skip(sd_bus_message *m, const char *types) { assert_return(m, -EINVAL); assert_return(m->sealed, -EPERM); - assert_return(types, -EINVAL); - if (isempty(types)) - return 0; + /* If types is NULL, read exactly one element */ + if (!types) { + struct bus_container *c; + size_t l; + + if (message_end_of_signature(m)) + return -ENXIO; + + if (message_end_of_array(m, m->rindex)) + return 0; + + c = message_get_container(m); + + r = signature_element_length(c->signature + c->index, &l); + if (r < 0) + return r; + + types = strndupa(c->signature + c->index, l); + } switch (*types) { + case 0: /* Nothing to drop */ + return 0; + case SD_BUS_TYPE_BYTE: case SD_BUS_TYPE_BOOLEAN: case SD_BUS_TYPE_INT16: @@ -5154,6 +5216,10 @@ int bus_message_parse_fields(sd_bus_message *m) { case SD_BUS_MESSAGE_SIGNAL: if (!m->path || !m->interface || !m->member) return -EBADMSG; + + if (m->reply_cookie != 0) + return -EBADMSG; + break; case SD_BUS_MESSAGE_METHOD_CALL: @@ -5161,6 +5227,9 @@ int bus_message_parse_fields(sd_bus_message *m) { if (!m->path || !m->member) return -EBADMSG; + if (m->reply_cookie != 0) + return -EBADMSG; + break; case SD_BUS_MESSAGE_METHOD_RETURN: @@ -5291,35 +5360,57 @@ _public_ int sd_bus_message_read_strv(sd_bus_message *m, char ***l) { return 1; } -const char* bus_message_get_arg(sd_bus_message *m, unsigned i) { - int r; - const char *t = NULL; +int bus_message_get_arg(sd_bus_message *m, unsigned i, const char **str, char ***strv) { + const char *contents; unsigned j; + char type; + int r; assert(m); + assert(str); + assert(strv); r = sd_bus_message_rewind(m, true); if (r < 0) - return NULL; + return r; - for (j = 0; j <= i; j++) { - char type; + for (j = 0;; j++) { + r = sd_bus_message_peek_type(m, &type, &contents); + if (r < 0) + return r; + if (r == 0) + return -ENXIO; + + /* Don't match against arguments after the first one we don't understand */ + if (!IN_SET(type, SD_BUS_TYPE_STRING, SD_BUS_TYPE_OBJECT_PATH, SD_BUS_TYPE_SIGNATURE) && + !(type == SD_BUS_TYPE_ARRAY && STR_IN_SET(contents, "s", "o", "g"))) + return -ENXIO; - r = sd_bus_message_peek_type(m, &type, NULL); + if (j >= i) + break; + + r = sd_bus_message_skip(m, NULL); if (r < 0) - return NULL; + return r; + } - if (type != SD_BUS_TYPE_STRING && - type != SD_BUS_TYPE_OBJECT_PATH && - type != SD_BUS_TYPE_SIGNATURE) - return NULL; + if (type == SD_BUS_TYPE_ARRAY) { - r = sd_bus_message_read_basic(m, type, &t); + r = sd_bus_message_read_strv(m, strv); if (r < 0) - return NULL; + return r; + + *str = NULL; + + } else { + r = sd_bus_message_read_basic(m, type, str); + if (r < 0) + return r; + + *strv = NULL; } - return t; + return 0; } bool bus_header_is_complete(struct bus_header *h, size_t size) { @@ -5374,6 +5465,18 @@ _public_ const char* sd_bus_message_get_signature(sd_bus_message *m, int complet return strempty(c->signature); } +_public_ int sd_bus_message_is_empty(sd_bus_message *m) { + assert_return(m, -EINVAL); + + return isempty(m->root_container.signature); +} + +_public_ int sd_bus_message_has_signature(sd_bus_message *m, const char *signature) { + assert_return(m, -EINVAL); + + return streq(strempty(m->root_container.signature), strempty(signature)); +} + _public_ int sd_bus_message_copy(sd_bus_message *m, sd_bus_message *source, int all) { bool done_something = false; int r; diff --git a/src/libsystemd/sd-bus/bus-message.h b/src/libsystemd/sd-bus/bus-message.h index df792945b0..4dd280dcf0 100644 --- a/src/libsystemd/sd-bus/bus-message.h +++ b/src/libsystemd/sd-bus/bus-message.h @@ -52,26 +52,14 @@ struct bus_container { char *peeked_signature; }; -struct bus_header { - uint8_t endian; - uint8_t type; - uint8_t flags; - uint8_t version; - uint32_t body_size; - - /* Note that what the bus spec calls "serial" we'll call - "cookie" instead, because we don't want to imply that the - cookie was in any way monotonically increasing. */ - uint32_t serial; - uint32_t fields_size; -} _packed_; - struct bus_body_part { struct bus_body_part *next; void *data; + void *mmap_begin; size_t size; size_t mapped; size_t allocated; + uint64_t memfd_offset; int memfd; bool free_this:1; bool munmap_this:1; @@ -100,6 +88,7 @@ struct sd_bus_message { usec_t realtime; uint64_t seqnum; int64_t priority; + uint64_t verify_destination_id; bool sealed:1; bool dont_send:1; @@ -143,6 +132,7 @@ struct sd_bus_message { char sender_buffer[3 + DECIMAL_STR_MAX(uint64_t) + 1]; char destination_buffer[3 + DECIMAL_STR_MAX(uint64_t) + 1]; + char *destination_ptr; size_t header_offsets[_BUS_MESSAGE_HEADER_MAX]; unsigned n_header_offsets; @@ -221,7 +211,7 @@ int bus_message_from_malloc( const char *label, sd_bus_message **ret); -const char* bus_message_get_arg(sd_bus_message *m, unsigned i); +int bus_message_get_arg(sd_bus_message *m, unsigned i, const char **str, char ***strv); int bus_message_append_ap(sd_bus_message *m, const char *types, va_list ap); diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c index 0ab1119b58..6162d12c1d 100644 --- a/src/libsystemd/sd-bus/bus-objects.c +++ b/src/libsystemd/sd-bus/bus-objects.c @@ -617,6 +617,9 @@ static int property_get_set_callbacks_run( return r; } else { + const char *signature = NULL; + char type = 0; + if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY) return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member); @@ -628,6 +631,13 @@ static int property_get_set_callbacks_run( c->last_iteration = bus->iteration_counter; + r = sd_bus_message_peek_type(m, &type, &signature); + if (r < 0) + return r; + + if (type != 'v' || !streq(strempty(signature), strempty(c->vtable->x.property.signature))) + return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Incorrect parameters for property '%s', expected '%s', got '%s'.", c->member, strempty(c->vtable->x.property.signature), strempty(signature)); + r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature); if (r < 0) return r; @@ -1682,6 +1692,11 @@ static int add_object_vtable_internal( goto fail; } + if (v->flags & SD_BUS_VTABLE_PROPERTY_CONST) { + r = -EINVAL; + goto fail; + } + /* Fall through */ case _SD_BUS_VTABLE_PROPERTY: { diff --git a/src/libsystemd/sd-bus/bus-protocol.h b/src/libsystemd/sd-bus/bus-protocol.h index 75c6ded728..6431dfbff8 100644 --- a/src/libsystemd/sd-bus/bus-protocol.h +++ b/src/libsystemd/sd-bus/bus-protocol.h @@ -23,6 +23,22 @@ #include <endian.h> +/* Packet header */ + +struct bus_header { + uint8_t endian; + uint8_t type; + uint8_t flags; + uint8_t version; + uint32_t body_size; + + /* Note that what the bus spec calls "serial" we'll call + "cookie" instead, because we don't want to imply that the + cookie was in any way monotonically increasing. */ + uint32_t serial; + uint32_t fields_size; +} _packed_; + /* Endianness */ enum { diff --git a/src/libsystemd/sd-bus/bus-slot.c b/src/libsystemd/sd-bus/bus-slot.c index 568a6ed60c..8060e9882c 100644 --- a/src/libsystemd/sd-bus/bus-slot.c +++ b/src/libsystemd/sd-bus/bus-slot.c @@ -208,6 +208,7 @@ _public_ sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot) { } bus_slot_disconnect(slot); + free(slot->description); free(slot); return NULL; @@ -265,3 +266,18 @@ _public_ void* sd_bus_slot_get_current_userdata(sd_bus_slot *slot) { return slot->bus->current_userdata; } + +_public_ int sd_bus_slot_set_description(sd_bus_slot *slot, const char *description) { + assert_return(slot, -EINVAL); + + return free_and_strdup(&slot->description, description); +} + +_public_ int sd_bus_slot_get_description(sd_bus_slot *slot, char **description) { + assert_return(slot, -EINVAL); + assert_return(description, -EINVAL); + assert_return(slot->description, -ENXIO); + + *description = slot->description; + return 0; +} diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c index d124d9aa13..d02994e283 100644 --- a/src/libsystemd/sd-bus/bus-socket.c +++ b/src/libsystemd/sd-bus/bus-socket.c @@ -610,10 +610,10 @@ void bus_socket_setup(sd_bus *b) { /* Enable SO_PASSCRED + SO_PASSEC. We try this on any * socket, just in case. */ enable = !b->bus_client; - setsockopt(b->input_fd, SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable)); + (void)setsockopt(b->input_fd, SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable)); enable = !b->bus_client && (b->attach_flags & KDBUS_ATTACH_SECLABEL); - setsockopt(b->input_fd, SOL_SOCKET, SO_PASSSEC, &enable, sizeof(enable)); + (void)setsockopt(b->input_fd, SOL_SOCKET, SO_PASSSEC, &enable, sizeof(enable)); /* Increase the buffers to 8 MB */ fd_inc_rcvbuf(b->input_fd, SNDBUF_SIZE); diff --git a/src/libsystemd/sd-bus/bus-track.c b/src/libsystemd/sd-bus/bus-track.c index f3b593d2b8..6be8310bbe 100644 --- a/src/libsystemd/sd-bus/bus-track.c +++ b/src/libsystemd/sd-bus/bus-track.c @@ -309,7 +309,7 @@ void bus_track_dispatch(sd_bus_track *track) { r = track->handler(track, track->userdata); if (r < 0) - log_debug("Failed to process track handler: %s", strerror(-r)); + log_debug_errno(r, "Failed to process track handler: %m"); else if (r == 0) bus_track_add_to_queue(track); diff --git a/src/libsystemd/sd-bus/bus-util.c b/src/libsystemd/sd-bus/bus-util.c index 43acf5b959..0f1a89c8e1 100644 --- a/src/libsystemd/sd-bus/bus-util.c +++ b/src/libsystemd/sd-bus/bus-util.c @@ -521,7 +521,7 @@ int bus_open_system_systemd(sd_bus **_bus) { if (r < 0) return r; - r = sd_bus_set_address(bus, KERNEL_SYSTEM_BUS_PATH); + r = sd_bus_set_address(bus, KERNEL_SYSTEM_BUS_ADDRESS); if (r < 0) return r; @@ -574,7 +574,7 @@ int bus_open_user_systemd(sd_bus **_bus) { if (r < 0) return r; - if (asprintf(&bus->address, KERNEL_USER_BUS_FMT, getuid()) < 0) + if (asprintf(&bus->address, KERNEL_USER_BUS_ADDRESS_FMT, getuid()) < 0) return -ENOMEM; bus->bus_client = true; @@ -640,8 +640,15 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) { if (r < 0) return r; - if (all || !isempty(s)) - printf("%s=%s\n", name, s); + if (all || !isempty(s)) { + _cleanup_free_ char *escaped = NULL; + + escaped = xescape(s, "\n"); + if (!escaped) + return -ENOMEM; + + printf("%s=%s\n", name, escaped); + } return 1; } @@ -732,10 +739,16 @@ int bus_print_property(const char *name, sd_bus_message *property, bool all) { return r; while((r = sd_bus_message_read_basic(property, SD_BUS_TYPE_STRING, &str)) > 0) { + _cleanup_free_ char *escaped = NULL; + if (first) printf("%s=", name); - printf("%s%s", first ? "" : " ", str); + escaped = xescape(str, "\n "); + if (!escaped) + return -ENOMEM; + + printf("%s%s", first ? "" : " ", escaped); first = false; } @@ -1250,13 +1263,11 @@ int bus_property_get_ulong( #endif int bus_log_parse_error(int r) { - log_error("Failed to parse bus message: %s", strerror(-r)); - return r; + return log_error_errno(r, "Failed to parse bus message: %m"); } int bus_log_create_error(int r) { - log_error("Failed to create bus message: %s", strerror(-r)); - return r; + return log_error_errno(r, "Failed to create bus message: %m"); } int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) { @@ -1361,7 +1372,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen if (STR_IN_SET(field, "CPUAccounting", "MemoryAccounting", "BlockIOAccounting", - "SendSIGHUP", "SendSIGKILL")) { + "SendSIGHUP", "SendSIGKILL", + "WakeSystem")) { r = parse_boolean(eq); if (r < 0) { @@ -1522,6 +1534,17 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen r = sd_bus_message_append(m, "v", "i", sig); + } else if (streq(field, "AccuracySec")) { + usec_t u; + + r = parse_sec(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 { log_error("Unknown assignment %s.", assignment); return -EINVAL; diff --git a/src/libsystemd/sd-bus/busctl-introspect.c b/src/libsystemd/sd-bus/busctl-introspect.c new file mode 100644 index 0000000000..15c10da7e9 --- /dev/null +++ b/src/libsystemd/sd-bus/busctl-introspect.c @@ -0,0 +1,793 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2014 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 "util.h" +#include "xml.h" +#include "sd-bus-vtable.h" + +#include "busctl-introspect.h" + +#define NODE_DEPTH_MAX 16 + +typedef struct Context { + const XMLIntrospectOps *ops; + void *userdata; + + char *interface_name; + uint64_t interface_flags; + + char *member_name; + char *member_signature; + char *member_result; + uint64_t member_flags; + bool member_writable; + + const char *current; + void *xml_state; +} Context; + +static void context_reset_member(Context *c) { + free(c->member_name); + free(c->member_signature); + free(c->member_result); + + c->member_name = c->member_signature = c->member_result = NULL; + c->member_flags = 0; + c->member_writable = false; +} + +static void context_reset_interface(Context *c) { + free(c->interface_name); + c->interface_name = NULL; + c->interface_flags = 0; + + context_reset_member(c); +} + +static int parse_xml_annotation(Context *context, uint64_t *flags) { + + enum { + STATE_ANNOTATION, + STATE_NAME, + STATE_VALUE + } state = STATE_ANNOTATION; + + _cleanup_free_ char *field = NULL, *value = NULL; + + assert(context); + + for (;;) { + _cleanup_free_ char *name = NULL; + + int t; + + t = xml_tokenize(&context->current, &name, &context->xml_state, NULL); + if (t < 0) { + log_error("XML parse error."); + return t; + } + + if (t == XML_END) { + log_error("Premature end of XML data."); + return -EBADMSG; + } + + switch (state) { + + case STATE_ANNOTATION: + + if (t == XML_ATTRIBUTE_NAME) { + + if (streq_ptr(name, "name")) + state = STATE_NAME; + + else if (streq_ptr(name, "value")) + state = STATE_VALUE; + + else { + log_error("Unexpected <annotation> attribute %s.", name); + return -EBADMSG; + } + + } else if (t == XML_TAG_CLOSE_EMPTY || + (t == XML_TAG_CLOSE && streq_ptr(name, "annotation"))) { + + if (flags) { + if (streq_ptr(field, "org.freedesktop.DBus.Deprecated")) { + + if (streq_ptr(value, "true")) + *flags |= SD_BUS_VTABLE_DEPRECATED; + + } else if (streq_ptr(field, "org.freedesktop.DBus.Method.NoReply")) { + + if (streq_ptr(value, "true")) + *flags |= SD_BUS_VTABLE_METHOD_NO_REPLY; + + } else if (streq_ptr(field, "org.freedesktop.DBus.Property.EmitsChangedSignal")) { + + if (streq_ptr(value, "const")) + *flags = (*flags & ~(SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)) | SD_BUS_VTABLE_PROPERTY_CONST; + else if (streq_ptr(value, "invalidates")) + *flags = (*flags & ~(SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_CONST)) | SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION; + else if (streq_ptr(value, "false")) + *flags = *flags & ~(SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION); + } + } + + return 0; + + } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { + log_error("Unexpected token in <annotation>. (1)"); + return -EINVAL; + } + + break; + + case STATE_NAME: + + if (t == XML_ATTRIBUTE_VALUE) { + free(field); + field = name; + name = NULL; + + state = STATE_ANNOTATION; + } else { + log_error("Unexpected token in <annotation>. (2)"); + return -EINVAL; + } + + break; + + case STATE_VALUE: + + if (t == XML_ATTRIBUTE_VALUE) { + free(value); + value = name; + name = NULL; + + state = STATE_ANNOTATION; + } else { + log_error("Unexpected token in <annotation>. (3)"); + return -EINVAL; + } + + break; + + default: + assert_not_reached("Bad state"); + } + } +} + +static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth) { + + enum { + STATE_NODE, + STATE_NODE_NAME, + STATE_INTERFACE, + STATE_INTERFACE_NAME, + STATE_METHOD, + STATE_METHOD_NAME, + STATE_METHOD_ARG, + STATE_METHOD_ARG_NAME, + STATE_METHOD_ARG_TYPE, + STATE_METHOD_ARG_DIRECTION, + STATE_SIGNAL, + STATE_SIGNAL_NAME, + STATE_SIGNAL_ARG, + STATE_SIGNAL_ARG_NAME, + STATE_SIGNAL_ARG_TYPE, + STATE_PROPERTY, + STATE_PROPERTY_NAME, + STATE_PROPERTY_TYPE, + STATE_PROPERTY_ACCESS, + } state = STATE_NODE; + + _cleanup_free_ char *node_path = NULL, *argument_type = NULL, *argument_direction = NULL; + const char *np = prefix; + int r; + + assert(context); + assert(prefix); + + if (n_depth > NODE_DEPTH_MAX) { + log_error("<node> depth too high."); + return -EINVAL; + } + + for (;;) { + _cleanup_free_ char *name = NULL; + int t; + + t = xml_tokenize(&context->current, &name, &context->xml_state, NULL); + if (t < 0) { + log_error("XML parse error."); + return t; + } + + if (t == XML_END) { + log_error("Premature end of XML data."); + return -EBADMSG; + } + + switch (state) { + + case STATE_NODE: + if (t == XML_ATTRIBUTE_NAME) { + + if (streq_ptr(name, "name")) + state = STATE_NODE_NAME; + else { + log_error("Unexpected <node> attribute %s.", name); + return -EBADMSG; + } + + } else if (t == XML_TAG_OPEN) { + + if (streq_ptr(name, "interface")) + state = STATE_INTERFACE; + else if (streq_ptr(name, "node")) { + + r = parse_xml_node(context, np, n_depth+1); + if (r < 0) + return r; + } else { + log_error("Unexpected <node> tag %s.", name); + return -EBADMSG; + } + + } else if (t == XML_TAG_CLOSE_EMPTY || + (t == XML_TAG_CLOSE && streq_ptr(name, "node"))) { + + if (context->ops->on_path) { + r = context->ops->on_path(node_path ? node_path : np, context->userdata); + if (r < 0) + return r; + } + + return 0; + + } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { + log_error("Unexpected token in <node>. (1)"); + return -EINVAL; + } + + break; + + case STATE_NODE_NAME: + + if (t == XML_ATTRIBUTE_VALUE) { + + free(node_path); + + if (name[0] == '/') { + node_path = name; + name = NULL; + } else { + + if (endswith(prefix, "/")) + node_path = strappend(prefix, name); + else + node_path = strjoin(prefix, "/", name, NULL); + if (!node_path) + return log_oom(); + } + + np = node_path; + state = STATE_NODE; + } else { + log_error("Unexpected token in <node>. (2)"); + return -EINVAL; + } + + break; + + case STATE_INTERFACE: + + if (t == XML_ATTRIBUTE_NAME) { + if (streq_ptr(name, "name")) + state = STATE_INTERFACE_NAME; + else { + log_error("Unexpected <interface> attribute %s.", name); + return -EBADMSG; + } + + } else if (t == XML_TAG_OPEN) { + if (streq_ptr(name, "method")) + state = STATE_METHOD; + else if (streq_ptr(name, "signal")) + state = STATE_SIGNAL; + else if (streq_ptr(name, "property")) { + context->member_flags |= SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE; + state = STATE_PROPERTY; + } else if (streq_ptr(name, "annotation")) { + r = parse_xml_annotation(context, &context->interface_flags); + if (r < 0) + return r; + } else { + log_error("Unexpected <interface> tag %s.", name); + return -EINVAL; + } + } else if (t == XML_TAG_CLOSE_EMPTY || + (t == XML_TAG_CLOSE && streq_ptr(name, "interface"))) { + + if (n_depth == 0) { + if (context->ops->on_interface) { + r = context->ops->on_interface(context->interface_name, context->interface_flags, context->userdata); + if (r < 0) + return r; + } + + context_reset_interface(context); + } + + state = STATE_NODE; + + } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { + log_error("Unexpected token in <interface>. (1)"); + return -EINVAL; + } + + break; + + case STATE_INTERFACE_NAME: + + if (t == XML_ATTRIBUTE_VALUE) { + if (n_depth == 0) { + free(context->interface_name); + context->interface_name = name; + name = NULL; + } + + state = STATE_INTERFACE; + } else { + log_error("Unexpected token in <interface>. (2)"); + return -EINVAL; + } + + break; + + case STATE_METHOD: + + if (t == XML_ATTRIBUTE_NAME) { + if (streq_ptr(name, "name")) + state = STATE_METHOD_NAME; + else { + log_error("Unexpected <method> attribute %s", name); + return -EBADMSG; + } + } else if (t == XML_TAG_OPEN) { + if (streq_ptr(name, "arg")) + state = STATE_METHOD_ARG; + else if (streq_ptr(name, "annotation")) { + r = parse_xml_annotation(context, &context->member_flags); + if (r < 0) + return r; + } else { + log_error("Unexpected <method> tag %s.", name); + return -EINVAL; + } + } else if (t == XML_TAG_CLOSE_EMPTY || + (t == XML_TAG_CLOSE && streq_ptr(name, "method"))) { + + if (n_depth == 0) { + if (context->ops->on_method) { + r = context->ops->on_method(context->interface_name, context->member_name, context->member_signature, context->member_result, context->member_flags, context->userdata); + if (r < 0) + return r; + } + + context_reset_member(context); + } + + state = STATE_INTERFACE; + + } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { + log_error("Unexpected token in <method> (1)."); + return -EINVAL; + } + + break; + + case STATE_METHOD_NAME: + + if (t == XML_ATTRIBUTE_VALUE) { + + if (n_depth == 0) { + free(context->member_name); + context->member_name = name; + name = NULL; + } + + state = STATE_METHOD; + } else { + log_error("Unexpected token in <method> (2)."); + return -EINVAL; + } + + break; + + case STATE_METHOD_ARG: + + if (t == XML_ATTRIBUTE_NAME) { + if (streq_ptr(name, "name")) + state = STATE_METHOD_ARG_NAME; + else if (streq_ptr(name, "type")) + state = STATE_METHOD_ARG_TYPE; + else if (streq_ptr(name, "direction")) + state = STATE_METHOD_ARG_DIRECTION; + else { + log_error("Unexpected method <arg> attribute %s.", name); + return -EBADMSG; + } + } else if (t == XML_TAG_OPEN) { + if (streq_ptr(name, "annotation")) { + r = parse_xml_annotation(context, NULL); + if (r < 0) + return r; + } else { + log_error("Unexpected method <arg> tag %s.", name); + return -EINVAL; + } + } else if (t == XML_TAG_CLOSE_EMPTY || + (t == XML_TAG_CLOSE && streq_ptr(name, "arg"))) { + + if (n_depth == 0) { + + if (argument_type) { + if (!argument_direction || streq(argument_direction, "in")) { + if (!strextend(&context->member_signature, argument_type, NULL)) + return log_oom(); + } else if (streq(argument_direction, "out")) { + if (!strextend(&context->member_result, argument_type, NULL)) + return log_oom(); + } + } + + free(argument_type); + free(argument_direction); + argument_type = argument_direction = NULL; + } + + state = STATE_METHOD; + } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { + log_error("Unexpected token in method <arg>. (1)"); + return -EINVAL; + } + + break; + + case STATE_METHOD_ARG_NAME: + + if (t == XML_ATTRIBUTE_VALUE) + state = STATE_METHOD_ARG; + else { + log_error("Unexpected token in method <arg>. (2)"); + return -EINVAL; + } + + break; + + case STATE_METHOD_ARG_TYPE: + + if (t == XML_ATTRIBUTE_VALUE) { + free(argument_type); + argument_type = name; + name = NULL; + + state = STATE_METHOD_ARG; + } else { + log_error("Unexpected token in method <arg>. (3)"); + return -EINVAL; + } + + break; + + case STATE_METHOD_ARG_DIRECTION: + + if (t == XML_ATTRIBUTE_VALUE) { + free(argument_direction); + argument_direction = name; + name = NULL; + + state = STATE_METHOD_ARG; + } else { + log_error("Unexpected token in method <arg>. (4)"); + return -EINVAL; + } + + break; + + case STATE_SIGNAL: + + if (t == XML_ATTRIBUTE_NAME) { + if (streq_ptr(name, "name")) + state = STATE_SIGNAL_NAME; + else { + log_error("Unexpected <signal> attribute %s.", name); + return -EBADMSG; + } + } else if (t == XML_TAG_OPEN) { + if (streq_ptr(name, "arg")) + state = STATE_SIGNAL_ARG; + else if (streq_ptr(name, "annotation")) { + r = parse_xml_annotation(context, &context->member_flags); + if (r < 0) + return r; + } else { + log_error("Unexpected <signal> tag %s.", name); + return -EINVAL; + } + } else if (t == XML_TAG_CLOSE_EMPTY || + (t == XML_TAG_CLOSE && streq_ptr(name, "signal"))) { + + if (n_depth == 0) { + if (context->ops->on_signal) { + r = context->ops->on_signal(context->interface_name, context->member_name, context->member_signature, context->member_flags, context->userdata); + if (r < 0) + return r; + } + + context_reset_member(context); + } + + state = STATE_INTERFACE; + + } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { + log_error("Unexpected token in <signal>. (1)"); + return -EINVAL; + } + + break; + + case STATE_SIGNAL_NAME: + + if (t == XML_ATTRIBUTE_VALUE) { + + if (n_depth == 0) { + free(context->member_name); + context->member_name = name; + name = NULL; + } + + state = STATE_SIGNAL; + } else { + log_error("Unexpected token in <signal>. (2)"); + return -EINVAL; + } + + break; + + + case STATE_SIGNAL_ARG: + + if (t == XML_ATTRIBUTE_NAME) { + if (streq_ptr(name, "name")) + state = STATE_SIGNAL_ARG_NAME; + else if (streq_ptr(name, "type")) + state = STATE_SIGNAL_ARG_TYPE; + else { + log_error("Unexpected signal <arg> attribute %s.", name); + return -EBADMSG; + } + } else if (t == XML_TAG_OPEN) { + if (streq_ptr(name, "annotation")) { + r = parse_xml_annotation(context, NULL); + if (r < 0) + return r; + } else { + log_error("Unexpected signal <arg> tag %s.", name); + return -EINVAL; + } + } else if (t == XML_TAG_CLOSE_EMPTY || + (t == XML_TAG_CLOSE && streq_ptr(name, "arg"))) { + + if (argument_type) { + if (!strextend(&context->member_signature, argument_type, NULL)) + return log_oom(); + + free(argument_type); + argument_type = NULL; + } + + state = STATE_SIGNAL; + } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { + log_error("Unexpected token in signal <arg> (1)."); + return -EINVAL; + } + + break; + + case STATE_SIGNAL_ARG_NAME: + + if (t == XML_ATTRIBUTE_VALUE) + state = STATE_SIGNAL_ARG; + else { + log_error("Unexpected token in signal <arg> (2)."); + return -EINVAL; + } + + break; + + case STATE_SIGNAL_ARG_TYPE: + + if (t == XML_ATTRIBUTE_VALUE) { + free(argument_type); + argument_type = name; + name = NULL; + + state = STATE_SIGNAL_ARG; + } else { + log_error("Unexpected token in signal <arg> (3)."); + return -EINVAL; + } + + break; + + case STATE_PROPERTY: + + if (t == XML_ATTRIBUTE_NAME) { + if (streq_ptr(name, "name")) + state = STATE_PROPERTY_NAME; + else if (streq_ptr(name, "type")) + state = STATE_PROPERTY_TYPE; + else if (streq_ptr(name, "access")) + state = STATE_PROPERTY_ACCESS; + else { + log_error("Unexpected <property> attribute %s.", name); + return -EBADMSG; + } + } else if (t == XML_TAG_OPEN) { + + if (streq_ptr(name, "annotation")) { + r = parse_xml_annotation(context, &context->member_flags); + if (r < 0) + return r; + } else { + log_error("Unexpected <property> tag %s.", name); + return -EINVAL; + } + + } else if (t == XML_TAG_CLOSE_EMPTY || + (t == XML_TAG_CLOSE && streq_ptr(name, "property"))) { + + if (n_depth == 0) { + if (context->ops->on_property) { + r = context->ops->on_property(context->interface_name, context->member_name, context->member_signature, context->member_writable, context->member_flags, context->userdata); + if (r < 0) + return r; + } + + context_reset_member(context); + } + + state = STATE_INTERFACE; + + } else if (t != XML_TEXT || !in_charset(name, WHITESPACE)) { + log_error("Unexpected token in <property>. (1)"); + return -EINVAL; + } + + break; + + case STATE_PROPERTY_NAME: + + if (t == XML_ATTRIBUTE_VALUE) { + + if (n_depth == 0) { + free(context->member_name); + context->member_name = name; + name = NULL; + } + state = STATE_PROPERTY; + } else { + log_error("Unexpected token in <property>. (2)"); + return -EINVAL; + } + + break; + + case STATE_PROPERTY_TYPE: + + if (t == XML_ATTRIBUTE_VALUE) { + + if (n_depth == 0) { + free(context->member_signature); + context->member_signature = name; + name = NULL; + } + + state = STATE_PROPERTY; + } else { + log_error("Unexpected token in <property>. (3)"); + return -EINVAL; + } + + break; + + case STATE_PROPERTY_ACCESS: + + if (t == XML_ATTRIBUTE_VALUE) { + + if (streq(name, "readwrite") || streq(name, "write")) + context->member_writable = true; + + state = STATE_PROPERTY; + } else { + log_error("Unexpected token in <property>. (4)"); + return -EINVAL; + } + + break; + } + } +} + +int parse_xml_introspect(const char *prefix, const char *xml, const XMLIntrospectOps *ops, void *userdata) { + Context context = { + .ops = ops, + .userdata = userdata, + .current = xml, + }; + + int r; + + assert(prefix); + assert(xml); + assert(ops); + + for (;;) { + _cleanup_free_ char *name = NULL; + + r = xml_tokenize(&context.current, &name, &context.xml_state, NULL); + if (r < 0) { + log_error("XML parse error"); + goto finish; + } + + if (r == XML_END) { + r = 0; + break; + } + + if (r == XML_TAG_OPEN) { + + if (streq(name, "node")) { + r = parse_xml_node(&context, prefix, 0); + if (r < 0) + goto finish; + } else { + log_error("Unexpected tag '%s' in introspection data.", name); + r = -EBADMSG; + goto finish; + } + } else if (r != XML_TEXT || !in_charset(name, WHITESPACE)) { + log_error("Unexpected token."); + r = -EBADMSG; + goto finish; + } + } + +finish: + context_reset_interface(&context); + + return r; +} diff --git a/src/libsystemd/sd-bus/busctl-introspect.h b/src/libsystemd/sd-bus/busctl-introspect.h new file mode 100644 index 0000000000..9bea43717d --- /dev/null +++ b/src/libsystemd/sd-bus/busctl-introspect.h @@ -0,0 +1,34 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 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 <inttypes.h> + +typedef struct XMLIntrospectOps { + int (*on_path)(const char *path, void *userdata); + int (*on_interface)(const char *name, uint64_t flags, void *userdata); + int (*on_method)(const char *interface, const char *name, const char *signature, const char *result, uint64_t flags, void *userdata); + int (*on_signal)(const char *interface, const char *name, const char *signature, uint64_t flags, void *userdata); + int (*on_property)(const char *interface, const char *name, const char *signature, bool writable, uint64_t flags, void *userdata); +} XMLIntrospectOps; + +int parse_xml_introspect(const char *prefix, const char *xml, const XMLIntrospectOps *ops, void *userdata); diff --git a/src/libsystemd/sd-bus/busctl.c b/src/libsystemd/sd-bus/busctl.c index fdc275ce46..dd6ae865b6 100644 --- a/src/libsystemd/sd-bus/busctl.c +++ b/src/libsystemd/sd-bus/busctl.c @@ -26,12 +26,17 @@ #include "log.h" #include "build.h" #include "pager.h" +#include "xml.h" +#include "path-util.h" #include "sd-bus.h" #include "bus-message.h" #include "bus-internal.h" #include "bus-util.h" #include "bus-dump.h" +#include "bus-signature.h" +#include "bus-type.h" +#include "busctl-introspect.h" static bool arg_no_pager = false; static bool arg_legend = true; @@ -44,6 +49,15 @@ static char **arg_matches = NULL; static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; static char *arg_host = NULL; static bool arg_user = false; +static size_t arg_snaplen = 4096; +static bool arg_list = false; +static bool arg_quiet = false; +static bool arg_verbose = false; +static bool arg_expect_reply = true; +static bool arg_auto_start = true; +static bool arg_allow_interactive_authorization = true; +static bool arg_augment_creds = true; +static usec_t arg_timeout = 0; static void pager_open_if_enabled(void) { @@ -54,6 +68,9 @@ static void pager_open_if_enabled(void) { pager_open(false); } +#define NAME_IS_ACQUIRED INT_TO_PTR(1) +#define NAME_IS_ACTIVATABLE INT_TO_PTR(2) + static int list_bus_names(sd_bus *bus, char **argv) { _cleanup_strv_free_ char **acquired = NULL, **activatable = NULL; _cleanup_free_ char **merged = NULL; @@ -68,11 +85,12 @@ static int list_bus_names(sd_bus *bus, char **argv) { assert(bus); + if (!arg_unique && !arg_acquired && !arg_activatable) + arg_unique = arg_acquired = arg_activatable = true; + r = sd_bus_list_names(bus, (arg_acquired || arg_unique) ? &acquired : NULL, arg_activatable ? &activatable : NULL); - if (r < 0) { - log_error("Failed to list names: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to list names: %m"); pager_open_if_enabled(); @@ -83,21 +101,17 @@ static int list_bus_names(sd_bus *bus, char **argv) { STRV_FOREACH(i, acquired) { max_i = MAX(max_i, strlen(*i)); - r = hashmap_put(names, *i, INT_TO_PTR(1)); - if (r < 0) { - log_error("Failed to add to hashmap: %s", strerror(-r)); - return r; - } + r = hashmap_put(names, *i, NAME_IS_ACQUIRED); + if (r < 0) + return log_error_errno(r, "Failed to add to hashmap: %m"); } STRV_FOREACH(i, activatable) { max_i = MAX(max_i, strlen(*i)); - r = hashmap_put(names, *i, INT_TO_PTR(2)); - if (r < 0 && r != -EEXIST) { - log_error("Failed to add to hashmap: %s", strerror(-r)); - return r; - } + r = hashmap_put(names, *i, NAME_IS_ACTIVATABLE); + if (r < 0 && r != -EEXIST) + return log_error_errno(r, "Failed to add to hashmap: %m"); } merged = new(char*, hashmap_size(names) + 1); @@ -109,7 +123,7 @@ static int list_bus_names(sd_bus *bus, char **argv) { if (arg_legend) { printf("%-*s %*s %-*s %-*s %-*s %-*s %-*s %-*s", - (int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER", 13, "CONNECTION", 25, "UNIT", 10, "SESSION", 19, "CONNECTION-NAME"); + (int) max_i, "NAME", 10, "PID", 15, "PROCESS", 16, "USER", 13, "CONNECTION", 25, "UNIT", 10, "SESSION", 19, "DESCRIPTION"); if (arg_show_machine) puts(" MACHINE"); @@ -121,7 +135,7 @@ static int list_bus_names(sd_bus *bus, char **argv) { _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL; sd_id128_t mid; - if (hashmap_get(names, *i) == INT_TO_PTR(2)) { + if (hashmap_get(names, *i) == NAME_IS_ACTIVATABLE) { /* Activatable */ printf("%-*s", (int) max_i, *i); @@ -142,10 +156,12 @@ static int list_bus_names(sd_bus *bus, char **argv) { printf("%-*s", (int) max_i, *i); - r = sd_bus_get_name_creds(bus, *i, - SD_BUS_CREDS_UID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM| - SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_SESSION| - SD_BUS_CREDS_CONNECTION_NAME, &creds); + r = sd_bus_get_name_creds( + bus, *i, + (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | + SD_BUS_CREDS_UID|SD_BUS_CREDS_PID|SD_BUS_CREDS_COMM| + SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_SESSION| + SD_BUS_CREDS_DESCRIPTION, &creds); if (r >= 0) { const char *unique, *session, *unit, *cn; pid_t pid; @@ -200,7 +216,7 @@ static int list_bus_names(sd_bus *bus, char **argv) { else fputs(" - ", stdout); - r = sd_bus_creds_get_connection_name(creds, &cn); + r = sd_bus_creds_get_description(creds, &cn); if (r >= 0) printf(" %-19s", cn); else @@ -223,7 +239,838 @@ static int list_bus_names(sd_bus *bus, char **argv) { return 0; } -static int monitor(sd_bus *bus, char *argv[]) { +static void print_subtree(const char *prefix, const char *path, char **l) { + const char *vertical, *space; + char **n; + + /* We assume the list is sorted. Let's first skip over the + * entry we are looking at. */ + for (;;) { + if (!*l) + return; + + if (!streq(*l, path)) + break; + + l++; + } + + vertical = strappenda(prefix, draw_special_char(DRAW_TREE_VERTICAL)); + space = strappenda(prefix, draw_special_char(DRAW_TREE_SPACE)); + + for (;;) { + bool has_more = false; + + if (!*l || !path_startswith(*l, path)) + break; + + n = l + 1; + for (;;) { + if (!*n || !path_startswith(*n, path)) + break; + + if (!path_startswith(*n, *l)) { + has_more = true; + break; + } + + n++; + } + + printf("%s%s%s\n", prefix, draw_special_char(has_more ? DRAW_TREE_BRANCH : DRAW_TREE_RIGHT), *l); + + print_subtree(has_more ? vertical : space, *l, l); + l = n; + } +} + +static void print_tree(const char *prefix, char **l) { + + pager_open_if_enabled(); + + prefix = strempty(prefix); + + if (arg_list) { + char **i; + + STRV_FOREACH(i, l) + printf("%s%s\n", prefix, *i); + return; + } + + if (strv_isempty(l)) { + printf("No objects discovered.\n"); + return; + } + + if (streq(l[0], "/") && !l[1]) { + printf("Only root object discovered.\n"); + return; + } + + print_subtree(prefix, "/", l); +} + +static int on_path(const char *path, void *userdata) { + Set *paths = userdata; + int r; + + assert(paths); + + r = set_put_strdup(paths, path); + if (r < 0) + return log_oom(); + + return 0; +} + +static int find_nodes(sd_bus *bus, const char *service, const char *path, Set *paths, bool many) { + static const XMLIntrospectOps ops = { + .on_path = on_path, + }; + + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + const char *xml; + int r; + + r = sd_bus_call_method(bus, service, path, "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, ""); + if (r < 0) { + if (many) + printf("Failed to introspect object %s of service %s: %s\n", path, service, bus_error_message(&error, r)); + else + log_error("Failed to introspect object %s of service %s: %s", path, service, bus_error_message(&error, r)); + return r; + } + + r = sd_bus_message_read(reply, "s", &xml); + if (r < 0) + return bus_log_parse_error(r); + + return parse_xml_introspect(path, xml, &ops, paths); +} + +static int tree_one(sd_bus *bus, const char *service, const char *prefix, bool many) { + _cleanup_set_free_free_ Set *paths = NULL, *done = NULL, *failed = NULL; + _cleanup_free_ char **l = NULL; + char *m; + int r; + + paths = set_new(&string_hash_ops); + if (!paths) + return log_oom(); + + done = set_new(&string_hash_ops); + if (!done) + return log_oom(); + + failed = set_new(&string_hash_ops); + if (!failed) + return log_oom(); + + m = strdup("/"); + if (!m) + return log_oom(); + + r = set_put(paths, m); + if (r < 0) { + free(m); + return log_oom(); + } + + for (;;) { + _cleanup_free_ char *p = NULL; + int q; + + p = set_steal_first(paths); + if (!p) + break; + + if (set_contains(done, p) || + set_contains(failed, p)) + continue; + + q = find_nodes(bus, service, p, paths, many); + if (q < 0) { + if (r >= 0) + r = q; + + q = set_put(failed, p); + } else + q = set_put(done, p); + + if (q < 0) + return log_oom(); + + assert(q != 0); + p = NULL; + } + + pager_open_if_enabled(); + + l = set_get_strv(done); + if (!l) + return log_oom(); + + strv_sort(l); + print_tree(prefix, l); + + fflush(stdout); + + return r; +} + +static int tree(sd_bus *bus, char **argv) { + char **i; + int r = 0; + + if (!arg_unique && !arg_acquired) + arg_acquired = true; + + if (strv_length(argv) <= 1) { + _cleanup_strv_free_ char **names = NULL; + bool not_first = false; + + r = sd_bus_list_names(bus, &names, NULL); + if (r < 0) + return log_error_errno(r, "Failed to get name list: %m"); + + pager_open_if_enabled(); + + STRV_FOREACH(i, names) { + int q; + + if (!arg_unique && (*i)[0] == ':') + continue; + + if (!arg_acquired && (*i)[0] == ':') + continue; + + if (not_first) + printf("\n"); + + printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_highlight_off()); + + q = tree_one(bus, *i, NULL, true); + if (q < 0 && r >= 0) + r = q; + + not_first = true; + } + } else { + STRV_FOREACH(i, argv+1) { + int q; + + if (i > argv+1) + printf("\n"); + + if (argv[2]) { + pager_open_if_enabled(); + printf("Service %s%s%s:\n", ansi_highlight(), *i, ansi_highlight_off()); + } + + q = tree_one(bus, *i, NULL, !!argv[2]); + if (q < 0 && r >= 0) + r = q; + } + } + + return r; +} + +static int format_cmdline(sd_bus_message *m, FILE *f, bool needs_space) { + int r; + + for (;;) { + const char *contents = NULL; + char type; + union { + uint8_t u8; + uint16_t u16; + int16_t s16; + uint32_t u32; + int32_t s32; + uint64_t u64; + int64_t s64; + double d64; + const char *string; + int i; + } basic; + + r = sd_bus_message_peek_type(m, &type, &contents); + if (r <= 0) + return r; + + if (bus_type_is_container(type) > 0) { + + r = sd_bus_message_enter_container(m, type, contents); + if (r < 0) + return r; + + if (type == SD_BUS_TYPE_ARRAY) { + unsigned n = 0; + + /* count array entries */ + for (;;) { + + r = sd_bus_message_skip(m, contents); + if (r < 0) + return r; + if (r == 0) + break; + + n++; + } + + r = sd_bus_message_rewind(m, false); + if (r < 0) + return r; + + if (needs_space) + fputc(' ', f); + + fprintf(f, "%u", n); + } else if (type == SD_BUS_TYPE_VARIANT) { + + if (needs_space) + fputc(' ', f); + + fprintf(f, "%s", contents); + } + + r = format_cmdline(m, f, needs_space || IN_SET(type, SD_BUS_TYPE_ARRAY, SD_BUS_TYPE_VARIANT)); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(m); + if (r < 0) + return r; + + continue; + } + + r = sd_bus_message_read_basic(m, type, &basic); + if (r < 0) + return r; + + if (needs_space) + fputc(' ', f); + + switch (type) { + case SD_BUS_TYPE_BYTE: + fprintf(f, "%u", basic.u8); + break; + + case SD_BUS_TYPE_BOOLEAN: + fputs(true_false(basic.i), f); + break; + + case SD_BUS_TYPE_INT16: + fprintf(f, "%i", basic.s16); + break; + + case SD_BUS_TYPE_UINT16: + fprintf(f, "%u", basic.u16); + break; + + case SD_BUS_TYPE_INT32: + fprintf(f, "%i", basic.s32); + break; + + case SD_BUS_TYPE_UINT32: + fprintf(f, "%u", basic.u32); + break; + + case SD_BUS_TYPE_INT64: + fprintf(f, "%" PRIi64, basic.s64); + break; + + case SD_BUS_TYPE_UINT64: + fprintf(f, "%" PRIu64, basic.u64); + break; + + case SD_BUS_TYPE_DOUBLE: + fprintf(f, "%g", basic.d64); + break; + + case SD_BUS_TYPE_STRING: + case SD_BUS_TYPE_OBJECT_PATH: + case SD_BUS_TYPE_SIGNATURE: { + _cleanup_free_ char *b = NULL; + + b = cescape(basic.string); + if (!b) + return -ENOMEM; + + fprintf(f, "\"%s\"", b); + break; + } + + case SD_BUS_TYPE_UNIX_FD: + fprintf(f, "%i", basic.i); + break; + + default: + assert_not_reached("Unknown basic type."); + } + + needs_space = true; + } +} + +typedef struct Member { + const char *type; + char *interface; + char *name; + char *signature; + char *result; + char *value; + bool writable; + uint64_t flags; +} Member; + +static unsigned long member_hash_func(const void *p, const uint8_t hash_key[]) { + const Member *m = p; + unsigned long ul; + + assert(m); + assert(m->type); + + ul = string_hash_func(m->type, hash_key); + + if (m->name) + ul ^= string_hash_func(m->name, hash_key); + + if (m->interface) + ul ^= string_hash_func(m->interface, hash_key); + + return ul; +} + +static int member_compare_func(const void *a, const void *b) { + const Member *x = a, *y = b; + int d; + + assert(x); + assert(y); + assert(x->type); + assert(y->type); + + if (!x->interface && y->interface) + return -1; + if (x->interface && !y->interface) + return 1; + if (x->interface && y->interface) { + d = strcmp(x->interface, y->interface); + if (d != 0) + return d; + } + + d = strcmp(x->type, y->type); + if (d != 0) + return d; + + if (!x->name && y->name) + return -1; + if (x->name && !y->name) + return 1; + if (x->name && y->name) + return strcmp(x->name, y->name); + + return 0; +} + +static int member_compare_funcp(const void *a, const void *b) { + const Member *const * x = (const Member *const *) a, * const *y = (const Member *const *) b; + + return member_compare_func(*x, *y); +} + +static void member_free(Member *m) { + if (!m) + return; + + free(m->interface); + free(m->name); + free(m->signature); + free(m->result); + free(m->value); + free(m); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(Member*, member_free); + +static void member_set_free(Set *s) { + Member *m; + + while ((m = set_steal_first(s))) + member_free(m); + + set_free(s); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, member_set_free); + +static int on_interface(const char *interface, uint64_t flags, void *userdata) { + _cleanup_(member_freep) Member *m; + Set *members = userdata; + int r; + + assert(interface); + assert(members); + + m = new0(Member, 1); + if (!m) + return log_oom(); + + m->type = "interface"; + m->flags = flags; + + r = free_and_strdup(&m->interface, interface); + if (r < 0) + return log_oom(); + + r = set_put(members, m); + if (r <= 0) { + log_error("Duplicate interface"); + return -EINVAL; + } + + m = NULL; + return 0; +} + +static int on_method(const char *interface, const char *name, const char *signature, const char *result, uint64_t flags, void *userdata) { + _cleanup_(member_freep) Member *m; + Set *members = userdata; + int r; + + assert(interface); + assert(name); + + m = new0(Member, 1); + if (!m) + return log_oom(); + + m->type = "method"; + m->flags = flags; + + r = free_and_strdup(&m->interface, interface); + if (r < 0) + return log_oom(); + + r = free_and_strdup(&m->name, name); + if (r < 0) + return log_oom(); + + r = free_and_strdup(&m->signature, signature); + if (r < 0) + return log_oom(); + + r = free_and_strdup(&m->result, result); + if (r < 0) + return log_oom(); + + r = set_put(members, m); + if (r <= 0) { + log_error("Duplicate method"); + return -EINVAL; + } + + m = NULL; + return 0; +} + +static int on_signal(const char *interface, const char *name, const char *signature, uint64_t flags, void *userdata) { + _cleanup_(member_freep) Member *m; + Set *members = userdata; + int r; + + assert(interface); + assert(name); + + m = new0(Member, 1); + if (!m) + return log_oom(); + + m->type = "signal"; + m->flags = flags; + + r = free_and_strdup(&m->interface, interface); + if (r < 0) + return log_oom(); + + r = free_and_strdup(&m->name, name); + if (r < 0) + return log_oom(); + + r = free_and_strdup(&m->signature, signature); + if (r < 0) + return log_oom(); + + r = set_put(members, m); + if (r <= 0) { + log_error("Duplicate signal"); + return -EINVAL; + } + + m = NULL; + return 0; +} + +static int on_property(const char *interface, const char *name, const char *signature, bool writable, uint64_t flags, void *userdata) { + _cleanup_(member_freep) Member *m; + Set *members = userdata; + int r; + + assert(interface); + assert(name); + + m = new0(Member, 1); + if (!m) + return log_oom(); + + m->type = "property"; + m->flags = flags; + m->writable = writable; + + r = free_and_strdup(&m->interface, interface); + if (r < 0) + return log_oom(); + + r = free_and_strdup(&m->name, name); + if (r < 0) + return log_oom(); + + r = free_and_strdup(&m->signature, signature); + if (r < 0) + return log_oom(); + + r = set_put(members, m); + if (r <= 0) { + log_error("Duplicate property"); + return -EINVAL; + } + + m = NULL; + return 0; +} + +static const char *strdash(const char *x) { + return isempty(x) ? "-" : x; +} + +static int introspect(sd_bus *bus, char **argv) { + static const struct hash_ops member_hash_ops = { + .hash = member_hash_func, + .compare = member_compare_func, + }; + + static const XMLIntrospectOps ops = { + .on_interface = on_interface, + .on_method = on_method, + .on_signal = on_signal, + .on_property = on_property, + }; + + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(member_set_freep) Set *members = NULL; + Iterator i; + Member *m; + const char *xml; + int r; + unsigned name_width, type_width, signature_width, result_width; + Member **sorted = NULL; + unsigned k = 0, j; + + if (strv_length(argv) != 3) { + log_error("Requires service and object path argument."); + return -EINVAL; + } + + members = set_new(&member_hash_ops); + if (!members) + return log_oom(); + + r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, ""); + if (r < 0) { + log_error("Failed to introspect object %s of service %s: %s", argv[2], argv[1], bus_error_message(&error, r)); + return r; + } + + r = sd_bus_message_read(reply, "s", &xml); + if (r < 0) + return bus_log_parse_error(r); + + /* First, get list of all properties */ + r = parse_xml_introspect(argv[2], xml, &ops, members); + if (r < 0) + return r; + + /* Second, find the current values for them */ + SET_FOREACH(m, members, i) { + + if (!streq(m->type, "property")) + continue; + + if (m->value) + continue; + + r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", m->interface); + if (r < 0) { + log_error("%s", bus_error_message(&error, r)); + return r; + } + + r = sd_bus_message_enter_container(reply, 'a', "{sv}"); + if (r < 0) + return bus_log_parse_error(r); + + for (;;) { + Member *z; + _cleanup_free_ char *buf = NULL; + _cleanup_fclose_ FILE *mf = NULL; + size_t sz = 0; + const char *name; + + r = sd_bus_message_enter_container(reply, 'e', "sv"); + if (r < 0) + return bus_log_parse_error(r); + + if (r == 0) + break; + + r = sd_bus_message_read(reply, "s", &name); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_enter_container(reply, 'v', NULL); + if (r < 0) + return bus_log_parse_error(r); + + mf = open_memstream(&buf, &sz); + if (!mf) + return log_oom(); + + r = format_cmdline(reply, mf, false); + if (r < 0) + return bus_log_parse_error(r); + + fclose(mf); + mf = NULL; + + z = set_get(members, &((Member) { + .type = "property", + .interface = m->interface, + .name = (char*) name })); + if (z) { + free(z->value); + z->value = buf; + buf = NULL; + } + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + } + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + } + + pager_open_if_enabled(); + + name_width = strlen("NAME"); + type_width = strlen("TYPE"); + signature_width = strlen("SIGNATURE"); + result_width = strlen("RESULT/VALUE"); + + sorted = newa(Member*, set_size(members)); + + SET_FOREACH(m, members, i) { + if (m->interface) + name_width = MAX(name_width, strlen(m->interface)); + if (m->name) + name_width = MAX(name_width, strlen(m->name) + 1); + if (m->type) + type_width = MAX(type_width, strlen(m->type)); + if (m->signature) + signature_width = MAX(signature_width, strlen(m->signature)); + if (m->result) + result_width = MAX(result_width, strlen(m->result)); + if (m->value) + result_width = MAX(result_width, strlen(m->value)); + + sorted[k++] = m; + } + + if (result_width > 40) + result_width = 40; + + assert(k == set_size(members)); + qsort(sorted, k, sizeof(Member*), member_compare_funcp); + + if (arg_legend) { + printf("%-*s %-*s %-*s %-*s %s\n", + (int) name_width, "NAME", + (int) type_width, "TYPE", + (int) signature_width, "SIGNATURE", + (int) result_width, "RESULT/VALUE", + "FLAGS"); + } + + for (j = 0; j < k; j++) { + _cleanup_free_ char *ellipsized = NULL; + const char *rv; + bool is_interface; + + m = sorted[j]; + + is_interface = streq(m->type, "interface"); + + if (m->value) { + ellipsized = ellipsize(m->value, result_width, 100); + if (!ellipsized) + return log_oom(); + + rv = ellipsized; + } else + rv = strdash(m->result); + + printf("%s%s%-*s%s %-*s %-*s %-*s%s%s%s%s%s%s\n", + is_interface ? ansi_highlight() : "", + is_interface ? "" : ".", + - !is_interface + (int) name_width, strdash(streq_ptr(m->type, "interface") ? m->interface : m->name), + is_interface ? ansi_highlight_off() : "", + (int) type_width, strdash(m->type), + (int) signature_width, strdash(m->signature), + (int) result_width, rv, + (m->flags & SD_BUS_VTABLE_DEPRECATED) ? " deprecated" : (m->flags || m->writable ? "" : " -"), + (m->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ? " no-reply" : "", + (m->flags & SD_BUS_VTABLE_PROPERTY_CONST) ? " const" : "", + (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) ? " emits-change" : "", + (m->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) ? " emits-invalidation" : "", + m->writable ? " writable" : ""); + } + + return 0; +} + +static int message_dump(sd_bus_message *m, FILE *f) { + return bus_message_dump(m, f, BUS_MESSAGE_DUMP_WITH_HEADER); +} + +static int message_pcap(sd_bus_message *m, FILE *f) { + return bus_message_pcap_frame(m, arg_snaplen, f); +} + +static int monitor(sd_bus *bus, char *argv[], int (*dump)(sd_bus_message *m, FILE *f)) { bool added_something = false; char **i; int r; @@ -241,43 +1088,35 @@ static int monitor(sd_bus *bus, char *argv[]) { return log_oom(); r = sd_bus_add_match(bus, NULL, m, NULL, NULL); - if (r < 0) { - log_error("Failed to add match: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add match: %m"); added_something = true; } STRV_FOREACH(i, arg_matches) { r = sd_bus_add_match(bus, NULL, *i, NULL, NULL); - if (r < 0) { - log_error("Failed to add match: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add match: %m"); added_something = true; } if (!added_something) { r = sd_bus_add_match(bus, NULL, "", NULL, NULL); - if (r < 0) { - log_error("Failed to add match: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add match: %m"); } for (;;) { _cleanup_bus_message_unref_ sd_bus_message *m = NULL; r = sd_bus_process(bus, &m); - if (r < 0) { - log_error("Failed to process bus: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to process bus: %m"); if (m) { - bus_message_dump(m, stdout, true); + dump(m, stdout); continue; } @@ -285,13 +1124,33 @@ static int monitor(sd_bus *bus, char *argv[]) { continue; r = sd_bus_wait(bus, (uint64_t) -1); - if (r < 0) { - log_error("Failed to wait for bus: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to wait for bus: %m"); } } +static int capture(sd_bus *bus, char *argv[]) { + int r; + + if (isatty(fileno(stdout)) > 0) { + log_error("Refusing to write message data to console, please redirect output to a file."); + return -EINVAL; + } + + bus_pcap_header(arg_snaplen, stdout); + + r = monitor(bus, argv, message_pcap); + if (r < 0) + return r; + + if (ferror(stdout)) { + log_error("Couldn't write capture file."); + return -EIO; + } + + return r; +} + static int status(sd_bus *bus, char *argv[]) { _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL; pid_t pid; @@ -299,23 +1158,498 @@ static int status(sd_bus *bus, char *argv[]) { assert(bus); - if (strv_length(argv) != 2) { - log_error("Expects one argument."); + if (strv_length(argv) > 2) { + log_error("Expects no or one argument."); + return -EINVAL; + } + + if (argv[1]) { + r = parse_pid(argv[1], &pid); + if (r < 0) + r = sd_bus_get_name_creds( + bus, + argv[1], + (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | _SD_BUS_CREDS_ALL, + &creds); + else + r = sd_bus_creds_new_from_pid( + &creds, + pid, + _SD_BUS_CREDS_ALL); + } else { + const char *scope, *address; + sd_id128_t bus_id; + + r = sd_bus_get_address(bus, &address); + if (r >= 0) + printf("BusAddress=%s%s%s\n", ansi_highlight(), address, ansi_highlight_off()); + + r = sd_bus_get_scope(bus, &scope); + if (r >= 0) + printf("BusScope=%s%s%s\n", ansi_highlight(), scope, ansi_highlight_off()); + + r = sd_bus_get_bus_id(bus, &bus_id); + if (r >= 0) + printf("BusID=%s" SD_ID128_FORMAT_STR "%s\n", ansi_highlight(), SD_ID128_FORMAT_VAL(bus_id), ansi_highlight_off()); + + r = sd_bus_get_owner_creds( + bus, + (arg_augment_creds ? SD_BUS_CREDS_AUGMENT : 0) | _SD_BUS_CREDS_ALL, + &creds); + } + + if (r < 0) + return log_error_errno(r, "Failed to get credentials: %m"); + + bus_creds_dump(creds, NULL, false); + return 0; +} + +static int message_append_cmdline(sd_bus_message *m, const char *signature, char ***x) { + char **p; + int r; + + assert(m); + assert(signature); + assert(x); + + p = *x; + + for (;;) { + const char *v; + char t; + + t = *signature; + v = *p; + + if (t == 0) + break; + if (!v) { + log_error("Too few parameters for signature."); + return -EINVAL; + } + + signature++; + p++; + + switch (t) { + + case SD_BUS_TYPE_BOOLEAN: + + r = parse_boolean(v); + if (r < 0) { + log_error("Failed to parse as boolean: %s", v); + return r; + } + + r = sd_bus_message_append_basic(m, t, &r); + break; + + case SD_BUS_TYPE_BYTE: { + uint8_t z; + + r = safe_atou8(v, &z); + if (r < 0) { + log_error("Failed to parse as byte (unsigned 8bit integer): %s", v); + return r; + } + + r = sd_bus_message_append_basic(m, t, &z); + break; + } + + case SD_BUS_TYPE_INT16: { + int16_t z; + + r = safe_atoi16(v, &z); + if (r < 0) { + log_error("Failed to parse as signed 16bit integer: %s", v); + return r; + } + + r = sd_bus_message_append_basic(m, t, &z); + break; + } + + case SD_BUS_TYPE_UINT16: { + uint16_t z; + + r = safe_atou16(v, &z); + if (r < 0) { + log_error("Failed to parse as unsigned 16bit integer: %s", v); + return r; + } + + r = sd_bus_message_append_basic(m, t, &z); + break; + } + + case SD_BUS_TYPE_INT32: { + int32_t z; + + r = safe_atoi32(v, &z); + if (r < 0) { + log_error("Failed to parse as signed 32bit integer: %s", v); + return r; + } + + r = sd_bus_message_append_basic(m, t, &z); + break; + } + + case SD_BUS_TYPE_UINT32: { + uint32_t z; + + r = safe_atou32(v, &z); + if (r < 0) { + log_error("Failed to parse as unsigned 32bit integer: %s", v); + return r; + } + + r = sd_bus_message_append_basic(m, t, &z); + break; + } + + case SD_BUS_TYPE_INT64: { + int64_t z; + + r = safe_atoi64(v, &z); + if (r < 0) { + log_error("Failed to parse as signed 64bit integer: %s", v); + return r; + } + + r = sd_bus_message_append_basic(m, t, &z); + break; + } + + case SD_BUS_TYPE_UINT64: { + uint64_t z; + + r = safe_atou64(v, &z); + if (r < 0) { + log_error("Failed to parse as unsigned 64bit integer: %s", v); + return r; + } + + r = sd_bus_message_append_basic(m, t, &z); + break; + } + + + case SD_BUS_TYPE_DOUBLE: { + double z; + + r = safe_atod(v, &z); + if (r < 0) { + log_error("Failed to parse as double precision floating point: %s", v); + return r; + } + + r = sd_bus_message_append_basic(m, t, &z); + break; + } + + case SD_BUS_TYPE_STRING: + case SD_BUS_TYPE_OBJECT_PATH: + case SD_BUS_TYPE_SIGNATURE: + + r = sd_bus_message_append_basic(m, t, v); + break; + + case SD_BUS_TYPE_ARRAY: { + uint32_t n; + size_t k; + + r = safe_atou32(v, &n); + if (r < 0) { + log_error("Failed to parse number of array entries: %s", v); + return r; + } + + r = signature_element_length(signature, &k); + if (r < 0) { + log_error("Invalid array signature."); + return r; + } + + { + unsigned i; + char s[k + 1]; + memcpy(s, signature, k); + s[k] = 0; + + r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, s); + if (r < 0) + return bus_log_create_error(r); + + for (i = 0; i < n; i++) { + r = message_append_cmdline(m, s, &p); + if (r < 0) + return r; + } + } + + signature += k; + + r = sd_bus_message_close_container(m); + break; + } + + case SD_BUS_TYPE_VARIANT: + r = sd_bus_message_open_container(m, SD_BUS_TYPE_VARIANT, v); + if (r < 0) + return bus_log_create_error(r); + + r = message_append_cmdline(m, v, &p); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + break; + + case SD_BUS_TYPE_STRUCT_BEGIN: + case SD_BUS_TYPE_DICT_ENTRY_BEGIN: { + size_t k; + + signature--; + p--; + + r = signature_element_length(signature, &k); + if (r < 0) { + log_error("Invalid struct/dict entry signature."); + return r; + } + + { + char s[k-1]; + memcpy(s, signature + 1, k - 2); + s[k - 2] = 0; + + r = sd_bus_message_open_container(m, t == SD_BUS_TYPE_STRUCT_BEGIN ? SD_BUS_TYPE_STRUCT : SD_BUS_TYPE_DICT_ENTRY, s); + if (r < 0) + return bus_log_create_error(r); + + r = message_append_cmdline(m, s, &p); + if (r < 0) + return r; + } + + signature += k; + + r = sd_bus_message_close_container(m); + break; + } + + case SD_BUS_TYPE_UNIX_FD: + log_error("UNIX file descriptor not supported as type."); + return -EINVAL; + + default: + log_error("Unknown signature type %c.", t); + return -EINVAL; + } + + if (r < 0) + return bus_log_create_error(r); + } + + *x = p; + return 0; +} + +static int call(sd_bus *bus, char *argv[]) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL; + int r; + + assert(bus); + + if (strv_length(argv) < 5) { + log_error("Expects at least four arguments."); return -EINVAL; } - r = parse_pid(argv[1], &pid); + r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], argv[3], argv[4]); if (r < 0) - r = sd_bus_get_name_creds(bus, argv[1], _SD_BUS_CREDS_ALL, &creds); - else - r = sd_bus_creds_new_from_pid(&creds, pid, _SD_BUS_CREDS_ALL); + return bus_log_create_error(r); + + r = sd_bus_message_set_expect_reply(m, arg_expect_reply); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_set_auto_start(m, arg_auto_start); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_set_allow_interactive_authorization(m, arg_allow_interactive_authorization); + if (r < 0) + return bus_log_create_error(r); + + if (!isempty(argv[5])) { + char **p; + + p = argv+6; + + r = message_append_cmdline(m, argv[5], &p); + if (r < 0) + return r; + + if (*p) { + log_error("Too many parameters for signature."); + return -EINVAL; + } + } + if (!arg_expect_reply) { + r = sd_bus_send(bus, m, NULL); + if (r < 0) { + log_error("Failed to send message."); + return r; + } + + return 0; + } + + r = sd_bus_call(bus, m, arg_timeout, &error, &reply); if (r < 0) { - log_error("Failed to get credentials: %s", strerror(-r)); + log_error("%s", bus_error_message(&error, r)); + return r; + } + + r = sd_bus_message_is_empty(reply); + if (r < 0) + return bus_log_parse_error(r); + + if (r == 0 && !arg_quiet) { + + if (arg_verbose) { + pager_open_if_enabled(); + + r = bus_message_dump(reply, stdout, 0); + if (r < 0) + return r; + } else { + + fputs(sd_bus_message_get_signature(reply, true), stdout); + fputc(' ', stdout); + + r = format_cmdline(reply, stdout, false); + if (r < 0) + return bus_log_parse_error(r); + + fputc('\n', stdout); + } + } + + return 0; +} + +static int get_property(sd_bus *bus, char *argv[]) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + unsigned n; + char **i; + int r; + + assert(bus); + + n = strv_length(argv); + if (n < 5) { + log_error("Expects at least four arguments."); + return -EINVAL; + } + + STRV_FOREACH(i, argv + 4) { + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + const char *contents = NULL; + char type; + + r = sd_bus_call_method(bus, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Get", &error, &reply, "ss", argv[3], *i); + if (r < 0) { + log_error("%s", bus_error_message(&error, r)); + return r; + } + + r = sd_bus_message_peek_type(reply, &type, &contents); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_enter_container(reply, 'v', contents); + if (r < 0) + return bus_log_parse_error(r); + + if (arg_verbose) { + pager_open_if_enabled(); + + r = bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_SUBTREE_ONLY); + if (r < 0) + return r; + } else { + fputs(contents, stdout); + fputc(' ', stdout); + + r = format_cmdline(reply, stdout, false); + if (r < 0) + return bus_log_parse_error(r); + + fputc('\n', stdout); + } + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + } + + return 0; +} + +static int set_property(sd_bus *bus, char *argv[]) { + _cleanup_bus_message_unref_ sd_bus_message *m = NULL; + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + unsigned n; + char **p; + int r; + + assert(bus); + + n = strv_length(argv); + if (n < 6) { + log_error("Expects at least five arguments."); + return -EINVAL; + } + + r = sd_bus_message_new_method_call(bus, &m, argv[1], argv[2], "org.freedesktop.DBus.Properties", "Set"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "ss", argv[3], argv[4]); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'v', argv[5]); + if (r < 0) + return bus_log_create_error(r); + + p = argv+6; + r = message_append_cmdline(m, argv[5], &p); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + if (*p) { + log_error("Too many parameters for signature."); + return -EINVAL; + } + + r = sd_bus_call(bus, m, arg_timeout, &error, NULL); + if (r < 0) { + log_error("%s", bus_error_message(&error, r)); return r; } - bus_creds_dump(creds, NULL); return 0; } @@ -335,11 +1669,29 @@ static int help(void) { " --unique Only show unique names\n" " --acquired Only show acquired names\n" " --activatable Only show activatable names\n" - " --match=MATCH Only show matching messages\n\n" + " --match=MATCH Only show matching messages\n" + " --list Don't show tree, but simple object path list\n" + " --quiet Don't show method call reply\n" + " --verbose Show result values in long format\n" + " --expect-reply=BOOL Expect a method call reply\n" + " --auto-start=BOOL Auto-start destination service\n" + " --allow-interactive-authorization=BOOL\n" + " Allow interactive authorization for operation\n" + " --timeout=SECS Maximum time to wait for method call completion\n" + " --augment-creds=BOOL Extend credential data with data read from /proc/$PID\n\n" "Commands:\n" " list List bus names\n" + " status [SERVICE] Show bus service, process or bus owner credentials\n" " monitor [SERVICE...] Show bus traffic\n" - " status NAME Show name status\n" + " capture [SERVICE...] Capture bus traffic as pcap\n" + " tree [SERVICE...] Show object tree of service\n" + " introspect SERVICE OBJECT\n" + " call SERVICE OBJECT INTERFACE METHOD [SIGNATURE [ARGUMENT...]]\n" + " Call a method\n" + " get-property SERVICE OBJECT INTERFACE PROPERTY...\n" + " Get property value\n" + " set-property SERVICE OBJECT INTERFACE PROPERTY SIGNATURE ARGUMENT...\n" + " Set property value\n" " help Show this help\n" , program_invocation_short_name); @@ -359,7 +1711,15 @@ static int parse_argv(int argc, char *argv[]) { ARG_SHOW_MACHINE, ARG_UNIQUE, ARG_ACQUIRED, - ARG_ACTIVATABLE + ARG_ACTIVATABLE, + ARG_SIZE, + ARG_LIST, + ARG_VERBOSE, + ARG_EXPECT_REPLY, + ARG_AUTO_START, + ARG_ALLOW_INTERACTIVE_AUTHORIZATION, + ARG_TIMEOUT, + ARG_AUGMENT_CREDS, }; static const struct option options[] = { @@ -377,15 +1737,24 @@ static int parse_argv(int argc, char *argv[]) { { "match", required_argument, NULL, ARG_MATCH }, { "host", required_argument, NULL, 'H' }, { "machine", required_argument, NULL, 'M' }, + { "size", required_argument, NULL, ARG_SIZE }, + { "list", no_argument, NULL, ARG_LIST }, + { "quiet", no_argument, NULL, 'q' }, + { "verbose", no_argument, NULL, ARG_VERBOSE }, + { "expect-reply", required_argument, NULL, ARG_EXPECT_REPLY }, + { "auto-start", required_argument, NULL, ARG_AUTO_START }, + { "allow-interactive-authorization", required_argument, NULL, ARG_ALLOW_INTERACTIVE_AUTHORIZATION }, + { "timeout", required_argument, NULL, ARG_TIMEOUT }, + { "augment-creds",required_argument, NULL, ARG_AUGMENT_CREDS}, {}, }; - int c; + int c, r; assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "hH:M:", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "hH:M:q", options, NULL)) >= 0) switch (c) { @@ -438,6 +1807,28 @@ static int parse_argv(int argc, char *argv[]) { return log_oom(); break; + case ARG_SIZE: { + off_t o; + + r = parse_size(optarg, 0, &o); + if (r < 0) { + log_error("Failed to parse size: %s", optarg); + return r; + } + + if ((off_t) (size_t) o != o) { + log_error("Size out of range."); + return -E2BIG; + } + + arg_snaplen = (size_t) o; + break; + } + + case ARG_LIST: + arg_list = true; + break; + case 'H': arg_transport = BUS_TRANSPORT_REMOTE; arg_host = optarg; @@ -448,6 +1839,65 @@ static int parse_argv(int argc, char *argv[]) { arg_host = optarg; break; + case 'q': + arg_quiet = true; + break; + + case ARG_VERBOSE: + arg_verbose = true; + break; + + case ARG_EXPECT_REPLY: + r = parse_boolean(optarg); + if (r < 0) { + log_error("Failed to parse --expect-reply= parameter."); + return r; + } + + arg_expect_reply = !!r; + break; + + + case ARG_AUTO_START: + r = parse_boolean(optarg); + if (r < 0) { + log_error("Failed to parse --auto-start= parameter."); + return r; + } + + arg_auto_start = !!r; + break; + + + case ARG_ALLOW_INTERACTIVE_AUTHORIZATION: + r = parse_boolean(optarg); + if (r < 0) { + log_error("Failed to parse --allow-interactive-authorization= parameter."); + return r; + } + + arg_allow_interactive_authorization = !!r; + break; + + case ARG_TIMEOUT: + r = parse_sec(optarg, &arg_timeout); + if (r < 0) { + log_error("Failed to parse --timeout= parameter."); + return r; + } + + break; + + case ARG_AUGMENT_CREDS: + r = parse_boolean(optarg); + if (r < 0) { + log_error("Failed to parse --augment-creds= parameter."); + return r; + } + + arg_augment_creds = !!r; + break; + case '?': return -EINVAL; @@ -455,9 +1905,6 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached("Unhandled option"); } - if (!arg_unique && !arg_acquired && !arg_activatable) - arg_unique = arg_acquired = arg_activatable = true; - return 1; } @@ -469,11 +1916,29 @@ static int busctl_main(sd_bus *bus, int argc, char *argv[]) { return list_bus_names(bus, argv + optind); if (streq(argv[optind], "monitor")) - return monitor(bus, argv + optind); + return monitor(bus, argv + optind, message_dump); + + if (streq(argv[optind], "capture")) + return capture(bus, argv + optind); if (streq(argv[optind], "status")) return status(bus, argv + optind); + if (streq(argv[optind], "tree")) + return tree(bus, argv + optind); + + if (streq(argv[optind], "introspect")) + return introspect(bus, argv + optind); + + if (streq(argv[optind], "call")) + return call(bus, argv + optind); + + if (streq(argv[optind], "get-property")) + return get_property(bus, argv + optind); + + if (streq(argv[optind], "set-property")) + return set_property(bus, argv + optind); + if (streq(argv[optind], "help")) return help(); @@ -494,33 +1959,34 @@ int main(int argc, char *argv[]) { r = sd_bus_new(&bus); if (r < 0) { - log_error("Failed to allocate bus: %s", strerror(-r)); + log_error_errno(r, "Failed to allocate bus: %m"); goto finish; } - if (streq_ptr(argv[optind], "monitor")) { + if (streq_ptr(argv[optind], "monitor") || + streq_ptr(argv[optind], "capture")) { r = sd_bus_set_monitor(bus, true); if (r < 0) { - log_error("Failed to set monitor mode: %s", strerror(-r)); + log_error_errno(r, "Failed to set monitor mode: %m"); goto finish; } - r = sd_bus_negotiate_creds(bus, _SD_BUS_CREDS_ALL); + r = sd_bus_negotiate_creds(bus, true, _SD_BUS_CREDS_ALL); if (r < 0) { - log_error("Failed to enable credentials: %s", strerror(-r)); + log_error_errno(r, "Failed to enable credentials: %m"); goto finish; } r = sd_bus_negotiate_timestamp(bus, true); if (r < 0) { - log_error("Failed to enable timestamps: %s", strerror(-r)); + log_error_errno(r, "Failed to enable timestamps: %m"); goto finish; } r = sd_bus_negotiate_fds(bus, true); if (r < 0) { - log_error("Failed to enable fds: %s", strerror(-r)); + log_error_errno(r, "Failed to enable fds: %m"); goto finish; } } @@ -528,13 +1994,22 @@ int main(int argc, char *argv[]) { if (arg_address) r = sd_bus_set_address(bus, arg_address); else { + r = sd_bus_set_bus_client(bus, true); + if (r < 0) { + log_error_errno(r, "Failed to set bus client: %m"); + goto finish; + } + switch (arg_transport) { case BUS_TRANSPORT_LOCAL: - if (arg_user) + if (arg_user) { + bus->is_user = true; r = bus_set_address_user(bus); - else + } else { + bus->is_system = true; r = bus_set_address_system(bus); + } break; case BUS_TRANSPORT_REMOTE: @@ -550,19 +2025,13 @@ int main(int argc, char *argv[]) { } } if (r < 0) { - log_error("Failed to set address: %s", strerror(-r)); - goto finish; - } - - r = sd_bus_set_bus_client(bus, true); - if (r < 0) { - log_error("Failed to set bus client: %s", strerror(-r)); + log_error_errno(r, "Failed to set address: %m"); goto finish; } r = sd_bus_start(bus); if (r < 0) { - log_error("Failed to connect to bus: %s", strerror(-r)); + log_error_errno(r, "Failed to connect to bus: %m"); goto finish; } diff --git a/src/libsystemd/sd-bus/kdbus.h b/src/libsystemd/sd-bus/kdbus.h index 2ebf405d7d..e2262de6ba 100644 --- a/src/libsystemd/sd-bus/kdbus.h +++ b/src/libsystemd/sd-bus/kdbus.h @@ -58,25 +58,41 @@ struct kdbus_notify_name_change { /** * struct kdbus_creds - process credentials * @uid: User ID + * @euid: Effective UID + * @suid: Saved UID + * @fsuid: Filesystem UID * @gid: Group ID + * @egid: Effective GID + * @sgid: Saved GID + * @fsgid: Filesystem GID + * + * Attached to: + * KDBUS_ITEM_CREDS + */ +struct kdbus_creds { + __u32 uid; + __u32 euid; + __u32 suid; + __u32 fsuid; + __u32 gid; + __u32 egid; + __u32 sgid; + __u32 fsgid; +}; + +/** + * struct kdbus_pids - process identifiers * @pid: Process ID * @tid: Thread ID - * @starttime: Starttime of the process * - * The starttime of the process PID. This is useful to detect PID overruns - * from the client side. i.e. if you use the PID to look something up in - * /proc/$PID/ you can afterwards check the starttime field of it, to ensure - * you didn't run into a PID overrun. + * The PID and TID of a process. * * Attached to: - * KDBUS_ITEM_CREDS + * KDBUS_ITEM_PIDS */ -struct kdbus_creds { - __u64 uid; - __u64 gid; +struct kdbus_pids { __u64 pid; __u64 tid; - __u64 starttime; }; /** @@ -103,8 +119,8 @@ struct kdbus_caps { * KDBUS_ITEM_AUDIT */ struct kdbus_audit { - __u64 sessionid; - __u64 loginuid; + __u32 sessionid; + __u32 loginuid; }; /** @@ -162,7 +178,8 @@ struct kdbus_bloom_filter { /** * struct kdbus_memfd - a kdbus memfd - * @size: The memfd's size + * @start: The offset into the memfd where the segment starts + * @size: The size of the memfd segment * @fd: The file descriptor number * @__pad: Padding to ensure proper alignment and size * @@ -170,6 +187,7 @@ struct kdbus_bloom_filter { * KDBUS_ITEM_PAYLOAD_MEMFD */ struct kdbus_memfd { + __u64 start; __u64 size; int fd; __u32 __pad; @@ -181,7 +199,7 @@ struct kdbus_memfd { * @name: Well-known name * * Attached to: - * KDBUS_ITEM_NAME + * KDBUS_ITEM_OWNED_NAME */ struct kdbus_name { __u64 flags; @@ -203,49 +221,67 @@ struct kdbus_policy_access { /** * enum kdbus_item_type - item types to chain data in a list - * @_KDBUS_ITEM_NULL: Uninitialized/invalid - * @_KDBUS_ITEM_USER_BASE: Start of user items - * @KDBUS_ITEM_PAYLOAD_VEC: Vector to data - * @KDBUS_ITEM_PAYLOAD_OFF: Data at returned offset to message head - * @KDBUS_ITEM_PAYLOAD_MEMFD: Data as sealed memfd - * @KDBUS_ITEM_FDS: Attached file descriptors - * @KDBUS_ITEM_BLOOM_PARAMETER: Bus-wide bloom parameters, used with - * KDBUS_CMD_BUS_MAKE, carries a - * struct kdbus_bloom_parameter - * @KDBUS_ITEM_BLOOM_FILTER: Bloom filter carried with a message, used to - * match against a bloom mask of a connection, - * carries a struct kdbus_bloom_filter - * @KDBUS_ITEM_BLOOM_MASK: Bloom mask used to match against a message's - * bloom filter - * @KDBUS_ITEM_DST_NAME: Destination's well-known name - * @KDBUS_ITEM_MAKE_NAME: Name of domain, bus, endpoint - * @KDBUS_ITEM_ATTACH_FLAGS: Attach-flags, used for updating which metadata - * a connection subscribes to - * @_KDBUS_ITEM_ATTACH_BASE: Start of metadata attach items - * @KDBUS_ITEM_NAME: Well-know name with flags - * @KDBUS_ITEM_ID: Connection ID - * @KDBUS_ITEM_TIMESTAMP: Timestamp - * @KDBUS_ITEM_CREDS: Process credential - * @KDBUS_ITEM_AUXGROUPS: Auxiliary process groups - * @KDBUS_ITEM_PID_COMM: Process ID "comm" identifier - * @KDBUS_ITEM_TID_COMM: Thread ID "comm" identifier - * @KDBUS_ITEM_EXE: The path of the executable - * @KDBUS_ITEM_CMDLINE: The process command line - * @KDBUS_ITEM_CGROUP: The croup membership - * @KDBUS_ITEM_CAPS: The process capabilities - * @KDBUS_ITEM_SECLABEL: The security label - * @KDBUS_ITEM_AUDIT: The audit IDs - * @KDBUS_ITEM_CONN_NAME: The connection's human-readable name (debugging) - * @_KDBUS_ITEM_POLICY_BASE: Start of policy items - * @KDBUS_ITEM_POLICY_ACCESS: Policy access block - * @_KDBUS_ITEM_KERNEL_BASE: Start of kernel-generated message items - * @KDBUS_ITEM_NAME_ADD: Notify in struct kdbus_notify_name_change - * @KDBUS_ITEM_NAME_REMOVE: Notify in struct kdbus_notify_name_change - * @KDBUS_ITEM_NAME_CHANGE: Notify in struct kdbus_notify_name_change - * @KDBUS_ITEM_ID_ADD: Notify in struct kdbus_notify_id_change - * @KDBUS_ITEM_ID_REMOVE: Notify in struct kdbus_notify_id_change - * @KDBUS_ITEM_REPLY_TIMEOUT: Timeout has been reached - * @KDBUS_ITEM_REPLY_DEAD: Destination died + * @_KDBUS_ITEM_NULL: Uninitialized/invalid + * @_KDBUS_ITEM_USER_BASE: Start of user items + * @KDBUS_ITEM_PAYLOAD_VEC: Vector to data + * @KDBUS_ITEM_PAYLOAD_OFF: Data at returned offset to message head + * @KDBUS_ITEM_PAYLOAD_MEMFD: Data as sealed memfd + * @KDBUS_ITEM_FDS: Attached file descriptors + * @KDBUS_ITEM_BLOOM_PARAMETER: Bus-wide bloom parameters, used with + * KDBUS_CMD_BUS_MAKE, carries a + * struct kdbus_bloom_parameter + * @KDBUS_ITEM_BLOOM_FILTER: Bloom filter carried with a message, + * used to match against a bloom mask of a + * connection, carries a struct + * kdbus_bloom_filter + * @KDBUS_ITEM_BLOOM_MASK: Bloom mask used to match against a + * message'sbloom filter + * @KDBUS_ITEM_DST_NAME: Destination's well-known name + * @KDBUS_ITEM_MAKE_NAME: Name of domain, bus, endpoint + * @KDBUS_ITEM_ATTACH_FLAGS_SEND: Attach-flags, used for updating which + * metadata a connection opts in to send + * @KDBUS_ITEM_ATTACH_FLAGS_RECV: Attach-flags, used for updating which + * metadata a connection requests to + * receive for each reeceived message + * @KDBUS_ITEM_ID: Connection ID + * @KDBUS_ITEM_NAME: Well-know name with flags + * @_KDBUS_ITEM_ATTACH_BASE: Start of metadata attach items + * @KDBUS_ITEM_TIMESTAMP: Timestamp + * @KDBUS_ITEM_CREDS: Process credentials + * @KDBUS_ITEM_PIDS: Process identifiers + * @KDBUS_ITEM_AUXGROUPS: Auxiliary process groups + * @KDBUS_ITEM_OWNED_NAME: A name owned by the associated + * connection + * @KDBUS_ITEM_TID_COMM: Thread ID "comm" identifier + * (Don't trust this, see below.) + * @KDBUS_ITEM_PID_COMM: Process ID "comm" identifier + * (Don't trust this, see below.) + * @KDBUS_ITEM_EXE: The path of the executable + * (Don't trust this, see below.) + * @KDBUS_ITEM_CMDLINE: The process command line + * (Don't trust this, see below.) + * @KDBUS_ITEM_CGROUP: The croup membership + * @KDBUS_ITEM_CAPS: The process capabilities + * @KDBUS_ITEM_SECLABEL: The security label + * @KDBUS_ITEM_AUDIT: The audit IDs + * @KDBUS_ITEM_CONN_DESCRIPTION: The connection's human-readable name + * (debugging) + * @_KDBUS_ITEM_POLICY_BASE: Start of policy items + * @KDBUS_ITEM_POLICY_ACCESS: Policy access block + * @_KDBUS_ITEM_KERNEL_BASE: Start of kernel-generated message items + * @KDBUS_ITEM_NAME_ADD: Notification in kdbus_notify_name_change + * @KDBUS_ITEM_NAME_REMOVE: Notification in kdbus_notify_name_change + * @KDBUS_ITEM_NAME_CHANGE: Notification in kdbus_notify_name_change + * @KDBUS_ITEM_ID_ADD: Notification in kdbus_notify_id_change + * @KDBUS_ITEM_ID_REMOVE: Notification in kdbus_notify_id_change + * @KDBUS_ITEM_REPLY_TIMEOUT: Timeout has been reached + * @KDBUS_ITEM_REPLY_DEAD: Destination died + * + * N.B: The process and thread COMM fields, as well as the CMDLINE and + * EXE fields may be altered by unprivileged processes und should + * hence *not* used for security decisions. Peers should make use of + * these items only for informational purposes, such as generating log + * records. */ enum kdbus_item_type { _KDBUS_ITEM_NULL, @@ -259,23 +295,27 @@ enum kdbus_item_type { KDBUS_ITEM_BLOOM_MASK, KDBUS_ITEM_DST_NAME, KDBUS_ITEM_MAKE_NAME, - KDBUS_ITEM_ATTACH_FLAGS, + KDBUS_ITEM_ATTACH_FLAGS_SEND, + KDBUS_ITEM_ATTACH_FLAGS_RECV, + KDBUS_ITEM_ID, + KDBUS_ITEM_NAME, + /* keep these item types in sync with KDBUS_ATTACH_* flags */ _KDBUS_ITEM_ATTACH_BASE = 0x1000, - KDBUS_ITEM_NAME = _KDBUS_ITEM_ATTACH_BASE, - KDBUS_ITEM_ID, - KDBUS_ITEM_TIMESTAMP, + KDBUS_ITEM_TIMESTAMP = _KDBUS_ITEM_ATTACH_BASE, KDBUS_ITEM_CREDS, + KDBUS_ITEM_PIDS, KDBUS_ITEM_AUXGROUPS, - KDBUS_ITEM_PID_COMM, + KDBUS_ITEM_OWNED_NAME, KDBUS_ITEM_TID_COMM, + KDBUS_ITEM_PID_COMM, KDBUS_ITEM_EXE, KDBUS_ITEM_CMDLINE, KDBUS_ITEM_CGROUP, KDBUS_ITEM_CAPS, KDBUS_ITEM_SECLABEL, KDBUS_ITEM_AUDIT, - KDBUS_ITEM_CONN_NAME, + KDBUS_ITEM_CONN_DESCRIPTION, _KDBUS_ITEM_POLICY_BASE = 0x2000, KDBUS_ITEM_POLICY_ACCESS = _KDBUS_ITEM_POLICY_BASE, @@ -326,6 +366,7 @@ struct kdbus_item { __u64 id; struct kdbus_vec vec; struct kdbus_creds creds; + struct kdbus_pids pids; struct kdbus_audit audit; struct kdbus_caps caps; struct kdbus_timestamp timestamp; @@ -445,6 +486,16 @@ enum kdbus_recv_flags { * @offset: Returned offset in the pool where the message is * stored. The user must use KDBUS_CMD_FREE to free * the allocated memory. + * @dropped_msgs: In case the KDBUS_CMD_MSG_RECV ioctl returns + * -EOVERFLOW, this field will contain the number of + * broadcast messages that have been lost since the + * last call. + * @msg_size: Filled by the kernel with the actual message size. This + * is the full size of the slice placed at @offset. It + * includes the memory used for the kdbus_msg object, but + * also for all appended VECs. By using @msg_size and + * @offset, you can map a single message, instead of + * mapping the whole pool. * * This struct is used with the KDBUS_CMD_MSG_RECV ioctl. */ @@ -452,13 +503,17 @@ struct kdbus_cmd_recv { __u64 flags; __u64 kernel_flags; __s64 priority; - __u64 offset; + union { + __u64 offset; + __u64 dropped_msgs; + }; + __u64 msg_size; } __attribute__((aligned(8))); /** * struct kdbus_cmd_cancel - struct to cancel a synchronously pending message - * @cookie The cookie of the pending message - * @flags Flags for the free command. Currently unused. + * @cookie: The cookie of the pending message + * @flags: Flags for the free command. Currently unused. * * This struct is used with the KDBUS_CMD_CANCEL ioctl. */ @@ -525,46 +580,54 @@ enum kdbus_policy_type { * a service * @KDBUS_HELLO_MONITOR: Special-purpose connection to monitor * bus traffic + * @KDBUS_HELLO_UNPRIVILEGED: Don't treat this connection as privileged once + * the bus connection was established. */ enum kdbus_hello_flags { KDBUS_HELLO_ACCEPT_FD = 1ULL << 0, KDBUS_HELLO_ACTIVATOR = 1ULL << 1, KDBUS_HELLO_POLICY_HOLDER = 1ULL << 2, KDBUS_HELLO_MONITOR = 1ULL << 3, + KDBUS_HELLO_UNPRIVILEGED = 1ULL << 4, }; /** * enum kdbus_attach_flags - flags for metadata attachments - * @KDBUS_ATTACH_TIMESTAMP: Timestamp - * @KDBUS_ATTACH_CREDS: Credentials - * @KDBUS_ATTACH_AUXGROUPS: Auxiliary groups - * @KDBUS_ATTACH_NAMES: Well-known names - * @KDBUS_ATTACH_COMM_TID: The "comm" process identifier of the TID - * @KDBUS_ATTACH_COMM_PID: The "comm" process identifier of the PID - * @KDBUS_ATTACH_EXE: The path of the executable - * @KDBUS_ATTACH_CMDLINE: The process command line - * @KDBUS_ATTACH_CGROUP: The croup membership - * @KDBUS_ATTACH_CAPS: The process capabilities - * @KDBUS_ATTACH_SECLABEL: The security label - * @KDBUS_ATTACH_AUDIT: The audit IDs - * @KDBUS_ATTACH_CONN_NAME: The human-readable connection name - * @_KDBUS_ATTACH_ALL: All of the above + * @KDBUS_ATTACH_TIMESTAMP: Timestamp + * @KDBUS_ATTACH_CREDS: Credentials + * @KDBUS_ATTACH_PIDS: PIDs + * @KDBUS_ATTACH_AUXGROUPS: Auxiliary groups + * @KDBUS_ATTACH_NAMES: Well-known names + * @KDBUS_ATTACH_TID_COMM: The "comm" process identifier of the TID + * @KDBUS_ATTACH_PID_COMM: The "comm" process identifier of the PID + * @KDBUS_ATTACH_EXE: The path of the executable + * @KDBUS_ATTACH_CMDLINE: The process command line + * @KDBUS_ATTACH_CGROUP: The croup membership + * @KDBUS_ATTACH_CAPS: The process capabilities + * @KDBUS_ATTACH_SECLABEL: The security label + * @KDBUS_ATTACH_AUDIT: The audit IDs + * @KDBUS_ATTACH_CONN_DESCRIPTION: The human-readable connection name + * @_KDBUS_ATTACH_ALL: All of the above + * @_KDBUS_ATTACH_ANY: Wildcard match to enable any kind of + * metatdata. */ enum kdbus_attach_flags { KDBUS_ATTACH_TIMESTAMP = 1ULL << 0, KDBUS_ATTACH_CREDS = 1ULL << 1, - KDBUS_ATTACH_AUXGROUPS = 1ULL << 2, - KDBUS_ATTACH_NAMES = 1ULL << 3, - KDBUS_ATTACH_TID_COMM = 1ULL << 4, - KDBUS_ATTACH_PID_COMM = 1ULL << 5, - KDBUS_ATTACH_EXE = 1ULL << 6, - KDBUS_ATTACH_CMDLINE = 1ULL << 7, - KDBUS_ATTACH_CGROUP = 1ULL << 8, - KDBUS_ATTACH_CAPS = 1ULL << 9, - KDBUS_ATTACH_SECLABEL = 1ULL << 10, - KDBUS_ATTACH_AUDIT = 1ULL << 11, - KDBUS_ATTACH_CONN_NAME = 1ULL << 12, - _KDBUS_ATTACH_ALL = (1ULL << 13) - 1, + KDBUS_ATTACH_PIDS = 1ULL << 2, + KDBUS_ATTACH_AUXGROUPS = 1ULL << 3, + KDBUS_ATTACH_NAMES = 1ULL << 4, + KDBUS_ATTACH_TID_COMM = 1ULL << 5, + KDBUS_ATTACH_PID_COMM = 1ULL << 6, + KDBUS_ATTACH_EXE = 1ULL << 7, + KDBUS_ATTACH_CMDLINE = 1ULL << 8, + KDBUS_ATTACH_CGROUP = 1ULL << 9, + KDBUS_ATTACH_CAPS = 1ULL << 10, + KDBUS_ATTACH_SECLABEL = 1ULL << 11, + KDBUS_ATTACH_AUDIT = 1ULL << 12, + KDBUS_ATTACH_CONN_DESCRIPTION = 1ULL << 13, + _KDBUS_ATTACH_ALL = (1ULL << 14) - 1, + _KDBUS_ATTACH_ANY = ~0ULL }; /** @@ -572,8 +635,10 @@ enum kdbus_attach_flags { * @size: The total size of the structure * @flags: Connection flags (KDBUS_HELLO_*), userspace → kernel * @kernel_flags: Supported connection flags, kernel → userspace - * @attach_flags: Mask of metadata to attach to each message sent - * (KDBUS_ATTACH_*) + * @attach_flags_send: Mask of metadata to attach to each message sent + * off by this connection (KDBUS_ATTACH_*) + * @attach_flags_recv: Mask of metadata to attach to each message receieved + * by the new connection (KDBUS_ATTACH_*) * @bus_flags: The flags field copied verbatim from the original * KDBUS_CMD_BUS_MAKE ioctl. It's intended to be useful * to do negotiation of features of the payload that is @@ -592,7 +657,8 @@ struct kdbus_cmd_hello { __u64 size; __u64 flags; __u64 kernel_flags; - __u64 attach_flags; + __u64 attach_flags_send; + __u64 attach_flags_recv; __u64 bus_flags; __u64 id; __u64 pool_size; @@ -603,8 +669,8 @@ struct kdbus_cmd_hello { /** * enum kdbus_make_flags - Flags for KDBUS_CMD_{BUS,EP,NS}_MAKE - * @KDBUS_MAKE_ACCESS_GROUP: Make the device node group-accessible - * @KDBUS_MAKE_ACCESS_WORLD: Make the device node world-accessible + * @KDBUS_MAKE_ACCESS_GROUP: Make the bus or endpoint node group-accessible + * @KDBUS_MAKE_ACCESS_WORLD: Make the bus or endpoint node world-accessible */ enum kdbus_make_flags { KDBUS_MAKE_ACCESS_GROUP = 1ULL << 0, @@ -619,8 +685,8 @@ enum kdbus_make_flags { * @kernel_flags: Supported flags for the used command, kernel → userspace * @items: Items describing details * - * This structure is used with the KDBUS_CMD_BUS_MAKE, KDBUS_CMD_ENDPOINT_MAKE - * and KDBUS_CMD_DOMAIN_MAKE ioctls. + * This structure is used with the KDBUS_CMD_BUS_MAKE and + * KDBUS_CMD_ENDPOINT_MAKE ioctls. */ struct kdbus_cmd_make { __u64 size; @@ -666,17 +732,15 @@ struct kdbus_cmd_name { /** * struct kdbus_name_info - struct to describe a well-known name * @size: The total size of the struct - * @flags: Flags for a name entry (KDBUS_NAME_*), * @conn_flags: The flags of the owning connection (KDBUS_HELLO_*) * @owner_id: The current owner of the name * @items: Item list, containing the well-known name as - * KDBUS_ITEM_NAME + * KDBUS_ITEM_OWNED_NAME * * This structure is used as return struct for the KDBUS_CMD_NAME_LIST ioctl. */ struct kdbus_name_info { __u64 size; - __u64 flags; __u64 conn_flags; __u64 owner_id; struct kdbus_item items[0]; @@ -699,11 +763,12 @@ enum kdbus_name_list_flags { /** * struct kdbus_cmd_name_list - request a list of name entries * @flags: Flags for the query (KDBUS_NAME_LIST_*), - * userspace → kernel + * userspace → kernel * @kernel_flags: Supported flags for queries, kernel → userspace * @offset: The returned offset in the caller's pool buffer. * The user must use KDBUS_CMD_FREE to free the * allocated memory. + * @size: Output buffer to report size of data at @offset. * * This structure is used with the KDBUS_CMD_NAME_LIST ioctl. */ @@ -711,6 +776,7 @@ struct kdbus_cmd_name_list { __u64 flags; __u64 kernel_flags; __u64 offset; + __u64 size; } __attribute__((aligned(8))); /** @@ -737,6 +803,7 @@ struct kdbus_name_list { * @offset: Returned offset in the caller's pool buffer where the * kdbus_info struct result is stored. The user must * use KDBUS_CMD_FREE to free the allocated memory. + * @info_size: Output buffer to report size of data at @offset. * @items: The optional item list, containing the * well-known name to look up as a KDBUS_ITEM_NAME. * Only needed in case @id is zero. @@ -751,6 +818,7 @@ struct kdbus_cmd_info { __u64 kernel_flags; __u64 id; __u64 offset; + __u64 info_size; struct kdbus_item items[0]; } __attribute__((aligned(8))); @@ -819,100 +887,94 @@ struct kdbus_cmd_match { } __attribute__((aligned(8))); /** - * enum kdbus_ioctl_type - Ioctl API - * @KDBUS_CMD_BUS_MAKE: After opening the "control" device node, this - * command creates a new bus with the specified + * Ioctl API + * KDBUS_CMD_BUS_MAKE: After opening the "control" node, this command + * creates a new bus with the specified * name. The bus is immediately shut down and - * cleaned up when the opened "control" device node - * is closed. - * @KDBUS_CMD_DOMAIN_MAKE: Similar to KDBUS_CMD_BUS_MAKE, but it creates a - * new kdbus domain. - * @KDBUS_CMD_ENDPOINT_MAKE: Creates a new named special endpoint to talk to + * cleaned up when the opened file descriptor is + * closed. + * KDBUS_CMD_ENDPOINT_MAKE: Creates a new named special endpoint to talk to * the bus. Such endpoints usually carry a more * restrictive policy and grant restricted access * to specific applications. - * @KDBUS_CMD_HELLO: By opening the bus device node a connection is + * KDBUS_CMD_HELLO: By opening the bus node, a connection is * created. After a HELLO the opened connection * becomes an active peer on the bus. - * @KDBUS_CMD_BYEBYE: Disconnect a connection. If there are no + * KDBUS_CMD_BYEBYE: Disconnect a connection. If there are no * messages queued up in the connection's pool, * the call succeeds, and the handle is rendered * unusable. Otherwise, -EBUSY is returned without * any further side-effects. - * @KDBUS_CMD_MSG_SEND: Send a message and pass data from userspace to + * KDBUS_CMD_MSG_SEND: Send a message and pass data from userspace to * the kernel. - * @KDBUS_CMD_MSG_RECV: Receive a message from the kernel which is + * KDBUS_CMD_MSG_RECV: Receive a message from the kernel which is * placed in the receiver's pool. - * @KDBUS_CMD_MSG_CANCEL: Cancel a pending request of a message that + * KDBUS_CMD_MSG_CANCEL: Cancel a pending request of a message that * blocks while waiting for a reply. The parameter * denotes the cookie of the message in flight. - * @KDBUS_CMD_FREE: Release the allocated memory in the receiver's + * KDBUS_CMD_FREE: Release the allocated memory in the receiver's * pool. - * @KDBUS_CMD_NAME_ACQUIRE: Request a well-known bus name to associate with + * KDBUS_CMD_NAME_ACQUIRE: Request a well-known bus name to associate with * the connection. Well-known names are used to * address a peer on the bus. - * @KDBUS_CMD_NAME_RELEASE: Release a well-known name the connection + * KDBUS_CMD_NAME_RELEASE: Release a well-known name the connection * currently owns. - * @KDBUS_CMD_NAME_LIST: Retrieve the list of all currently registered + * KDBUS_CMD_NAME_LIST: Retrieve the list of all currently registered * well-known and unique names. - * @KDBUS_CMD_CONN_INFO: Retrieve credentials and properties of the + * KDBUS_CMD_CONN_INFO: Retrieve credentials and properties of the * initial creator of the connection. The data was * stored at registration time and does not * necessarily represent the connected process or * the actual state of the process. - * @KDBUS_CMD_CONN_UPDATE: Update the properties of a connection. Used to + * KDBUS_CMD_CONN_UPDATE: Update the properties of a connection. Used to * update the metadata subscription mask and * policy. - * @KDBUS_CMD_BUS_CREATOR_INFO: Retrieve information of the creator of the bus + * KDBUS_CMD_BUS_CREATOR_INFO: Retrieve information of the creator of the bus * a connection is attached to. - * @KDBUS_CMD_ENDPOINT_UPDATE: Update the properties of a custom enpoint. Used + * KDBUS_CMD_ENDPOINT_UPDATE: Update the properties of a custom enpoint. Used * to update the policy. - * @KDBUS_CMD_MATCH_ADD: Install a match which broadcast messages should + * KDBUS_CMD_MATCH_ADD: Install a match which broadcast messages should * be delivered to the connection. - * @KDBUS_CMD_MATCH_REMOVE: Remove a current match for broadcast messages. - */ -enum kdbus_ioctl_type { - KDBUS_CMD_BUS_MAKE = _IOW(KDBUS_IOCTL_MAGIC, 0x00, - struct kdbus_cmd_make), - KDBUS_CMD_DOMAIN_MAKE = _IOW(KDBUS_IOCTL_MAGIC, 0x10, - struct kdbus_cmd_make), - KDBUS_CMD_ENDPOINT_MAKE = _IOW(KDBUS_IOCTL_MAGIC, 0x20, - struct kdbus_cmd_make), - - KDBUS_CMD_HELLO = _IOWR(KDBUS_IOCTL_MAGIC, 0x30, - struct kdbus_cmd_hello), - KDBUS_CMD_BYEBYE = _IO(KDBUS_IOCTL_MAGIC, 0x31), - - KDBUS_CMD_MSG_SEND = _IOWR(KDBUS_IOCTL_MAGIC, 0x40, - struct kdbus_msg), - KDBUS_CMD_MSG_RECV = _IOWR(KDBUS_IOCTL_MAGIC, 0x41, - struct kdbus_cmd_recv), - KDBUS_CMD_MSG_CANCEL = _IOW(KDBUS_IOCTL_MAGIC, 0x42, - struct kdbus_cmd_cancel), - KDBUS_CMD_FREE = _IOW(KDBUS_IOCTL_MAGIC, 0x43, - struct kdbus_cmd_free), - - KDBUS_CMD_NAME_ACQUIRE = _IOWR(KDBUS_IOCTL_MAGIC, 0x50, - struct kdbus_cmd_name), - KDBUS_CMD_NAME_RELEASE = _IOW(KDBUS_IOCTL_MAGIC, 0x51, - struct kdbus_cmd_name), - KDBUS_CMD_NAME_LIST = _IOWR(KDBUS_IOCTL_MAGIC, 0x52, - struct kdbus_cmd_name_list), - - KDBUS_CMD_CONN_INFO = _IOWR(KDBUS_IOCTL_MAGIC, 0x60, - struct kdbus_cmd_info), - KDBUS_CMD_CONN_UPDATE = _IOW(KDBUS_IOCTL_MAGIC, 0x61, - struct kdbus_cmd_update), - KDBUS_CMD_BUS_CREATOR_INFO = _IOWR(KDBUS_IOCTL_MAGIC, 0x62, - struct kdbus_cmd_info), - - KDBUS_CMD_ENDPOINT_UPDATE = _IOW(KDBUS_IOCTL_MAGIC, 0x71, - struct kdbus_cmd_update), - - KDBUS_CMD_MATCH_ADD = _IOW(KDBUS_IOCTL_MAGIC, 0x80, - struct kdbus_cmd_match), - KDBUS_CMD_MATCH_REMOVE = _IOW(KDBUS_IOCTL_MAGIC, 0x81, - struct kdbus_cmd_match), -}; + * KDBUS_CMD_MATCH_REMOVE: Remove a current match for broadcast messages. + */ +#define KDBUS_CMD_BUS_MAKE _IOW(KDBUS_IOCTL_MAGIC, 0x00, \ + struct kdbus_cmd_make) +#define KDBUS_CMD_ENDPOINT_MAKE _IOW(KDBUS_IOCTL_MAGIC, 0x10, \ + struct kdbus_cmd_make) + +#define KDBUS_CMD_HELLO _IOWR(KDBUS_IOCTL_MAGIC, 0x20, \ + struct kdbus_cmd_hello) +#define KDBUS_CMD_BYEBYE _IO(KDBUS_IOCTL_MAGIC, 0x21) \ + +#define KDBUS_CMD_MSG_SEND _IOWR(KDBUS_IOCTL_MAGIC, 0x30, \ + struct kdbus_msg) +#define KDBUS_CMD_MSG_RECV _IOWR(KDBUS_IOCTL_MAGIC, 0x31, \ + struct kdbus_cmd_recv) +#define KDBUS_CMD_MSG_CANCEL _IOW(KDBUS_IOCTL_MAGIC, 0x32, \ + struct kdbus_cmd_cancel) +#define KDBUS_CMD_FREE _IOW(KDBUS_IOCTL_MAGIC, 0x33, \ + struct kdbus_cmd_free) + +#define KDBUS_CMD_NAME_ACQUIRE _IOWR(KDBUS_IOCTL_MAGIC, 0x40, \ + struct kdbus_cmd_name) +#define KDBUS_CMD_NAME_RELEASE _IOW(KDBUS_IOCTL_MAGIC, 0x41, \ + struct kdbus_cmd_name) +#define KDBUS_CMD_NAME_LIST _IOWR(KDBUS_IOCTL_MAGIC, 0x42, \ + struct kdbus_cmd_name_list) + +#define KDBUS_CMD_CONN_INFO _IOWR(KDBUS_IOCTL_MAGIC, 0x50, \ + struct kdbus_cmd_info) +#define KDBUS_CMD_CONN_UPDATE _IOW(KDBUS_IOCTL_MAGIC, 0x51, \ + struct kdbus_cmd_update) +#define KDBUS_CMD_BUS_CREATOR_INFO _IOWR(KDBUS_IOCTL_MAGIC, 0x52, \ + struct kdbus_cmd_info) + +#define KDBUS_CMD_ENDPOINT_UPDATE _IOW(KDBUS_IOCTL_MAGIC, 0x61, \ + struct kdbus_cmd_update) + +#define KDBUS_CMD_MATCH_ADD _IOW(KDBUS_IOCTL_MAGIC, 0x70, \ + struct kdbus_cmd_match) +#define KDBUS_CMD_MATCH_REMOVE _IOW(KDBUS_IOCTL_MAGIC, 0x71, \ + struct kdbus_cmd_match) #endif /* _KDBUS_UAPI_H_ */ diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c index 03ec6036fa..ef0b15185f 100644 --- a/src/libsystemd/sd-bus/sd-bus.c +++ b/src/libsystemd/sd-bus/sd-bus.c @@ -129,7 +129,7 @@ static void bus_free(sd_bus *b) { free(b->machine); free(b->fake_label); free(b->cgroup_root); - free(b->connection_name); + free(b->description); free(b->exec_path); strv_free(b->exec_argv); @@ -274,24 +274,50 @@ _public_ int sd_bus_negotiate_fds(sd_bus *bus, int b) { } _public_ int sd_bus_negotiate_timestamp(sd_bus *bus, int b) { + uint64_t new_flags; assert_return(bus, -EINVAL); - assert_return(bus->state == BUS_UNSET, -EPERM); + assert_return(!IN_SET(bus->state, BUS_CLOSING, BUS_CLOSED), -EPERM); assert_return(!bus_pid_changed(bus), -ECHILD); - SET_FLAG(bus->attach_flags, KDBUS_ATTACH_TIMESTAMP, b); + new_flags = bus->attach_flags; + SET_FLAG(new_flags, KDBUS_ATTACH_TIMESTAMP, b); + + if (bus->attach_flags == new_flags) + return 0; + + bus->attach_flags = new_flags; + if (bus->state != BUS_UNSET && bus->is_kernel) + bus_kernel_realize_attach_flags(bus); + return 0; } -_public_ int sd_bus_negotiate_creds(sd_bus *bus, uint64_t mask) { +_public_ int sd_bus_negotiate_creds(sd_bus *bus, int b, uint64_t mask) { + uint64_t new_flags; + assert_return(bus, -EINVAL); assert_return(mask <= _SD_BUS_CREDS_ALL, -EINVAL); - assert_return(bus->state == BUS_UNSET, -EPERM); + assert_return(!IN_SET(bus->state, BUS_CLOSING, BUS_CLOSED), -EPERM); assert_return(!bus_pid_changed(bus), -ECHILD); + if (b) + bus->creds_mask |= mask; + else + bus->creds_mask &= ~mask; + /* The well knowns we need unconditionally, so that matches can work */ - bus->creds_mask = mask | SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_UNIQUE_NAME; + bus->creds_mask |= SD_BUS_CREDS_WELL_KNOWN_NAMES|SD_BUS_CREDS_UNIQUE_NAME; + + /* Make sure we don't lose the timestamp flag */ + new_flags = (bus->attach_flags & KDBUS_ATTACH_TIMESTAMP) | attach_flags_to_kdbus(bus->creds_mask); + if (bus->attach_flags == new_flags) + return 0; - return kdbus_translate_attach_flags(bus->creds_mask, &bus->attach_flags); + bus->attach_flags = new_flags; + if (bus->state != BUS_UNSET && bus->is_kernel) + bus_kernel_realize_attach_flags(bus); + + return 0; } _public_ int sd_bus_set_server(sd_bus *bus, int b, sd_id128_t server_id) { @@ -323,22 +349,12 @@ _public_ int sd_bus_set_trusted(sd_bus *bus, int b) { return 0; } -_public_ int sd_bus_set_name(sd_bus *bus, const char *name) { - char *n; - +_public_ int sd_bus_set_description(sd_bus *bus, const char *description) { assert_return(bus, -EINVAL); - assert_return(name, -EINVAL); assert_return(bus->state == BUS_UNSET, -EPERM); assert_return(!bus_pid_changed(bus), -ECHILD); - n = strdup(name); - if (!n) - return -ENOMEM; - - free(bus->connection_name); - bus->connection_name = n; - - return 0; + return free_and_strdup(&bus->description, description); } static int hello_callback(sd_bus *bus, sd_bus_message *reply, void *userdata, sd_bus_error *error) { @@ -817,7 +833,7 @@ static int parse_container_kernel_address(sd_bus *b, const char **p, char **guid machine = NULL; free(b->kernel); - b->kernel = strdup("/dev/kdbus/0-system/bus"); + b->kernel = strdup("/sys/fs/kdbus/0-system/bus"); if (!b->kernel) return -ENOMEM; @@ -1081,6 +1097,7 @@ _public_ int sd_bus_open(sd_bus **ret) { * be safe, and authenticate everything */ b->trusted = false; b->attach_flags |= KDBUS_ATTACH_CAPS | KDBUS_ATTACH_CREDS; + b->creds_mask |= SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_EFFECTIVE_CAPS; r = sd_bus_start(b); if (r < 0) @@ -1102,7 +1119,7 @@ int bus_set_address_system(sd_bus *b) { if (e) return sd_bus_set_address(b, e); - return sd_bus_set_address(b, DEFAULT_SYSTEM_BUS_PATH); + return sd_bus_set_address(b, DEFAULT_SYSTEM_BUS_ADDRESS); } _public_ int sd_bus_open_system(sd_bus **ret) { @@ -1126,6 +1143,7 @@ _public_ int sd_bus_open_system(sd_bus **ret) { * need the caller's UID and capability set for that. */ b->trusted = false; b->attach_flags |= KDBUS_ATTACH_CAPS | KDBUS_ATTACH_CREDS; + b->creds_mask |= SD_BUS_CREDS_UID | SD_BUS_CREDS_EUID | SD_BUS_CREDS_EFFECTIVE_CAPS; r = sd_bus_start(b); if (r < 0) @@ -1157,13 +1175,13 @@ int bus_set_address_user(sd_bus *b) { return -ENOMEM; #ifdef ENABLE_KDBUS - (void) asprintf(&b->address, KERNEL_USER_BUS_FMT ";" UNIX_USER_BUS_FMT, getuid(), ee); + (void) asprintf(&b->address, KERNEL_USER_BUS_ADDRESS_FMT ";" UNIX_USER_BUS_ADDRESS_FMT, getuid(), ee); #else - (void) asprintf(&b->address, UNIX_USER_BUS_FMT, ee); + (void) asprintf(&b->address, UNIX_USER_BUS_ADDRESS_FMT, ee); #endif } else { #ifdef ENABLE_KDBUS - (void) asprintf(&b->address, KERNEL_USER_BUS_FMT, getuid()); + (void) asprintf(&b->address, KERNEL_USER_BUS_ADDRESS_FMT, getuid()); #else return -ECONNREFUSED; #endif @@ -1265,6 +1283,7 @@ _public_ int sd_bus_open_system_remote(sd_bus **ret, const char *host) { bus->bus_client = true; bus->trusted = false; + bus->is_system = true; r = sd_bus_start(bus); if (r < 0) @@ -1317,6 +1336,7 @@ _public_ int sd_bus_open_system_container(sd_bus **ret, const char *machine) { bus->bus_client = true; bus->trusted = false; + bus->is_system = true; r = sd_bus_start(bus); if (r < 0) @@ -1422,18 +1442,18 @@ _public_ int sd_bus_can_send(sd_bus *bus, char type) { return bus_type_is_valid(type); } -_public_ int sd_bus_get_server_id(sd_bus *bus, sd_id128_t *server_id) { +_public_ int sd_bus_get_bus_id(sd_bus *bus, sd_id128_t *id) { int r; assert_return(bus, -EINVAL); - assert_return(server_id, -EINVAL); + assert_return(id, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); r = bus_ensure_running(bus); if (r < 0) return r; - *server_id = bus->server_id; + *id = bus->server_id; return 0; } @@ -2487,6 +2507,15 @@ null_message: return r; } +static void bus_message_set_sender_local(sd_bus *bus, sd_bus_message *m) { + assert(bus); + assert(m); + + m->sender = m->creds.unique_name = (char*) "org.freedesktop.DBus.Local"; + m->creds.well_known_names_local = true; + m->creds.mask |= (SD_BUS_CREDS_UNIQUE_NAME|SD_BUS_CREDS_WELL_KNOWN_NAMES) & bus->creds_mask; +} + static int process_closing(sd_bus *bus, sd_bus_message **ret) { _cleanup_bus_message_unref_ sd_bus_message *m = NULL; struct reply_callback *c; @@ -2555,7 +2584,7 @@ static int process_closing(sd_bus *bus, sd_bus_message **ret) { if (r < 0) return r; - m->sender = "org.freedesktop.DBus.Local"; + bus_message_set_sender_local(bus, m); r = bus_seal_synthetic_message(bus, m); if (r < 0) @@ -3015,7 +3044,7 @@ static int attach_io_events(sd_bus *bus) { if (r < 0) return r; - r = sd_event_source_set_name(bus->input_io_event_source, "bus-input"); + r = sd_event_source_set_description(bus->input_io_event_source, "bus-input"); } else r = sd_event_source_set_io_fd(bus->input_io_event_source, bus->input_fd); @@ -3034,7 +3063,7 @@ static int attach_io_events(sd_bus *bus) { if (r < 0) return r; - r = sd_event_source_set_name(bus->input_io_event_source, "bus-output"); + r = sd_event_source_set_description(bus->input_io_event_source, "bus-output"); } else r = sd_event_source_set_io_fd(bus->output_io_event_source, bus->output_fd); @@ -3087,7 +3116,7 @@ _public_ int sd_bus_attach_event(sd_bus *bus, sd_event *event, int priority) { if (r < 0) goto fail; - r = sd_event_source_set_name(bus->time_event_source, "bus-time"); + r = sd_event_source_set_description(bus->time_event_source, "bus-time"); if (r < 0) goto fail; @@ -3095,7 +3124,7 @@ _public_ int sd_bus_attach_event(sd_bus *bus, sd_event *event, int priority) { if (r < 0) goto fail; - r = sd_event_source_set_name(bus->quit_event_source, "bus-exit"); + r = sd_event_source_set_description(bus->quit_event_source, "bus-exit"); if (r < 0) goto fail; @@ -3322,12 +3351,13 @@ _public_ int sd_bus_try_close(sd_bus *bus) { return 0; } -_public_ int sd_bus_get_name(sd_bus *bus, const char **name) { +_public_ int sd_bus_get_description(sd_bus *bus, const char **description) { assert_return(bus, -EINVAL); - assert_return(name, -EINVAL); + assert_return(description, -EINVAL); + assert_return(bus->description, -ENXIO); assert_return(!bus_pid_changed(bus), -ECHILD); - *name = bus->connection_name; + *description = bus->description; return 0; } @@ -3348,3 +3378,101 @@ int bus_get_root_path(sd_bus *bus) { return r; } + +_public_ int sd_bus_get_scope(sd_bus *bus, const char **scope) { + int r; + + assert_return(bus, -EINVAL); + assert_return(scope, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (bus->is_kernel) { + _cleanup_free_ char *n = NULL; + const char *dash; + + r = bus_kernel_get_bus_name(bus, &n); + if (r < 0) + return r; + + if (streq(n, "0-system")) { + *scope = "system"; + return 0; + } + + dash = strchr(n, '-'); + if (streq_ptr(dash, "-user")) { + *scope = "user"; + return 0; + } + } + + if (bus->is_user) { + *scope = "user"; + return 0; + } + + if (bus->is_system) { + *scope = "system"; + return 0; + } + + return -ENODATA; +} + +_public_ int sd_bus_get_address(sd_bus *bus, const char **address) { + + assert_return(bus, -EINVAL); + assert_return(address, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (bus->address) { + *address = bus->address; + return 0; + } + + return -ENODATA; +} + +int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *mask) { + assert_return(bus, -EINVAL); + assert_return(mask, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + *mask = bus->creds_mask; + return 0; +} + +int sd_bus_is_bus_client(sd_bus *bus) { + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + return bus->bus_client; +} + +int sd_bus_is_server(sd_bus *bus) { + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + return bus->is_server; +} + +int sd_bus_is_anonymous(sd_bus *bus) { + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + return bus->anonymous_auth; +} + +int sd_bus_is_trusted(sd_bus *bus) { + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + return bus->trusted; +} + +int sd_bus_is_monitor(sd_bus *bus) { + assert_return(bus, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + return !!(bus->hello_flags & KDBUS_HELLO_MONITOR); +} diff --git a/src/libsystemd/sd-bus/test-bus-chat.c b/src/libsystemd/sd-bus/test-bus-chat.c index d5903f41d5..06edd621e4 100644 --- a/src/libsystemd/sd-bus/test-bus-chat.c +++ b/src/libsystemd/sd-bus/test-bus-chat.c @@ -53,10 +53,8 @@ static int object_callback(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bu log_info("Invoked Foobar() on %s", sd_bus_message_get_path(m)); r = sd_bus_reply_method_return(m, NULL); - if (r < 0) { - log_error("Failed to send reply: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to send reply: %m"); return 1; } @@ -74,19 +72,19 @@ static int server_init(sd_bus **_bus) { r = sd_bus_open_user(&bus); if (r < 0) { - log_error("Failed to connect to user bus: %s", strerror(-r)); + log_error_errno(r, "Failed to connect to user bus: %m"); goto fail; } - r = sd_bus_get_server_id(bus, &id); + r = sd_bus_get_bus_id(bus, &id); if (r < 0) { - log_error("Failed to get server ID: %s", strerror(-r)); + log_error_errno(r, "Failed to get server ID: %m"); goto fail; } r = sd_bus_get_unique_name(bus, &unique); if (r < 0) { - log_error("Failed to get unique name: %s", strerror(-r)); + log_error_errno(r, "Failed to get unique name: %m"); goto fail; } @@ -96,25 +94,25 @@ static int server_init(sd_bus **_bus) { r = sd_bus_request_name(bus, "org.freedesktop.systemd.test", 0); if (r < 0) { - log_error("Failed to acquire name: %s", strerror(-r)); + log_error_errno(r, "Failed to acquire name: %m"); goto fail; } r = sd_bus_add_fallback(bus, NULL, "/foo/bar", object_callback, NULL); if (r < 0) { - log_error("Failed to add object: %s", strerror(-r)); + log_error_errno(r, "Failed to add object: %m"); goto fail; } r = sd_bus_add_match(bus, NULL, "type='signal',interface='foo.bar',member='Notify'", match_callback, NULL); if (r < 0) { - log_error("Failed to add match: %s", strerror(-r)); + log_error_errno(r, "Failed to add match: %m"); goto fail; } r = sd_bus_add_match(bus, NULL, "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged'", match_callback, NULL); if (r < 0) { - log_error("Failed to add match: %s", strerror(-r)); + log_error_errno(r, "Failed to add match: %m"); goto fail; } @@ -141,14 +139,14 @@ static int server(sd_bus *bus) { r = sd_bus_process(bus, &m); if (r < 0) { - log_error("Failed to process requests: %s", strerror(-r)); + log_error_errno(r, "Failed to process requests: %m"); goto fail; } if (r == 0) { r = sd_bus_wait(bus, (uint64_t) -1); if (r < 0) { - log_error("Failed to wait: %s", strerror(-r)); + log_error_errno(r, "Failed to wait: %m"); goto fail; } @@ -173,7 +171,7 @@ static int server(sd_bus *bus) { r = sd_bus_message_read(m, "s", &hello); if (r < 0) { - log_error("Failed to get parameter: %s", strerror(-r)); + log_error_errno(r, "Failed to get parameter: %m"); goto fail; } @@ -187,14 +185,14 @@ static int server(sd_bus *bus) { r = sd_bus_reply_method_return(m, "s", lowercase); if (r < 0) { - log_error("Failed to send reply: %s", strerror(-r)); + log_error_errno(r, "Failed to send reply: %m"); goto fail; } } else if (sd_bus_message_is_method_call(m, "org.freedesktop.systemd.test", "ExitClient1")) { r = sd_bus_reply_method_return(m, NULL); if (r < 0) { - log_error("Failed to send reply: %s", strerror(-r)); + log_error_errno(r, "Failed to send reply: %m"); goto fail; } @@ -203,7 +201,7 @@ static int server(sd_bus *bus) { r = sd_bus_reply_method_return(m, NULL); if (r < 0) { - log_error("Failed to send reply: %s", strerror(-r)); + log_error_errno(r, "Failed to send reply: %m"); goto fail; } @@ -214,7 +212,7 @@ static int server(sd_bus *bus) { r = sd_bus_reply_method_return(m, NULL); if (r < 0) { - log_error("Failed to send reply: %s", strerror(-r)); + log_error_errno(r, "Failed to send reply: %m"); goto fail; } @@ -224,21 +222,21 @@ static int server(sd_bus *bus) { r = sd_bus_message_read(m, "h", &fd); if (r < 0) { - log_error("Failed to get parameter: %s", strerror(-r)); + log_error_errno(r, "Failed to get parameter: %m"); goto fail; } log_info("Received fd=%d", fd); if (write(fd, &x, 1) < 0) { - log_error("Failed to write to fd: %m"); + log_error_errno(errno, "Failed to write to fd: %m"); safe_close(fd); goto fail; } r = sd_bus_reply_method_return(m, NULL); if (r < 0) { - log_error("Failed to send reply: %s", strerror(-r)); + log_error_errno(r, "Failed to send reply: %m"); goto fail; } @@ -248,7 +246,7 @@ static int server(sd_bus *bus) { m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNKNOWN_METHOD, "Unknown method.")); if (r < 0) { - log_error("Failed to send reply: %s", strerror(-r)); + log_error_errno(r, "Failed to send reply: %m"); goto fail; } } @@ -276,7 +274,7 @@ static void* client1(void*p) { r = sd_bus_open_user(&bus); if (r < 0) { - log_error("Failed to connect to user bus: %s", strerror(-r)); + log_error_errno(r, "Failed to connect to user bus: %m"); goto finish; } @@ -291,20 +289,20 @@ static void* client1(void*p) { "s", "HELLO"); if (r < 0) { - log_error("Failed to issue method call: %s", strerror(-r)); + log_error_errno(r, "Failed to issue method call: %m"); goto finish; } r = sd_bus_message_read(reply, "s", &hello); if (r < 0) { - log_error("Failed to get string: %s", strerror(-r)); + log_error_errno(r, "Failed to get string: %m"); goto finish; } assert(streq(hello, "hello")); if (pipe2(pp, O_CLOEXEC|O_NONBLOCK) < 0) { - log_error("Failed to allocate pipe: %m"); + log_error_errno(errno, "Failed to allocate pipe: %m"); r = -errno; goto finish; } @@ -322,7 +320,7 @@ static void* client1(void*p) { "h", pp[1]); if (r < 0) { - log_error("Failed to issue method call: %s", strerror(-r)); + log_error_errno(r, "Failed to issue method call: %m"); goto finish; } @@ -346,7 +344,7 @@ finish: "org.freedesktop.systemd.test", "ExitClient1"); if (r < 0) - log_error("Failed to allocate method call: %s", strerror(-r)); + log_error_errno(r, "Failed to allocate method call: %m"); else sd_bus_send(bus, q, NULL); @@ -380,7 +378,7 @@ static void* client2(void*p) { r = sd_bus_open_user(&bus); if (r < 0) { - log_error("Failed to connect to user bus: %s", strerror(-r)); + log_error_errno(r, "Failed to connect to user bus: %m"); goto finish; } @@ -392,7 +390,7 @@ static void* client2(void*p) { "org.object.test", "Foobar"); if (r < 0) { - log_error("Failed to allocate method call: %s", strerror(-r)); + log_error_errno(r, "Failed to allocate method call: %m"); goto finish; } @@ -412,7 +410,7 @@ static void* client2(void*p) { "foo.bar", "Notify"); if (r < 0) { - log_error("Failed to allocate signal: %s", strerror(-r)); + log_error_errno(r, "Failed to allocate signal: %m"); goto finish; } @@ -433,7 +431,7 @@ static void* client2(void*p) { "org.freedesktop.DBus.Peer", "GetMachineId"); if (r < 0) { - log_error("Failed to allocate method call: %s", strerror(-r)); + log_error_errno(r, "Failed to allocate method call: %m"); goto finish; } @@ -445,7 +443,7 @@ static void* client2(void*p) { r = sd_bus_message_read(reply, "s", &mid); if (r < 0) { - log_error("Failed to parse machine ID: %s", strerror(-r)); + log_error_errno(r, "Failed to parse machine ID: %m"); goto finish; } @@ -462,7 +460,7 @@ static void* client2(void*p) { "org.freedesktop.systemd.test", "Slow"); if (r < 0) { - log_error("Failed to allocate method call: %s", strerror(-r)); + log_error_errno(r, "Failed to allocate method call: %m"); goto finish; } @@ -486,7 +484,7 @@ static void* client2(void*p) { "org.freedesktop.systemd.test", "Slow"); if (r < 0) { - log_error("Failed to allocate method call: %s", strerror(-r)); + log_error_errno(r, "Failed to allocate method call: %m"); goto finish; } @@ -499,13 +497,13 @@ static void* client2(void*p) { while (!quit) { r = sd_bus_process(bus, NULL); if (r < 0) { - log_error("Failed to process requests: %s", strerror(-r)); + log_error_errno(r, "Failed to process requests: %m"); goto finish; } if (r == 0) { r = sd_bus_wait(bus, (uint64_t) -1); if (r < 0) { - log_error("Failed to wait: %s", strerror(-r)); + log_error_errno(r, "Failed to wait: %m"); goto finish; } } @@ -525,7 +523,7 @@ finish: "org.freedesktop.systemd.test", "ExitClient2"); if (r < 0) { - log_error("Failed to allocate method call: %s", strerror(-r)); + log_error_errno(r, "Failed to allocate method call: %m"); goto finish; } diff --git a/src/libsystemd/sd-bus/test-bus-creds.c b/src/libsystemd/sd-bus/test-bus-creds.c index c4894e8696..ff2602ba34 100644 --- a/src/libsystemd/sd-bus/test-bus-creds.c +++ b/src/libsystemd/sd-bus/test-bus-creds.c @@ -31,7 +31,7 @@ int main(int argc, char *argv[]) { r = sd_bus_creds_new_from_pid(&creds, 0, _SD_BUS_CREDS_ALL); assert_se(r >= 0); - bus_creds_dump(creds, NULL); + bus_creds_dump(creds, NULL, true); creds = sd_bus_creds_unref(creds); @@ -39,7 +39,7 @@ int main(int argc, char *argv[]) { if (r != -EACCES) { assert_se(r >= 0); putchar('\n'); - bus_creds_dump(creds, NULL); + bus_creds_dump(creds, NULL, true); } return 0; diff --git a/src/libsystemd/sd-bus/test-bus-error.c b/src/libsystemd/sd-bus/test-bus-error.c index b78be5499c..463fc81c75 100644 --- a/src/libsystemd/sd-bus/test-bus-error.c +++ b/src/libsystemd/sd-bus/test-bus-error.c @@ -22,9 +22,10 @@ #include "sd-bus.h" #include "bus-error.h" #include "bus-util.h" +#include "errno-list.h" +#include "bus-common-errors.h" -int main(int argc, char *argv[]) { - +static void test_error(void) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL, second = SD_BUS_ERROR_NULL; const sd_bus_error const_error = SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_EXISTS, "const error"); const sd_bus_error temporarily_const_error = { @@ -110,6 +111,92 @@ int main(int argc, char *argv[]) { assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_IO_ERROR)); assert_se(sd_bus_error_get_errno(&error) == EIO); assert_se(sd_bus_error_is_set(&error)); +} + +extern const sd_bus_error_map __start_BUS_ERROR_MAP[]; +extern const sd_bus_error_map __stop_BUS_ERROR_MAP[]; + +static void dump_mapping_table(void) { + const sd_bus_error_map *m; + + printf("----- errno mappings ------\n"); + m = __start_BUS_ERROR_MAP; + while (m < __stop_BUS_ERROR_MAP) { + + if (m->code == BUS_ERROR_MAP_END_MARKER) { + m = ALIGN8_PTR(m+1); + continue; + } + + printf("%s -> %i/%s\n", strna(m->name), m->code, strna(errno_to_name(m->code))); + m ++; + } + printf("---------------------------\n"); +} + +static void test_errno_mapping_standard(void) { + assert_se(sd_bus_error_set(NULL, "System.Error.EUCLEAN", NULL) == -EUCLEAN); + assert_se(sd_bus_error_set(NULL, "System.Error.EBUSY", NULL) == -EBUSY); + assert_se(sd_bus_error_set(NULL, "System.Error.EINVAL", NULL) == -EINVAL); + assert_se(sd_bus_error_set(NULL, "System.Error.WHATSIT", NULL) == -EIO); +} + +BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map test_errors[] = { + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error", 5), + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-2", 52), + SD_BUS_ERROR_MAP_END +}; + +BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map test_errors2[] = { + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-3", 33), + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-4", 44), + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-33", 333), + SD_BUS_ERROR_MAP_END +}; + +static const sd_bus_error_map test_errors3[] = { + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-88", 888), + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-99", 999), + SD_BUS_ERROR_MAP_END +}; + +static const sd_bus_error_map test_errors4[] = { + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-77", 777), + SD_BUS_ERROR_MAP("org.freedesktop.custom-dbus-error-78", 778), + SD_BUS_ERROR_MAP_END +}; + +static void test_errno_mapping_custom(void) { + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error", NULL) == -5); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-2", NULL) == -52); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-x", NULL) == -EIO); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-33", NULL) == -333); + + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-88", NULL) == -EIO); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-99", NULL) == -EIO); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-77", NULL) == -EIO); + + assert_se(sd_bus_error_add_map(test_errors3) > 0); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-88", NULL) == -888); + assert_se(sd_bus_error_add_map(test_errors4) > 0); + assert_se(sd_bus_error_add_map(test_errors4) == 0); + assert_se(sd_bus_error_add_map(test_errors3) == 0); + + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-99", NULL) == -999); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-77", NULL) == -777); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-78", NULL) == -778); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-2", NULL) == -52); + assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-y", NULL) == -EIO); + + assert_se(sd_bus_error_set(NULL, BUS_ERROR_NO_SUCH_UNIT, NULL) == -ENOENT); +} + +int main(int argc, char *argv[]) { + dump_mapping_table(); + + test_error(); + test_errno_mapping_standard(); + test_errno_mapping_custom(); return 0; } diff --git a/src/libsystemd/sd-bus/test-bus-gvariant.c b/src/libsystemd/sd-bus/test-bus-gvariant.c index 9226858825..56df5d0b48 100644 --- a/src/libsystemd/sd-bus/test-bus-gvariant.c +++ b/src/libsystemd/sd-bus/test-bus-gvariant.c @@ -175,14 +175,14 @@ static void test_marshal(void) { } #endif - assert_se(bus_message_dump(m, NULL, true) >= 0); + assert_se(bus_message_dump(m, NULL, BUS_MESSAGE_DUMP_WITH_HEADER) >= 0); assert_se(bus_message_get_blob(m, &blob, &sz) >= 0); assert_se(bus_message_from_malloc(bus, blob, sz, NULL, 0, NULL, NULL, &n) >= 0); blob = NULL; - assert_se(bus_message_dump(n, NULL, true) >= 0); + assert_se(bus_message_dump(n, NULL, BUS_MESSAGE_DUMP_WITH_HEADER) >= 0); m = sd_bus_message_unref(m); @@ -191,7 +191,7 @@ static void test_marshal(void) { assert_se(sd_bus_message_append(m, "as", 0) >= 0); assert_se(bus_message_seal(m, 4712, 0) >= 0); - assert_se(bus_message_dump(m, NULL, true) >= 0); + assert_se(bus_message_dump(m, NULL, BUS_MESSAGE_DUMP_WITH_HEADER) >= 0); } int main(int argc, char *argv[]) { diff --git a/src/libsystemd/sd-bus/test-bus-kernel-bloom.c b/src/libsystemd/sd-bus/test-bus-kernel-bloom.c index 5ee6eea0e8..071b7e0cf9 100644 --- a/src/libsystemd/sd-bus/test-bus-kernel-bloom.c +++ b/src/libsystemd/sd-bus/test-bus-kernel-bloom.c @@ -32,6 +32,7 @@ static void test_one( const char *path, const char *interface, const char *member, + bool as_list, const char *arg0, const char *match, bool good) { @@ -76,7 +77,11 @@ static void test_one( assert_se(r >= 0); log_debug("signal"); - r = sd_bus_emit_signal(a, path, interface, member, "s", arg0); + + if (as_list) + r = sd_bus_emit_signal(a, path, interface, member, "as", 1, arg0); + else + r = sd_bus_emit_signal(a, path, interface, member, "s", arg0); assert_se(r >= 0); r = sd_bus_process(b, &m); @@ -89,27 +94,29 @@ static void test_one( int main(int argc, char *argv[]) { log_set_max_level(LOG_DEBUG); - test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path='/foo/bar/waldo'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path='/foo/bar/waldo/tuut'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "interface='waldo.com'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "member='Piep'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "member='Pi_ep'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "arg0='foobar'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "arg0='foo_bar'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path='/foo/bar/waldo',interface='waldo.com',member='Piep',arg0='foobar'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path='/foo/bar/waldo',interface='waldo.com',member='Piep',arg0='foobar2'", false); - - test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path='/foo/bar/waldo'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path='/foo/bar'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path='/foo'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path='/'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path='/foo/bar/waldo/quux'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path_namespace='/foo/bar/waldo'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path_namespace='/foo/bar'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path_namespace='/foo'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path_namespace='/'", true); - test_one("/foo/bar/waldo", "waldo.com", "Piep", "foobar", "path_namespace='/quux'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo/tuut'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "interface='waldo.com'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "member='Piep'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "member='Pi_ep'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "arg0='foobar'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "arg0='foo_bar'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", true, "foobar", "arg0='foobar'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", true, "foobar", "arg0='foo_bar'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo',interface='waldo.com',member='Piep',arg0='foobar'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo',interface='waldo.com',member='Piep',arg0='foobar2'", false); + + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo/quux'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/foo/bar/waldo'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/foo/bar'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/foo'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path_namespace='/quux'", false); return 0; } diff --git a/src/libsystemd/sd-bus/test-bus-kernel.c b/src/libsystemd/sd-bus/test-bus-kernel.c index 7bb8b0a540..3aec568229 100644 --- a/src/libsystemd/sd-bus/test-bus-kernel.c +++ b/src/libsystemd/sd-bus/test-bus-kernel.c @@ -33,7 +33,7 @@ int main(int argc, char *argv[]) { _cleanup_close_ int bus_ref = -1; - _cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL; + _cleanup_free_ char *name = NULL, *bus_name = NULL, *address = NULL, *bname = NULL; _cleanup_bus_message_unref_ sd_bus_message *m = NULL; _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; const char *ua = NULL, *ub = NULL, *the_string = NULL; @@ -45,6 +45,8 @@ int main(int argc, char *argv[]) { assert_se(asprintf(&name, "deine-mutter-%u", (unsigned) getpid()) >= 0); + bus_kernel_fix_attach_mask(); + bus_ref = bus_kernel_create_bus(name, false, &bus_name); if (bus_ref == -ENOENT) return EXIT_TEST_SKIP; @@ -60,7 +62,7 @@ int main(int argc, char *argv[]) { r = sd_bus_new(&b); assert_se(r >= 0); - r = sd_bus_set_name(a, "a"); + r = sd_bus_set_description(a, "a"); assert_se(r >= 0); r = sd_bus_set_address(a, address); @@ -70,10 +72,10 @@ int main(int argc, char *argv[]) { assert_se(r >= 0); assert_se(sd_bus_negotiate_timestamp(a, 1) >= 0); - assert_se(sd_bus_negotiate_creds(a, _SD_BUS_CREDS_ALL) >= 0); + assert_se(sd_bus_negotiate_creds(a, true, _SD_BUS_CREDS_ALL) >= 0); - assert_se(sd_bus_negotiate_timestamp(b, 1) >= 0); - assert_se(sd_bus_negotiate_creds(b, _SD_BUS_CREDS_ALL) >= 0); + assert_se(sd_bus_negotiate_timestamp(b, 0) >= 0); + assert_se(sd_bus_negotiate_creds(b, true, 0) >= 0); r = sd_bus_start(a); assert_se(r >= 0); @@ -81,11 +83,14 @@ int main(int argc, char *argv[]) { r = sd_bus_start(b); assert_se(r >= 0); + assert_se(sd_bus_negotiate_timestamp(b, 1) >= 0); + assert_se(sd_bus_negotiate_creds(b, true, _SD_BUS_CREDS_ALL) >= 0); + r = sd_bus_get_unique_name(a, &ua); assert_se(r >= 0); printf("unique a: %s\n", ua); - r = sd_bus_get_name(a, &nn); + r = sd_bus_get_description(a, &nn); assert_se(r >= 0); printf("name of a: %s\n", nn); @@ -93,10 +98,13 @@ int main(int argc, char *argv[]) { assert_se(r >= 0); printf("unique b: %s\n", ub); - r = sd_bus_get_name(b, &nn); + r = sd_bus_get_description(b, &nn); assert_se(r >= 0); printf("name of b: %s\n", nn); + assert_se(bus_kernel_get_bus_name(b, &bname) >= 0); + assert_se(endswith(bname, name)); + r = sd_bus_call_method(a, "this.doesnt.exist", "/foo", "meh.mah", "muh", &error, NULL, "s", "yayayay"); assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_SERVICE_UNKNOWN)); assert_se(r == -EHOSTUNREACH); @@ -117,7 +125,7 @@ int main(int argc, char *argv[]) { assert_se(r > 0); assert_se(m); - bus_message_dump(m, stdout, true); + bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); assert_se(sd_bus_message_rewind(m, true) >= 0); r = sd_bus_message_read(m, "s", &the_string); @@ -154,7 +162,7 @@ int main(int argc, char *argv[]) { assert_se(r > 0); assert_se(m); - bus_message_dump(m, stdout, true); + bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); assert_se(sd_bus_message_rewind(m, true) >= 0); if (sd_bus_message_is_method_call(m, "an.inter.face", "AMethod")) { diff --git a/src/libsystemd/sd-bus/test-bus-marshal.c b/src/libsystemd/sd-bus/test-bus-marshal.c index 9532112524..8cefc7a154 100644 --- a/src/libsystemd/sd-bus/test-bus-marshal.c +++ b/src/libsystemd/sd-bus/test-bus-marshal.c @@ -148,10 +148,10 @@ int main(int argc, char *argv[]) { r = bus_message_seal(m, 4711, 0); assert_se(r >= 0); - bus_message_dump(m, stdout, true); + bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); ms = open_memstream(&first, &first_size); - bus_message_dump(m, ms, false); + bus_message_dump(m, ms, 0); fflush(ms); assert_se(!ferror(ms)); @@ -201,11 +201,11 @@ int main(int argc, char *argv[]) { r = bus_message_from_malloc(bus, buffer, sz, NULL, 0, NULL, NULL, &m); assert_se(r >= 0); - bus_message_dump(m, stdout, true); + bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); fclose(ms); ms = open_memstream(&second, &second_size); - bus_message_dump(m, ms, false); + bus_message_dump(m, ms, 0); fflush(ms); assert_se(!ferror(ms)); assert_se(first_size == second_size); @@ -285,7 +285,7 @@ int main(int argc, char *argv[]) { fclose(ms); ms = open_memstream(&third, &third_size); - bus_message_dump(copy, ms, false); + bus_message_dump(copy, ms, 0); fflush(ms); assert_se(!ferror(ms)); diff --git a/src/libsystemd/sd-bus/test-bus-match.c b/src/libsystemd/sd-bus/test-bus-match.c index 6c5d35b3d3..7133117038 100644 --- a/src/libsystemd/sd-bus/test-bus-match.c +++ b/src/libsystemd/sd-bus/test-bus-match.c @@ -33,8 +33,9 @@ static bool mask[32]; static int filter(sd_bus *b, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { - log_info("Ran %i", PTR_TO_INT(userdata)); - mask[PTR_TO_INT(userdata)] = true; + log_info("Ran %u", PTR_TO_UINT(userdata)); + assert(PTR_TO_UINT(userdata) < ELEMENTSOF(mask)); + mask[PTR_TO_UINT(userdata)] = true; return 0; } @@ -85,9 +86,9 @@ int main(int argc, char *argv[]) { }; _cleanup_bus_message_unref_ sd_bus_message *m = NULL; - _cleanup_bus_unref_ sd_bus *bus = NULL; + _cleanup_bus_close_unref_ sd_bus *bus = NULL; enum bus_match_node_type i; - sd_bus_slot slots[15]; + sd_bus_slot slots[19]; int r; r = sd_bus_open_system(&bus); @@ -108,16 +109,20 @@ int main(int argc, char *argv[]) { assert_se(match_add(slots, &root, "arg1='two'", 12) >= 0); assert_se(match_add(slots, &root, "member='waldo',arg2path='/prefix/'", 13) >= 0); assert_se(match_add(slots, &root, "member=waldo,path='/foo/bar',arg3namespace='prefix'", 14) >= 0); + assert_se(match_add(slots, &root, "arg4='pi'", 15) >= 0); + assert_se(match_add(slots, &root, "arg4='pa'", 16) >= 0); + assert_se(match_add(slots, &root, "arg4='po'", 17) >= 0); + assert_se(match_add(slots, &root, "arg4='pu'", 18) >= 0); bus_match_dump(&root, 0); assert_se(sd_bus_message_new_signal(bus, &m, "/foo/bar", "bar.x", "waldo") >= 0); - assert_se(sd_bus_message_append(m, "ssss", "one", "two", "/prefix/three", "prefix.four") >= 0); + assert_se(sd_bus_message_append(m, "ssssas", "one", "two", "/prefix/three", "prefix.four", 3, "pi", "pa", "po") >= 0); assert_se(bus_message_seal(m, 1, 0) >= 0); zero(mask); assert_se(bus_match_run(NULL, &root, m) == 0); - assert_se(mask_contains((unsigned[]) { 9, 8, 7, 5, 10, 12, 13, 14 }, 8)); + assert_se(mask_contains((unsigned[]) { 9, 8, 7, 5, 10, 12, 13, 14, 15, 16, 17 }, 11)); assert_se(bus_match_remove(&root, &slots[8].match_callback) >= 0); assert_se(bus_match_remove(&root, &slots[13].match_callback) >= 0); @@ -126,7 +131,7 @@ int main(int argc, char *argv[]) { zero(mask); assert_se(bus_match_run(NULL, &root, m) == 0); - assert_se(mask_contains((unsigned[]) { 9, 5, 10, 12, 14, 7 }, 6)); + assert_se(mask_contains((unsigned[]) { 9, 5, 10, 12, 14, 7, 15, 16, 17 }, 9)); for (i = 0; i < _BUS_MATCH_NODE_TYPE_MAX; i++) { char buf[32]; diff --git a/src/libsystemd/sd-bus/test-bus-objects.c b/src/libsystemd/sd-bus/test-bus-objects.c index e7a445f3cb..06b8904f13 100644 --- a/src/libsystemd/sd-bus/test-bus-objects.c +++ b/src/libsystemd/sd-bus/test-bus-objects.c @@ -237,14 +237,14 @@ static void *server(void *p) { r = sd_bus_process(bus, NULL); if (r < 0) { - log_error("Failed to process requests: %s", strerror(-r)); + log_error_errno(r, "Failed to process requests: %m"); goto fail; } if (r == 0) { r = sd_bus_wait(bus, (uint64_t) -1); if (r < 0) { - log_error("Failed to wait: %s", strerror(-r)); + log_error_errno(r, "Failed to wait: %m"); goto fail; } @@ -385,7 +385,7 @@ static int client(struct context *c) { r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", ""); assert_se(r >= 0); - bus_message_dump(reply, stdout, true); + bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); sd_bus_message_unref(reply); reply = NULL; @@ -403,7 +403,7 @@ static int client(struct context *c) { r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", &error, &reply, ""); assert_se(r >= 0); - bus_message_dump(reply, stdout, true); + bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); sd_bus_message_unref(reply); reply = NULL; @@ -415,7 +415,7 @@ static int client(struct context *c) { assert_se(r > 0); assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.Properties", "PropertiesChanged")); - bus_message_dump(reply, stdout, true); + bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); sd_bus_message_unref(reply); reply = NULL; @@ -427,7 +427,7 @@ static int client(struct context *c) { assert_se(r > 0); assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.Properties", "PropertiesChanged")); - bus_message_dump(reply, stdout, true); + bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); sd_bus_message_unref(reply); reply = NULL; @@ -439,7 +439,7 @@ static int client(struct context *c) { assert_se(r > 0); assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded")); - bus_message_dump(reply, stdout, true); + bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); sd_bus_message_unref(reply); reply = NULL; @@ -451,7 +451,7 @@ static int client(struct context *c) { assert_se(r > 0); assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved")); - bus_message_dump(reply, stdout, true); + bus_message_dump(reply, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); sd_bus_message_unref(reply); reply = NULL; diff --git a/src/libsystemd/sd-bus/test-bus-server.c b/src/libsystemd/sd-bus/test-bus-server.c index 96d6298336..5f807c3b1e 100644 --- a/src/libsystemd/sd-bus/test-bus-server.c +++ b/src/libsystemd/sd-bus/test-bus-server.c @@ -65,14 +65,14 @@ static void *server(void *p) { r = sd_bus_process(bus, &m); if (r < 0) { - log_error("Failed to process requests: %s", strerror(-r)); + log_error_errno(r, "Failed to process requests: %m"); goto fail; } if (r == 0) { r = sd_bus_wait(bus, (uint64_t) -1); if (r < 0) { - log_error("Failed to wait: %s", strerror(-r)); + log_error_errno(r, "Failed to wait: %m"); goto fail; } @@ -90,7 +90,7 @@ static void *server(void *p) { r = sd_bus_message_new_method_return(m, &reply); if (r < 0) { - log_error("Failed to allocate return: %s", strerror(-r)); + log_error_errno(r, "Failed to allocate return: %m"); goto fail; } @@ -102,7 +102,7 @@ static void *server(void *p) { &reply, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNKNOWN_METHOD, "Unknown method.")); if (r < 0) { - log_error("Failed to allocate return: %s", strerror(-r)); + log_error_errno(r, "Failed to allocate return: %m"); goto fail; } } @@ -110,7 +110,7 @@ static void *server(void *p) { if (reply) { r = sd_bus_send(bus, reply, NULL); if (r < 0) { - log_error("Failed to send reply: %s", strerror(-r)); + log_error_errno(r, "Failed to send reply: %m"); goto fail; } } @@ -146,10 +146,8 @@ static int client(struct context *c) { "/", "org.freedesktop.systemd.test", "Exit"); - if (r < 0) { - log_error("Failed to allocate method call: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to allocate method call: %m"); r = sd_bus_call(bus, m, 0, &error, &reply); if (r < 0) { diff --git a/src/libsystemd/sd-bus/test-bus-zero-copy.c b/src/libsystemd/sd-bus/test-bus-zero-copy.c index e3010fbf7e..a054f74bf3 100644 --- a/src/libsystemd/sd-bus/test-bus-zero-copy.c +++ b/src/libsystemd/sd-bus/test-bus-zero-copy.c @@ -24,7 +24,7 @@ #include "util.h" #include "log.h" -#include "memfd.h" +#include "memfd-util.h" #include "sd-bus.h" #include "bus-message.h" @@ -48,6 +48,7 @@ int main(int argc, char *argv[]) { uint32_t u32; size_t i, l; char *s; + _cleanup_close_ int sfd = -1; log_set_max_level(LOG_DEBUG); @@ -107,7 +108,7 @@ int main(int argc, char *argv[]) { assert_se(r >= 0); assert_se(sz == STRING_SIZE); - r = sd_bus_message_append_string_memfd(m, f); + r = sd_bus_message_append_string_memfd(m, f, 0, (uint64_t) -1); assert_se(r >= 0); close(f); @@ -124,7 +125,7 @@ int main(int argc, char *argv[]) { assert_se(r >= 0); assert_se(sz == SECOND_ARRAY); - r = sd_bus_message_append_array_memfd(m, 'y', f); + r = sd_bus_message_append_array_memfd(m, 'y', f, 0, (uint64_t) -1); assert_se(r >= 0); close(f); @@ -135,10 +136,15 @@ int main(int argc, char *argv[]) { r = sd_bus_message_append(m, "u", 4711); assert_se(r >= 0); + assert_se((sfd = memfd_new_and_map(NULL, 6, (void**) &p)) >= 0); + memcpy(p, "abcd\0", 6); + munmap(p, 6); + assert_se(sd_bus_message_append_string_memfd(m, sfd, 1, 4) >= 0); + r = bus_message_seal(m, 55, 99*USEC_PER_SEC); assert_se(r >= 0); - bus_message_dump(m, stdout, true); + bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); r = sd_bus_send(b, m, NULL); assert_se(r >= 0); @@ -148,7 +154,7 @@ int main(int argc, char *argv[]) { r = sd_bus_process(a, &m); assert_se(r > 0); - bus_message_dump(m, stdout, true); + bus_message_dump(m, stdout, BUS_MESSAGE_DUMP_WITH_HEADER); sd_bus_message_rewind(m, true); r = sd_bus_message_enter_container(m, 'r', "aysay"); @@ -188,6 +194,10 @@ int main(int argc, char *argv[]) { assert_se(r > 0); assert_se(u32 == 4711); + r = sd_bus_message_read(m, "s", &s); + assert_se(r > 0); + assert_se(streq_ptr(s, "bcd")); + sd_bus_message_unref(m); sd_bus_unref(a); diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index 80a2ae97e8..f9fa54d2e8 100644 --- a/src/libsystemd/sd-event/sd-event.c +++ b/src/libsystemd/sd-event/sd-event.c @@ -66,7 +66,7 @@ struct sd_event_source { void *userdata; sd_event_handler_t prepare; - char *name; + char *description; EventSourceType type:5; int enabled:3; @@ -737,7 +737,7 @@ static void source_free(sd_event_source *s) { assert(s); source_disconnect(s); - free(s->name); + free(s->description); free(s); } @@ -1031,6 +1031,9 @@ _public_ int sd_event_add_signal( } } + /* Use the signal name as description for the event source by default */ + (void) sd_event_source_set_description(s, signal_to_string(sig)); + if (ret) *ret = s; @@ -1253,18 +1256,20 @@ _public_ sd_event_source* sd_event_source_unref(sd_event_source *s) { return NULL; } -_public_ int sd_event_source_set_name(sd_event_source *s, const char *name) { +_public_ int sd_event_source_set_description(sd_event_source *s, const char *description) { assert_return(s, -EINVAL); + assert_return(!event_pid_changed(s->event), -ECHILD); - return free_and_strdup(&s->name, name); + return free_and_strdup(&s->description, description); } -_public_ int sd_event_source_get_name(sd_event_source *s, const char **name) { +_public_ int sd_event_source_get_description(sd_event_source *s, const char **description) { assert_return(s, -EINVAL); - assert_return(name, -EINVAL); - - *name = s->name; + assert_return(description, -EINVAL); + assert_return(s->description, -ENXIO); + assert_return(!event_pid_changed(s->event), -ECHILD); + *description = s->description; return 0; } @@ -2152,10 +2157,10 @@ static int source_dispatch(sd_event_source *s) { s->dispatching = false; if (r < 0) { - if (s->name) - log_debug("Event source '%s' returned error, disabling: %s", s->name, strerror(-r)); + if (s->description) + log_debug_errno(r, "Event source '%s' returned error, disabling: %m", s->description); else - log_debug("Event source %p returned error, disabling: %s", s, strerror(-r)); + log_debug_errno(r, "Event source %p returned error, disabling: %m", s); } if (s->n_ref == 0) @@ -2190,10 +2195,10 @@ static int event_prepare(sd_event *e) { s->dispatching = false; if (r < 0) { - if (s->name) - log_debug("Prepare callback of event source '%s' returned error, disabling: %s", s->name, strerror(-r)); + if (s->description) + log_debug_errno(r, "Prepare callback of event source '%s' returned error, disabling: %m", s->description); else - log_debug("Prepare callback of event source %p returned error, disabling: %s", s, strerror(-r)); + log_debug_errno(r, "Prepare callback of event source %p returned error, disabling: %m", s); } if (s->n_ref == 0) diff --git a/src/libsystemd/sd-resolve/test-resolve.c b/src/libsystemd/sd-resolve/test-resolve.c index c6c3bfb0ea..a9dc9313a6 100644 --- a/src/libsystemd/sd-resolve/test-resolve.c +++ b/src/libsystemd/sd-resolve/test-resolve.c @@ -43,7 +43,7 @@ static int getaddrinfo_handler(sd_resolve_query *q, int ret, const struct addrin assert(q); if (ret != 0) { - log_error("getaddrinfo error: %s %i\n", gai_strerror(ret), ret); + log_error("getaddrinfo error: %s %i", gai_strerror(ret), ret); return 0; } @@ -63,7 +63,7 @@ static int getnameinfo_handler(sd_resolve_query *q, int ret, const char *host, c assert(q); if (ret != 0) { - log_error("getnameinfo error: %s %i\n", gai_strerror(ret), ret); + log_error("getnameinfo error: %s %i", gai_strerror(ret), ret); return 0; } @@ -80,12 +80,12 @@ static int res_handler(sd_resolve_query *q, int ret, unsigned char *answer, void assert(q); if (ret < 0) { - log_error("res_query() error: %s %i\n", strerror(errno), errno); + log_error("res_query() error: %s %i", strerror(errno), errno); return 0; } if (ret == 0) { - log_error("No reply for SRV lookup\n"); + log_error("No reply for SRV lookup"); return 0; } @@ -146,18 +146,18 @@ int main(int argc, char *argv[]) { /* Make a name -> address query */ r = sd_resolve_getaddrinfo(resolve, &q1, argc >= 2 ? argv[1] : "www.heise.de", NULL, &hints, getaddrinfo_handler, NULL); if (r < 0) - log_error("sd_resolve_getaddrinfo(): %s\n", strerror(-r)); + log_error_errno(r, "sd_resolve_getaddrinfo(): %m"); /* Make an address -> name query */ sa.sin_addr.s_addr = inet_addr(argc >= 3 ? argv[2] : "193.99.144.71"); r = sd_resolve_getnameinfo(resolve, &q2, (struct sockaddr*) &sa, sizeof(sa), 0, SD_RESOLVE_GET_BOTH, getnameinfo_handler, NULL); if (r < 0) - log_error("sd_resolve_getnameinfo(): %s\n", strerror(-r)); + log_error_errno(r, "sd_resolve_getnameinfo(): %m"); /* Make a res_query() call */ r = sd_resolve_res_query(resolve, &q3, "_xmpp-client._tcp.gmail.com", C_IN, T_SRV, res_handler, NULL); if (r < 0) - log_error("sd_resolve_res_query(): %s\n", strerror(-r)); + log_error_errno(r, "sd_resolve_res_query(): %m"); /* Wait until the three queries are completed */ while (sd_resolve_query_is_done(q1) == 0 || @@ -166,7 +166,7 @@ int main(int argc, char *argv[]) { r = sd_resolve_wait(resolve, (uint64_t) -1); if (r < 0) { - log_error("sd_resolve_wait(): %s\n", strerror(-r)); + log_error_errno(r, "sd_resolve_wait(): %m"); assert_not_reached("sd_resolve_wait() failed"); } } diff --git a/src/libsystemd/sd-rtnl/local-addresses.c b/src/libsystemd/sd-rtnl/local-addresses.c index c5508856c8..31bfa06066 100644 --- a/src/libsystemd/sd-rtnl/local-addresses.c +++ b/src/libsystemd/sd-rtnl/local-addresses.c @@ -30,14 +30,19 @@ static int address_compare(const void *_a, const void *_b) { /* Order lowest scope first, IPv4 before IPv6, lowest interface index first */ + if (a->family == AF_INET && b->family == AF_INET6) + return -1; + if (a->family == AF_INET6 && b->family == AF_INET) + return 1; + if (a->scope < b->scope) return -1; if (a->scope > b->scope) return 1; - if (a->family == AF_INET && b->family == AF_INET6) + if (a->metric < b->metric) return -1; - if (a->family == AF_INET6 && b->family == AF_INET) + if (a->metric > b->metric) return 1; if (a->ifindex < b->ifindex) @@ -45,10 +50,10 @@ static int address_compare(const void *_a, const void *_b) { if (a->ifindex > b->ifindex) return 1; - return 0; + return memcmp(&a->address, &b->address, FAMILY_ADDRESS_SIZE(a->family)); } -int local_addresses(sd_rtnl *context, int ifindex, struct local_address **ret) { +int local_addresses(sd_rtnl *context, int ifindex, int af, struct local_address **ret) { _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL; _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL; _cleanup_free_ struct local_address *list = NULL; @@ -66,7 +71,7 @@ int local_addresses(sd_rtnl *context, int ifindex, struct local_address **ret) { return r; } - r = sd_rtnl_message_new_addr(rtnl, &req, RTM_GETADDR, 0, AF_UNSPEC); + r = sd_rtnl_message_new_addr(rtnl, &req, RTM_GETADDR, 0, af); if (r < 0) return r; @@ -78,7 +83,7 @@ int local_addresses(sd_rtnl *context, int ifindex, struct local_address **ret) { struct local_address *a; unsigned char flags; uint16_t type; - int ifi; + int ifi, family; r = sd_rtnl_message_get_errno(m); if (r < 0) @@ -87,25 +92,28 @@ int local_addresses(sd_rtnl *context, int ifindex, struct local_address **ret) { r = sd_rtnl_message_get_type(m, &type); if (r < 0) return r; - if (type != RTM_NEWADDR) continue; r = sd_rtnl_message_addr_get_ifindex(m, &ifi); if (r < 0) return r; + if (ifindex > 0 && ifi != ifindex) + continue; - if (ifindex != 0 && ifi != ifindex) + r = sd_rtnl_message_addr_get_family(m, &family); + if (r < 0) + return r; + if (af != AF_UNSPEC && af != family) continue; r = sd_rtnl_message_addr_get_flags(m, &flags); if (r < 0) return r; - if (flags & IFA_F_DEPRECATED) continue; - if (!GREEDY_REALLOC(list, n_allocated, n_list+1)) + if (!GREEDY_REALLOC0(list, n_allocated, n_list+1)) return -ENOMEM; a = list + n_list; @@ -117,11 +125,7 @@ int local_addresses(sd_rtnl *context, int ifindex, struct local_address **ret) { if (ifindex == 0 && (a->scope == RT_SCOPE_HOST || a->scope == RT_SCOPE_NOWHERE)) continue; - r = sd_rtnl_message_addr_get_family(m, &a->family); - if (r < 0) - return r; - - switch (a->family) { + switch (family) { case AF_INET: r = sd_rtnl_message_read_in_addr(m, IFA_LOCAL, &a->address.in); @@ -146,11 +150,123 @@ int local_addresses(sd_rtnl *context, int ifindex, struct local_address **ret) { } a->ifindex = ifi; + a->family = family; n_list++; }; - if (n_list) + if (n_list > 0) + qsort(list, n_list, sizeof(struct local_address), address_compare); + + *ret = list; + list = NULL; + + return (int) n_list; +} + +int local_gateways(sd_rtnl *context, int ifindex, int af, struct local_address **ret) { + _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL; + _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL; + _cleanup_free_ struct local_address *list = NULL; + sd_rtnl_message *m = NULL; + size_t n_list = 0, n_allocated = 0; + int r; + + assert(ret); + + if (context) + rtnl = sd_rtnl_ref(context); + else { + r = sd_rtnl_open(&rtnl, 0); + if (r < 0) + return r; + } + + r = sd_rtnl_message_new_route(rtnl, &req, RTM_GETROUTE, af, RTPROT_UNSPEC); + if (r < 0) + return r; + + r = sd_rtnl_message_request_dump(req, true); + if (r < 0) + return r; + + r = sd_rtnl_call(rtnl, req, 0, &reply); + if (r < 0) + return r; + + for (m = reply; m; m = sd_rtnl_message_next(m)) { + struct local_address *a; + uint16_t type; + unsigned char dst_len, src_len; + uint32_t ifi; + int family; + + r = sd_rtnl_message_get_errno(m); + if (r < 0) + return r; + + r = sd_rtnl_message_get_type(m, &type); + if (r < 0) + return r; + if (type != RTM_NEWROUTE) + continue; + + /* We only care for default routes */ + r = sd_rtnl_message_route_get_dst_prefixlen(m, &dst_len); + if (r < 0) + return r; + if (dst_len != 0) + continue; + + r = sd_rtnl_message_route_get_src_prefixlen(m, &src_len); + if (r < 0) + return r; + if (src_len != 0) + continue; + + r = sd_rtnl_message_read_u32(m, RTA_OIF, &ifi); + if (r < 0) + return r; + if (ifindex > 0 && (int) ifi != ifindex) + continue; + + r = sd_rtnl_message_route_get_family(m, &family); + if (r < 0) + return r; + if (af != AF_UNSPEC && af != family) + continue; + + if (!GREEDY_REALLOC0(list, n_allocated, n_list + 1)) + return -ENOMEM; + + a = list + n_list; + + switch (family) { + case AF_INET: + r = sd_rtnl_message_read_in_addr(m, RTA_GATEWAY, &a->address.in); + if (r < 0) + continue; + + break; + case AF_INET6: + r = sd_rtnl_message_read_in6_addr(m, RTA_GATEWAY, &a->address.in6); + if (r < 0) + continue; + + break; + default: + continue; + } + + sd_rtnl_message_read_u32(m, RTA_PRIORITY, &a->metric); + + a->ifindex = ifi; + a->family = family; + + n_list++; + } + + if (n_list > 0) qsort(list, n_list, sizeof(struct local_address), address_compare); *ret = list; diff --git a/src/libsystemd/sd-rtnl/local-addresses.h b/src/libsystemd/sd-rtnl/local-addresses.h index b1ed6341f6..ef7def530d 100644 --- a/src/libsystemd/sd-rtnl/local-addresses.h +++ b/src/libsystemd/sd-rtnl/local-addresses.h @@ -32,7 +32,10 @@ struct local_address { int family, ifindex; unsigned char scope; + uint32_t metric; union in_addr_union address; }; -int local_addresses(sd_rtnl *rtnl, int ifindex, struct local_address **ret); +int local_addresses(sd_rtnl *rtnl, int ifindex, int af, struct local_address **ret); + +int local_gateways(sd_rtnl *rtnl, int ifindex, int af, struct local_address **ret); diff --git a/src/libsystemd/sd-rtnl/rtnl-message.c b/src/libsystemd/sd-rtnl/rtnl-message.c index b501a52cf1..165e84d7a0 100644 --- a/src/libsystemd/sd-rtnl/rtnl-message.c +++ b/src/libsystemd/sd-rtnl/rtnl-message.c @@ -36,6 +36,8 @@ #define GET_CONTAINER(m, i) ((i) < (m)->n_containers ? (struct rtattr*)((uint8_t*)(m)->hdr + (m)->container_offsets[i]) : NULL) #define PUSH_CONTAINER(m, new) (m)->container_offsets[(m)->n_containers ++] = (uint8_t*)(new) - (uint8_t*)(m)->hdr; +#define RTA_TYPE(rta) ((rta)->rta_type & NLA_TYPE_MASK) + static int message_new_empty(sd_rtnl *rtnl, sd_rtnl_message **ret) { sd_rtnl_message *m; @@ -112,6 +114,24 @@ int sd_rtnl_message_route_set_dst_prefixlen(sd_rtnl_message *m, unsigned char pr return 0; } +int sd_rtnl_message_route_set_src_prefixlen(sd_rtnl_message *m, unsigned char prefixlen) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + if ((rtm->rtm_family == AF_INET && prefixlen > 32) || + (rtm->rtm_family == AF_INET6 && prefixlen > 128)) + return -ERANGE; + + rtm->rtm_src_len = prefixlen; + + return 0; +} + int sd_rtnl_message_route_set_scope(sd_rtnl_message *m, unsigned char scope) { struct rtmsg *rtm; @@ -126,6 +146,51 @@ int sd_rtnl_message_route_set_scope(sd_rtnl_message *m, unsigned char scope) { return 0; } +int sd_rtnl_message_route_get_family(sd_rtnl_message *m, int *family) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + assert_return(family, -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + *family = rtm->rtm_family; + + return 0; +} + +int sd_rtnl_message_route_get_dst_prefixlen(sd_rtnl_message *m, unsigned char *dst_len) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + assert_return(dst_len, -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + *dst_len = rtm->rtm_dst_len; + + return 0; +} + +int sd_rtnl_message_route_get_src_prefixlen(sd_rtnl_message *m, unsigned char *src_len) { + struct rtmsg *rtm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL); + assert_return(src_len, -EINVAL); + + rtm = NLMSG_DATA(m->hdr); + + *src_len = rtm->rtm_src_len; + + return 0; +} + int sd_rtnl_message_new_route(sd_rtnl *rtnl, sd_rtnl_message **ret, uint16_t nlmsg_type, int rtm_family, unsigned char rtm_protocol) { @@ -133,7 +198,8 @@ int sd_rtnl_message_new_route(sd_rtnl *rtnl, sd_rtnl_message **ret, int r; assert_return(rtnl_message_type_is_route(nlmsg_type), -EINVAL); - assert_return(rtm_family == AF_INET || rtm_family == AF_INET6, -EINVAL); + assert_return((nlmsg_type == RTM_GETROUTE && rtm_family == AF_UNSPEC) || + rtm_family == AF_INET || rtm_family == AF_INET6, -EINVAL); assert_return(ret, -EINVAL); r = message_new(rtnl, ret, nlmsg_type); @@ -154,6 +220,59 @@ int sd_rtnl_message_new_route(sd_rtnl *rtnl, sd_rtnl_message **ret, return 0; } +int sd_rtnl_message_neigh_get_family(sd_rtnl_message *m, int *family) { + struct ndmsg *ndm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL); + assert_return(family, -EINVAL); + + ndm = NLMSG_DATA(m->hdr); + + *family = ndm->ndm_family; + + return 0; +} + +int sd_rtnl_message_neigh_get_ifindex(sd_rtnl_message *m, int *index) { + struct ndmsg *ndm; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL); + assert_return(index, -EINVAL); + + ndm = NLMSG_DATA(m->hdr); + + *index = ndm->ndm_ifindex; + + return 0; +} + +int sd_rtnl_message_new_neigh(sd_rtnl *rtnl, sd_rtnl_message **ret, uint16_t nlmsg_type, int index, int ndm_family) { + struct ndmsg *ndm; + int r; + + assert_return(rtnl_message_type_is_neigh(nlmsg_type), -EINVAL); + assert_return(ndm_family == AF_INET || ndm_family == AF_INET6, -EINVAL); + assert_return(ret, -EINVAL); + + r = message_new(rtnl, ret, nlmsg_type); + if (r < 0) + return r; + + if (nlmsg_type == RTM_NEWNEIGH) + (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_APPEND; + + ndm = NLMSG_DATA((*ret)->hdr); + + ndm->ndm_family = ndm_family; + ndm->ndm_ifindex = index; + + return 0; +} + int sd_rtnl_message_link_set_flags(sd_rtnl_message *m, unsigned flags, unsigned change) { struct ifinfomsg *ifi; @@ -184,6 +303,20 @@ int sd_rtnl_message_link_set_type(sd_rtnl_message *m, unsigned type) { return 0; } +int sd_rtnl_message_link_set_family(sd_rtnl_message *m, unsigned family) { + struct ifinfomsg *ifi; + + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_link(m->hdr->nlmsg_type), -EINVAL); + + ifi = NLMSG_DATA(m->hdr); + + ifi->ifi_family = family; + + return 0; +} + int sd_rtnl_message_new_link(sd_rtnl *rtnl, sd_rtnl_message **ret, uint16_t nlmsg_type, int index) { struct ifinfomsg *ifi; @@ -211,9 +344,10 @@ int sd_rtnl_message_new_link(sd_rtnl *rtnl, sd_rtnl_message **ret, int sd_rtnl_message_request_dump(sd_rtnl_message *m, int dump) { assert_return(m, -EINVAL); assert_return(m->hdr, -EINVAL); - assert_return(m->hdr->nlmsg_type == RTM_GETLINK || - m->hdr->nlmsg_type == RTM_GETADDR || - m->hdr->nlmsg_type == RTM_GETROUTE, + assert_return(m->hdr->nlmsg_type == RTM_GETLINK || + m->hdr->nlmsg_type == RTM_GETADDR || + m->hdr->nlmsg_type == RTM_GETROUTE || + m->hdr->nlmsg_type == RTM_GETNEIGH, -EINVAL); if (dump) @@ -566,8 +700,8 @@ int sd_rtnl_message_append_string(sd_rtnl_message *m, unsigned short type, const size = (size_t)r; if (size) { - length = strnlen(data, size); - if (length >= size) + length = strnlen(data, size+1); + if (length > size) return -EINVAL; } else length = strlen(data); @@ -723,7 +857,7 @@ int sd_rtnl_message_open_container(sd_rtnl_message *m, unsigned short type) { if (r < 0) return r; - r = add_rtattr(m, type, NULL, size); + r = add_rtattr(m, type | NLA_F_NESTED, NULL, size); if (r < 0) return r; @@ -799,6 +933,8 @@ int sd_rtnl_message_read_string(sd_rtnl_message *m, unsigned short type, const c int r; void *attr_data; + assert_return(m, -EINVAL); + r = message_attribute_has_type(m, type, NLA_STRING); if (r < 0) return r; @@ -809,7 +945,8 @@ int sd_rtnl_message_read_string(sd_rtnl_message *m, unsigned short type, const c else if (strnlen(attr_data, r) >= (size_t) r) return -EIO; - *data = (const char *) attr_data; + if (data) + *data = (const char *) attr_data; return 0; } @@ -818,6 +955,8 @@ int sd_rtnl_message_read_u8(sd_rtnl_message *m, unsigned short type, uint8_t *da int r; void *attr_data; + assert_return(m, -EINVAL); + r = message_attribute_has_type(m, type, NLA_U8); if (r < 0) return r; @@ -828,7 +967,8 @@ int sd_rtnl_message_read_u8(sd_rtnl_message *m, unsigned short type, uint8_t *da else if ((size_t) r < sizeof(uint8_t)) return -EIO; - *data = *(uint8_t *) attr_data; + if (data) + *data = *(uint8_t *) attr_data; return 0; } @@ -837,6 +977,8 @@ int sd_rtnl_message_read_u16(sd_rtnl_message *m, unsigned short type, uint16_t * int r; void *attr_data; + assert_return(m, -EINVAL); + r = message_attribute_has_type(m, type, NLA_U16); if (r < 0) return r; @@ -847,7 +989,8 @@ int sd_rtnl_message_read_u16(sd_rtnl_message *m, unsigned short type, uint16_t * else if ((size_t) r < sizeof(uint16_t)) return -EIO; - *data = *(uint16_t *) attr_data; + if (data) + *data = *(uint16_t *) attr_data; return 0; } @@ -856,6 +999,8 @@ int sd_rtnl_message_read_u32(sd_rtnl_message *m, unsigned short type, uint32_t * int r; void *attr_data; + assert_return(m, -EINVAL); + r = message_attribute_has_type(m, type, NLA_U32); if (r < 0) return r; @@ -866,7 +1011,8 @@ int sd_rtnl_message_read_u32(sd_rtnl_message *m, unsigned short type, uint32_t * else if ((size_t)r < sizeof(uint32_t)) return -EIO; - *data = *(uint32_t *) attr_data; + if (data) + *data = *(uint32_t *) attr_data; return 0; } @@ -875,6 +1021,8 @@ int sd_rtnl_message_read_ether_addr(sd_rtnl_message *m, unsigned short type, str int r; void *attr_data; + assert_return(m, -EINVAL); + r = message_attribute_has_type(m, type, NLA_ETHER_ADDR); if (r < 0) return r; @@ -885,7 +1033,8 @@ int sd_rtnl_message_read_ether_addr(sd_rtnl_message *m, unsigned short type, str else if ((size_t)r < sizeof(struct ether_addr)) return -EIO; - memcpy(data, attr_data, sizeof(struct ether_addr)); + if (data) + memcpy(data, attr_data, sizeof(struct ether_addr)); return 0; } @@ -894,6 +1043,8 @@ int sd_rtnl_message_read_cache_info(sd_rtnl_message *m, unsigned short type, str int r; void *attr_data; + assert_return(m, -EINVAL); + r = message_attribute_has_type(m, type, NLA_CACHE_INFO); if (r < 0) return r; @@ -904,7 +1055,8 @@ int sd_rtnl_message_read_cache_info(sd_rtnl_message *m, unsigned short type, str else if ((size_t)r < sizeof(struct ifa_cacheinfo)) return -EIO; - memcpy(info, attr_data, sizeof(struct ifa_cacheinfo)); + if (info) + memcpy(info, attr_data, sizeof(struct ifa_cacheinfo)); return 0; } @@ -913,6 +1065,8 @@ int sd_rtnl_message_read_in_addr(sd_rtnl_message *m, unsigned short type, struct int r; void *attr_data; + assert_return(m, -EINVAL); + r = message_attribute_has_type(m, type, NLA_IN_ADDR); if (r < 0) return r; @@ -923,7 +1077,8 @@ int sd_rtnl_message_read_in_addr(sd_rtnl_message *m, unsigned short type, struct else if ((size_t)r < sizeof(struct in_addr)) return -EIO; - memcpy(data, attr_data, sizeof(struct in_addr)); + if (data) + memcpy(data, attr_data, sizeof(struct in_addr)); return 0; } @@ -932,6 +1087,8 @@ int sd_rtnl_message_read_in6_addr(sd_rtnl_message *m, unsigned short type, struc int r; void *attr_data; + assert_return(m, -EINVAL); + r = message_attribute_has_type(m, type, NLA_IN_ADDR); if (r < 0) return r; @@ -942,7 +1099,8 @@ int sd_rtnl_message_read_in6_addr(sd_rtnl_message *m, unsigned short type, struc else if ((size_t)r < sizeof(struct in6_addr)) return -EIO; - memcpy(data, attr_data, sizeof(struct in6_addr)); + if (data) + memcpy(data, attr_data, sizeof(struct in6_addr)); return 0; } @@ -1036,13 +1194,20 @@ uint32_t rtnl_message_get_serial(sd_rtnl_message *m) { return m->hdr->nlmsg_seq; } +int sd_rtnl_message_is_error(sd_rtnl_message *m) { + assert_return(m, 0); + assert_return(m->hdr, 0); + + return m->hdr->nlmsg_type == NLMSG_ERROR; +} + int sd_rtnl_message_get_errno(sd_rtnl_message *m) { struct nlmsgerr *err; assert_return(m, -EINVAL); assert_return(m->hdr, -EINVAL); - if (m->hdr->nlmsg_type != NLMSG_ERROR) + if (!sd_rtnl_message_is_error(m)) return 0; err = NLMSG_DATA(m->hdr); @@ -1066,7 +1231,7 @@ int rtnl_message_parse(sd_rtnl_message *m, *rta_tb_size = max + 1; for (; RTA_OK(rta, rt_len); rta = RTA_NEXT(rta, rt_len)) { - type = rta->rta_type; + type = RTA_TYPE(rta); /* if the kernel is newer than the headers we used when building, we ignore out-of-range attributes @@ -1222,7 +1387,7 @@ int socket_read_message(sd_rtnl *rtnl) { } } - for (new_msg = rtnl->rbuffer; NLMSG_OK(new_msg, len); new_msg = NLMSG_NEXT(new_msg, len)) { + for (new_msg = rtnl->rbuffer; NLMSG_OK(new_msg, len) && !done; new_msg = NLMSG_NEXT(new_msg, len)) { _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL; const NLType *nl_type; @@ -1237,7 +1402,8 @@ int socket_read_message(sd_rtnl *rtnl) { if (new_msg->nlmsg_type == NLMSG_DONE) { /* finished reading multi-part message */ done = true; - break; + + continue; } /* check that we support this message type */ @@ -1251,8 +1417,10 @@ int socket_read_message(sd_rtnl *rtnl) { } /* check that the size matches the message type */ - if (new_msg->nlmsg_len < NLMSG_LENGTH(nl_type->size)) + if (new_msg->nlmsg_len < NLMSG_LENGTH(nl_type->size)) { + log_debug("sd-rtnl: message larger than expected, dropping"); continue; + } r = message_new_empty(rtnl, &m); if (r < 0) diff --git a/src/libsystemd/sd-rtnl/rtnl-types.c b/src/libsystemd/sd-rtnl/rtnl-types.c index df9a45dab1..a1db2ab76c 100644 --- a/src/libsystemd/sd-rtnl/rtnl-types.c +++ b/src/libsystemd/sd-rtnl/rtnl-types.c @@ -211,7 +211,23 @@ static const NLTypeSystem rtnl_link_info_type_system = { .types = rtnl_link_info_types, }; -static const NLType rtnl_link_types[IFLA_MAX + 1] = { +static const struct NLType rtnl_bridge_port_types[IFLA_BRPORT_MAX + 1] = { + [IFLA_BRPORT_STATE] = { .type = NLA_U8 }, + [IFLA_BRPORT_COST] = { .type = NLA_U32 }, + [IFLA_BRPORT_PRIORITY] = { .type = NLA_U16 }, + [IFLA_BRPORT_MODE] = { .type = NLA_U8 }, + [IFLA_BRPORT_GUARD] = { .type = NLA_U8 }, + [IFLA_BRPORT_PROTECT] = { .type = NLA_U8 }, + [IFLA_BRPORT_LEARNING] = { .type = NLA_U8 }, + [IFLA_BRPORT_UNICAST_FLOOD] = { .type = NLA_U8 }, +}; + +static const NLTypeSystem rtnl_bridge_port_type_system = { + .max = ELEMENTSOF(rtnl_bridge_port_types) - 1, + .types = rtnl_bridge_port_types, +}; + +static const NLType rtnl_link_types[IFLA_MAX + 1 ] = { [IFLA_ADDRESS] = { .type = NLA_ETHER_ADDR, }, [IFLA_BROADCAST] = { .type = NLA_ETHER_ADDR, }, [IFLA_IFNAME] = { .type = NLA_STRING, .size = IFNAMSIZ - 1, }, @@ -228,6 +244,7 @@ static const NLType rtnl_link_types[IFLA_MAX + 1] = { [IFLA_WIRELESS], [IFLA_PROTINFO], */ + [IFLA_PROTINFO] = { .type = NLA_NESTED, .type_system = &rtnl_bridge_port_type_system }, [IFLA_TXQLEN] = { .type = NLA_U32 }, /* [IFLA_MAP] = { .len = sizeof(struct rtnl_link_ifmap) }, @@ -312,6 +329,25 @@ static const NLTypeSystem rtnl_route_type_system = { .types = rtnl_route_types, }; +static const NLType rtnl_neigh_types[NDA_MAX + 1] = { + [NDA_DST] = { .type = NLA_IN_ADDR }, + [NDA_LLADDR] = { .type = NLA_ETHER_ADDR }, +/* + NDA_CACHEINFO, + NDA_PROBES, + NDA_VLAN, + NDA_PORT + NDA_VNI + NDA_IFINDEX + NDA_MASTER +*/ +}; + +static const NLTypeSystem rtnl_neigh_type_system = { + .max = ELEMENTSOF(rtnl_neigh_types) - 1, + .types = rtnl_neigh_types, +}; + static const NLType rtnl_types[RTM_MAX + 1] = { [NLMSG_ERROR] = { .type = NLA_META, .size = sizeof(struct nlmsgerr) }, [RTM_NEWLINK] = { .type = NLA_NESTED, .type_system = &rtnl_link_type_system, .size = sizeof(struct ifinfomsg) }, @@ -324,6 +360,9 @@ static const NLType rtnl_types[RTM_MAX + 1] = { [RTM_NEWROUTE] = { .type = NLA_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) }, [RTM_DELROUTE] = { .type = NLA_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) }, [RTM_GETROUTE] = { .type = NLA_NESTED, .type_system = &rtnl_route_type_system, .size = sizeof(struct rtmsg) }, + [RTM_NEWNEIGH] = { .type = NLA_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) }, + [RTM_DELNEIGH] = { .type = NLA_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) }, + [RTM_GETNEIGH] = { .type = NLA_NESTED, .type_system = &rtnl_neigh_type_system, .size = sizeof(struct ndmsg) }, }; const NLTypeSystem rtnl_type_system = { diff --git a/src/libsystemd/sd-rtnl/rtnl-util.c b/src/libsystemd/sd-rtnl/rtnl-util.c index 1ec1fa8d27..194a267b04 100644 --- a/src/libsystemd/sd-rtnl/rtnl-util.c +++ b/src/libsystemd/sd-rtnl/rtnl-util.c @@ -122,6 +122,17 @@ int rtnl_message_new_synthetic_error(int error, uint32_t serial, sd_rtnl_message return 0; } +bool rtnl_message_type_is_neigh(uint16_t type) { + switch (type) { + case RTM_NEWNEIGH: + case RTM_GETNEIGH: + case RTM_DELNEIGH: + return true; + default: + return false; + } +} + bool rtnl_message_type_is_route(uint16_t type) { switch (type) { case RTM_NEWROUTE: @@ -157,11 +168,9 @@ bool rtnl_message_type_is_addr(uint16_t type) { } int rtnl_log_parse_error(int r) { - log_error("Failed to parse netlink message: %s", strerror(-r)); - return r; + return log_error_errno(r, "Failed to parse netlink message: %m"); } int rtnl_log_create_error(int r) { - log_error("Failed to create netlink message: %s", strerror(-r)); - return r; + return log_error_errno(r, "Failed to create netlink message: %m"); } diff --git a/src/libsystemd/sd-rtnl/rtnl-util.h b/src/libsystemd/sd-rtnl/rtnl-util.h index fa3592df9d..ca9fbd4f41 100644 --- a/src/libsystemd/sd-rtnl/rtnl-util.h +++ b/src/libsystemd/sd-rtnl/rtnl-util.h @@ -33,6 +33,7 @@ void rtnl_message_seal(sd_rtnl_message *m); bool rtnl_message_type_is_link(uint16_t type); bool rtnl_message_type_is_addr(uint16_t type); bool rtnl_message_type_is_route(uint16_t type); +bool rtnl_message_type_is_neigh(uint16_t type); int rtnl_set_link_name(sd_rtnl **rtnl, int ifindex, const char *name); int rtnl_set_link_properties(sd_rtnl **rtnl, int ifindex, const char *alias, const struct ether_addr *mac, unsigned mtu); diff --git a/src/libsystemd/sd-rtnl/sd-rtnl.c b/src/libsystemd/sd-rtnl/sd-rtnl.c index b7f1afe905..abb011ea24 100644 --- a/src/libsystemd/sd-rtnl/sd-rtnl.c +++ b/src/libsystemd/sd-rtnl/sd-rtnl.c @@ -140,6 +140,10 @@ int sd_rtnl_open(sd_rtnl **ret, unsigned n_groups, ...) { return 0; } +int sd_rtnl_inc_rcvbuf(const sd_rtnl *const rtnl, const int size) { + return fd_inc_rcvbuf(rtnl->fd, size); +} + sd_rtnl *sd_rtnl_ref(sd_rtnl *rtnl) { assert_return(rtnl, NULL); assert_return(!rtnl_pid_changed(rtnl), NULL); @@ -859,7 +863,7 @@ int sd_rtnl_attach_event(sd_rtnl *rtnl, sd_event *event, int priority) { if (r < 0) goto fail; - r = sd_event_source_set_name(rtnl->io_event_source, "rtnl-receive-message"); + r = sd_event_source_set_description(rtnl->io_event_source, "rtnl-receive-message"); if (r < 0) goto fail; @@ -875,7 +879,7 @@ int sd_rtnl_attach_event(sd_rtnl *rtnl, sd_event *event, int priority) { if (r < 0) goto fail; - r = sd_event_source_set_name(rtnl->time_event_source, "rtnl-timer"); + r = sd_event_source_set_description(rtnl->time_event_source, "rtnl-timer"); if (r < 0) goto fail; @@ -883,7 +887,7 @@ int sd_rtnl_attach_event(sd_rtnl *rtnl, sd_event *event, int priority) { if (r < 0) goto fail; - r = sd_event_source_set_name(rtnl->exit_event_source, "rtnl-exit"); + r = sd_event_source_set_description(rtnl->exit_event_source, "rtnl-exit"); if (r < 0) goto fail; diff --git a/src/libsystemd/sd-rtnl/test-local-addresses.c b/src/libsystemd/sd-rtnl/test-local-addresses.c new file mode 100644 index 0000000000..38cbcfbccb --- /dev/null +++ b/src/libsystemd/sd-rtnl/test-local-addresses.c @@ -0,0 +1,58 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2014 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 "in-addr-util.h" +#include "local-addresses.h" +#include "af-list.h" + +static void print_local_addresses(struct local_address *a, unsigned n) { + unsigned i; + + for (i = 0; i < n; i++) { + _cleanup_free_ char *b = NULL; + + assert_se(in_addr_to_string(a[i].family, &a[i].address, &b) >= 0); + printf("%s if%i scope=%i metric=%u address=%s\n", af_to_name(a[i].family), a[i].ifindex, a[i].scope, a[i].metric, b); + } +} + +int main(int argc, char *argv[]) { + struct local_address *a; + int n; + + a = NULL; + n = local_addresses(NULL, 0, AF_UNSPEC, &a); + assert_se(n >= 0); + + printf("Local Addresses:\n"); + print_local_addresses(a, (unsigned) n); + free(a); + + a = NULL; + n = local_gateways(NULL, 0, AF_UNSPEC, &a); + assert_se(n >= 0); + + printf("Local Gateways:\n"); + print_local_addresses(a, (unsigned) n); + free(a); + + return 0; +} diff --git a/src/libsystemd/sd-rtnl/test-rtnl.c b/src/libsystemd/sd-rtnl/test-rtnl.c index 46b5bb20c3..72d8fe2273 100644 --- a/src/libsystemd/sd-rtnl/test-rtnl.c +++ b/src/libsystemd/sd-rtnl/test-rtnl.c @@ -135,7 +135,7 @@ static void test_route(void) { r = sd_rtnl_message_new_route(NULL, &req, RTM_NEWROUTE, AF_INET, RTPROT_STATIC); if (r < 0) { - log_error("Could not create RTM_NEWROUTE message: %s", strerror(-r)); + log_error_errno(r, "Could not create RTM_NEWROUTE message: %m"); return; } @@ -143,13 +143,13 @@ static void test_route(void) { r = sd_rtnl_message_append_in_addr(req, RTA_GATEWAY, &addr); if (r < 0) { - log_error("Could not append RTA_GATEWAY attribute: %s", strerror(-r)); + log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m"); return; } r = sd_rtnl_message_append_u32(req, RTA_OIF, index); if (r < 0) { - log_error("Could not append RTA_OIF attribute: %s", strerror(-r)); + log_error_errno(r, "Could not append RTA_OIF attribute: %m"); return; } @@ -223,7 +223,7 @@ static int pipe_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { r = sd_rtnl_message_get_errno(m); - log_info("%d left in pipe. got reply: %s", *counter, strerror(-r)); + log_info_errno(r, "%d left in pipe. got reply: %m", *counter); assert_se(r >= 0); diff --git a/src/libudev/libudev-device-private.c b/src/libudev/libudev-device-private.c index 637d064819..fb4c6e2940 100644 --- a/src/libudev/libudev-device-private.c +++ b/src/libudev/libudev-device-private.c @@ -102,7 +102,6 @@ static bool device_has_info(struct udev_device *udev_device) int udev_device_update_db(struct udev_device *udev_device) { - struct udev *udev = udev_device_get_udev(udev_device); bool has_info; const char *id; char filename[UTIL_PATH_SIZE]; @@ -129,10 +128,8 @@ int udev_device_update_db(struct udev_device *udev_device) strscpyl(filename_tmp, sizeof(filename_tmp), filename, ".tmp", NULL); mkdir_parents(filename_tmp, 0755); f = fopen(filename_tmp, "we"); - if (f == NULL) { - udev_err(udev, "unable to create temporary db file '%s': %m\n", filename_tmp); - return -1; - } + if (f == NULL) + return log_debug_errno(errno, "unable to create temporary db file '%s': %m", filename_tmp); /* * set 'sticky' bit to indicate that we should not clean the @@ -172,7 +169,7 @@ int udev_device_update_db(struct udev_device *udev_device) r = rename(filename_tmp, filename); if (r < 0) return -1; - udev_dbg(udev, "created %s file '%s' for '%s'\n", has_info ? "db" : "empty", + log_debug("created %s file '%s' for '%s'", has_info ? "db" : "empty", filename, udev_device_get_devpath(udev_device)); return 0; } diff --git a/src/libudev/libudev-device.c b/src/libudev/libudev-device.c index 2699374072..16ee1f4be3 100644 --- a/src/libudev/libudev-device.c +++ b/src/libudev/libudev-device.c @@ -534,10 +534,8 @@ int udev_device_read_db(struct udev_device *udev_device, const char *dbfile) } f = fopen(dbfile, "re"); - if (f == NULL) { - udev_dbg(udev_device->udev, "no db file to read %s: %m\n", dbfile); - return -errno; - } + if (f == NULL) + return log_debug_errno(errno, "no db file to read %s: %m", dbfile); /* devices with a database entry are initialized */ udev_device->is_initialized = true; @@ -577,7 +575,7 @@ int udev_device_read_db(struct udev_device *udev_device, const char *dbfile) } fclose(f); - udev_dbg(udev_device->udev, "device %p filled with db file data\n", udev_device); + log_debug("device %p filled with db file data", udev_device); return 0; } @@ -642,7 +640,6 @@ void udev_device_set_info_loaded(struct udev_device *device) struct udev_device *udev_device_new(struct udev *udev) { struct udev_device *udev_device; - struct udev_list_entry *list_entry; if (udev == NULL) { errno = EINVAL; @@ -662,11 +659,7 @@ struct udev_device *udev_device_new(struct udev *udev) udev_list_init(udev, &udev_device->sysattr_list, false); udev_list_init(udev, &udev_device->tags_list, true); udev_device->watch_handle = -1; - /* copy global properties */ - udev_list_entry_foreach(list_entry, udev_get_properties_list_entry(udev)) - udev_device_add_property(udev_device, - udev_list_entry_get_name(list_entry), - udev_list_entry_get_value(list_entry)); + return udev_device; } @@ -704,7 +697,7 @@ _public_ struct udev_device *udev_device_new_from_syspath(struct udev *udev, con /* path starts in sys */ if (!startswith(syspath, "/sys")) { - udev_dbg(udev, "not in sys :%s\n", syspath); + log_debug("not in sys :%s", syspath); errno = EINVAL; return NULL; } @@ -730,8 +723,13 @@ _public_ struct udev_device *udev_device_new_from_syspath(struct udev *udev, con return NULL; } else { /* everything else just needs to be a directory */ - if (stat(path, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) + if (stat(path, &statbuf) != 0) + return NULL; + + if (!S_ISDIR(statbuf.st_mode)) { + errno = EISDIR; return NULL; + } } udev_device = udev_device_new(udev); @@ -739,7 +737,7 @@ _public_ struct udev_device *udev_device_new_from_syspath(struct udev *udev, con return NULL; udev_device_set_syspath(udev_device, path); - udev_dbg(udev, "device %p has devpath '%s'\n", udev_device, udev_device_get_devpath(udev_device)); + log_debug("device %p has devpath '%s'", udev_device, udev_device_get_devpath(udev_device)); return udev_device; } @@ -971,7 +969,7 @@ _public_ struct udev_device *udev_device_new_from_environment(struct udev *udev) udev_device_add_property_from_string_parse(udev_device, environ[i]); if (udev_device_add_property_from_string_parse_finish(udev_device) < 0) { - udev_dbg(udev, "missing values, invalid device\n"); + log_debug("missing values, invalid device"); udev_device_unref(udev_device); udev_device = NULL; } diff --git a/src/libudev/libudev-hwdb.c b/src/libudev/libudev-hwdb.c index a1cfc0bd5a..05a685868f 100644 --- a/src/libudev/libudev-hwdb.c +++ b/src/libudev/libudev-hwdb.c @@ -289,45 +289,45 @@ _public_ struct udev_hwdb *udev_hwdb_new(struct udev *udev) { else if (errno == ENOENT) continue; else { - udev_dbg(udev, "error reading %s: %m", hwdb_bin_path); + log_debug_errno(errno, "error reading %s: %m", hwdb_bin_path); udev_hwdb_unref(hwdb); return NULL; } } if (!hwdb->f) { - udev_err(udev, "hwdb.bin does not exist, please run udevadm hwdb --update"); + log_debug("hwdb.bin does not exist, please run udevadm hwdb --update"); udev_hwdb_unref(hwdb); return NULL; } if (fstat(fileno(hwdb->f), &hwdb->st) < 0 || (size_t)hwdb->st.st_size < offsetof(struct trie_header_f, strings_len) + 8) { - udev_dbg(udev, "error reading %s: %m", hwdb_bin_path); + log_debug_errno(errno, "error reading %s: %m", hwdb_bin_path); udev_hwdb_unref(hwdb); return NULL; } hwdb->map = mmap(0, hwdb->st.st_size, PROT_READ, MAP_SHARED, fileno(hwdb->f), 0); if (hwdb->map == MAP_FAILED) { - udev_dbg(udev, "error mapping %s: %m", hwdb_bin_path); + log_debug_errno(errno, "error mapping %s: %m", hwdb_bin_path); udev_hwdb_unref(hwdb); return NULL; } if (memcmp(hwdb->map, sig, sizeof(hwdb->head->signature)) != 0 || (size_t)hwdb->st.st_size != le64toh(hwdb->head->file_size)) { - udev_dbg(udev, "error recognizing the format of %s", hwdb_bin_path); + log_debug("error recognizing the format of %s", hwdb_bin_path); udev_hwdb_unref(hwdb); return NULL; } - udev_dbg(udev, "=== trie on-disk ===\n"); - udev_dbg(udev, "tool version: %"PRIu64, le64toh(hwdb->head->tool_version)); - udev_dbg(udev, "file size: %8"PRIu64" bytes\n", hwdb->st.st_size); - udev_dbg(udev, "header size %8"PRIu64" bytes\n", le64toh(hwdb->head->header_size)); - udev_dbg(udev, "strings %8"PRIu64" bytes\n", le64toh(hwdb->head->strings_len)); - udev_dbg(udev, "nodes %8"PRIu64" bytes\n", le64toh(hwdb->head->nodes_len)); + log_debug("=== trie on-disk ==="); + log_debug("tool version: %"PRIu64, le64toh(hwdb->head->tool_version)); + log_debug("file size: %8"PRIu64" bytes", hwdb->st.st_size); + log_debug("header size %8"PRIu64" bytes", le64toh(hwdb->head->header_size)); + log_debug("strings %8"PRIu64" bytes", le64toh(hwdb->head->strings_len)); + log_debug("nodes %8"PRIu64" bytes", le64toh(hwdb->head->nodes_len)); return hwdb; } diff --git a/src/libudev/libudev-monitor.c b/src/libudev/libudev-monitor.c index 59698b85b7..e8d6b4a61b 100644 --- a/src/libudev/libudev-monitor.c +++ b/src/libudev/libudev-monitor.c @@ -121,7 +121,7 @@ static bool udev_has_devtmpfs(struct udev *udev) { r = name_to_handle_at(AT_FDCWD, "/dev", &h.handle, &mount_id, 0); if (r < 0) { if (errno != EOPNOTSUPP) - udev_err(udev, "name_to_handle_at on /dev: %m\n"); + log_debug_errno(errno, "name_to_handle_at on /dev: %m"); return false; } @@ -174,7 +174,7 @@ struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const c * will not receive any messages. */ if (access("/run/udev/control", F_OK) < 0 && !udev_has_devtmpfs(udev)) { - udev_dbg(udev, "the udev service seems not to be active, disable the monitor\n"); + log_debug("the udev service seems not to be active, disable the monitor"); group = UDEV_MONITOR_NONE; } else group = UDEV_MONITOR_UDEV; @@ -190,7 +190,7 @@ struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const c if (fd < 0) { udev_monitor->sock = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_KOBJECT_UEVENT); if (udev_monitor->sock == -1) { - udev_err(udev, "error getting socket: %m\n"); + log_debug_errno(errno, "error getting socket: %m"); free(udev_monitor); return NULL; } @@ -407,14 +407,14 @@ _public_ int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) if (err == 0) udev_monitor->snl.nl.nl_pid = snl.nl.nl_pid; } else { - udev_err(udev_monitor->udev, "bind failed: %m\n"); + log_debug_errno(errno, "bind failed: %m"); return -errno; } /* enable receiving of sender credentials */ err = setsockopt(udev_monitor->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); if (err < 0) - udev_err(udev_monitor->udev, "setting SO_PASSCRED failed: %m\n"); + log_debug_errno(errno, "setting SO_PASSCRED failed: %m"); return 0; } @@ -602,12 +602,12 @@ retry: buflen = recvmsg(udev_monitor->sock, &smsg, 0); if (buflen < 0) { if (errno != EINTR) - udev_dbg(udev_monitor->udev, "unable to receive message\n"); + log_debug("unable to receive message"); return NULL; } if (buflen < 32 || (size_t)buflen >= sizeof(buf)) { - udev_dbg(udev_monitor->udev, "invalid message length\n"); + log_debug("invalid message length"); return NULL; } @@ -615,12 +615,12 @@ retry: /* unicast message, check if we trust the sender */ if (udev_monitor->snl_trusted_sender.nl.nl_pid == 0 || snl.nl.nl_pid != udev_monitor->snl_trusted_sender.nl.nl_pid) { - udev_dbg(udev_monitor->udev, "unicast netlink message ignored\n"); + log_debug("unicast netlink message ignored"); return NULL; } } else if (snl.nl.nl_groups == UDEV_MONITOR_KERNEL) { if (snl.nl.nl_pid > 0) { - udev_dbg(udev_monitor->udev, "multicast kernel netlink message from pid %d ignored\n", + log_debug("multicast kernel netlink message from pid %d ignored", snl.nl.nl_pid); return NULL; } @@ -628,13 +628,13 @@ retry: cmsg = CMSG_FIRSTHDR(&smsg); if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { - udev_dbg(udev_monitor->udev, "no sender credentials received, message ignored\n"); + log_debug("no sender credentials received, message ignored"); return NULL; } cred = (struct ucred *)CMSG_DATA(cmsg); if (cred->uid != 0) { - udev_dbg(udev_monitor->udev, "sender uid=%d, message ignored\n", cred->uid); + log_debug("sender uid=%d, message ignored", cred->uid); return NULL; } @@ -648,7 +648,7 @@ retry: /* udev message needs proper version magic */ nlh = (struct udev_monitor_netlink_header *) buf; if (nlh->magic != htonl(UDEV_MONITOR_MAGIC)) { - udev_err(udev_monitor->udev, "unrecognized message signature (%x != %x)\n", + log_debug("unrecognized message signature (%x != %x)", nlh->magic, htonl(UDEV_MONITOR_MAGIC)); udev_device_unref(udev_device); return NULL; @@ -666,14 +666,14 @@ retry: /* kernel message with header */ bufpos = strlen(buf) + 1; if ((size_t)bufpos < sizeof("a@/d") || bufpos >= buflen) { - udev_dbg(udev_monitor->udev, "invalid message length\n"); + log_debug("invalid message length"); udev_device_unref(udev_device); return NULL; } /* check message header */ if (strstr(buf, "@/") == NULL) { - udev_dbg(udev_monitor->udev, "unrecognized message header\n"); + log_debug("unrecognized message header"); udev_device_unref(udev_device); return NULL; } @@ -694,7 +694,7 @@ retry: } if (udev_device_add_property_from_string_parse_finish(udev_device) < 0) { - udev_dbg(udev_monitor->udev, "missing values, invalid device\n"); + log_debug("missing values, invalid device"); udev_device_unref(udev_device); return NULL; } @@ -778,7 +778,7 @@ int udev_monitor_send_device(struct udev_monitor *udev_monitor, smsg.msg_name = &udev_monitor->snl_destination; smsg.msg_namelen = sizeof(struct sockaddr_nl); count = sendmsg(udev_monitor->sock, &smsg, 0); - udev_dbg(udev_monitor->udev, "passed %zi bytes to netlink monitor %p\n", count, udev_monitor); + log_debug("passed %zi bytes to netlink monitor %p", count, udev_monitor); return count; } diff --git a/src/libudev/libudev-private.h b/src/libudev/libudev-private.h index 7e11f7326d..64f132f91d 100644 --- a/src/libudev/libudev-private.h +++ b/src/libudev/libudev-private.h @@ -33,24 +33,8 @@ #define READ_END 0 #define WRITE_END 1 -/* avoid (sometimes expensive) calculations of parameters for debug output */ -#define udev_log_cond(udev, prio, arg...) \ - do { \ - if (udev_get_log_priority(udev) >= prio) \ - udev_log(udev, prio, __FILE__, __LINE__, __FUNCTION__, ## arg); \ - } while (0) - -#define udev_dbg(udev, arg...) udev_log_cond(udev, LOG_DEBUG, ## arg) -#define udev_info(udev, arg...) udev_log_cond(udev, LOG_INFO, ## arg) -#define udev_err(udev, arg...) udev_log_cond(udev, LOG_ERR, ## arg) - /* libudev.c */ -void udev_log(struct udev *udev, - int priority, const char *file, int line, const char *fn, - const char *format, ...) _printf_(6, 7); int udev_get_rules_path(struct udev *udev, char **path[], usec_t *ts_usec[]); -struct udev_list_entry *udev_add_property(struct udev *udev, const char *key, const char *value); -struct udev_list_entry *udev_get_properties_list_entry(struct udev *udev); /* libudev-device.c */ struct udev_device *udev_device_new(struct udev *udev); diff --git a/src/libudev/libudev-util.c b/src/libudev/libudev-util.c index f3fdf3b5aa..291829e6d8 100644 --- a/src/libudev/libudev-util.c +++ b/src/libudev/libudev-util.c @@ -93,7 +93,7 @@ int util_resolve_subsys_kernel(struct udev *udev, const char *string, strscpy(result, maxsize, val); else result[0] = '\0'; - udev_dbg(udev, "value '[%s/%s]%s' is '%s'\n", subsys, sysname, attr, result); + log_debug("value '[%s/%s]%s' is '%s'", subsys, sysname, attr, result); } else { size_t l; char *s; @@ -102,7 +102,7 @@ int util_resolve_subsys_kernel(struct udev *udev, const char *string, l = strpcpyl(&s, maxsize, udev_device_get_syspath(dev), NULL); if (attr != NULL) strpcpyl(&s, l, "/", attr, NULL); - udev_dbg(udev, "path '[%s/%s]%s' is '%s'\n", subsys, sysname, attr, result); + log_debug("path '[%s/%s]%s' is '%s'", subsys, sysname, attr, result); } udev_device_unref(dev); return 0; @@ -159,9 +159,13 @@ int util_log_priority(const char *priority) char *endptr; int prio; - prio = strtol(priority, &endptr, 10); - if (endptr[0] == '\0' || isspace(endptr[0])) - return prio; + prio = strtoul(priority, &endptr, 10); + if (endptr[0] == '\0' || isspace(endptr[0])) { + if (prio >= 0 && prio <= 7) + return prio; + else + return -ERANGE; + } return log_level_from_string(priority); } diff --git a/src/libudev/libudev.c b/src/libudev/libudev.c index e2ab960d55..8785f22071 100644 --- a/src/libudev/libudev.c +++ b/src/libudev/libudev.c @@ -1,7 +1,7 @@ /*** This file is part of systemd. - Copyright 2008-2012 Kay Sievers <kay@vrfy.org> + Copyright 2008-2014 Kay Sievers <kay@vrfy.org> 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 @@ -50,41 +50,18 @@ struct udev { int priority, const char *file, int line, const char *fn, const char *format, va_list args); void *userdata; - struct udev_list properties_list; - int log_priority; }; -void udev_log(struct udev *udev, - int priority, const char *file, int line, const char *fn, - const char *format, ...) -{ - va_list args; - - va_start(args, format); - udev->log_fn(udev, priority, file, line, fn, format, args); - va_end(args); -} - -_printf_(6,0) -static void log_stderr(struct udev *udev, - int priority, const char *file, int line, const char *fn, - const char *format, va_list args) -{ - fprintf(stderr, "libudev: %s: ", fn); - vfprintf(stderr, format, args); -} - /** * udev_get_userdata: * @udev: udev library context * * Retrieve stored data pointer from library context. This might be useful - * to access from callbacks like a custom logging function. + * to access from callbacks. * * Returns: stored userdata **/ -_public_ void *udev_get_userdata(struct udev *udev) -{ +_public_ void *udev_get_userdata(struct udev *udev) { if (udev == NULL) return NULL; return udev->userdata; @@ -97,8 +74,7 @@ _public_ void *udev_get_userdata(struct udev *udev) * * Store custom @userdata in the library context. **/ -_public_ void udev_set_userdata(struct udev *udev, void *userdata) -{ +_public_ void udev_set_userdata(struct udev *udev, void *userdata) { if (udev == NULL) return; udev->userdata = userdata; @@ -115,24 +91,19 @@ _public_ void udev_set_userdata(struct udev *udev, void *userdata) * * Returns: a new udev library context **/ -_public_ struct udev *udev_new(void) -{ +_public_ struct udev *udev_new(void) { struct udev *udev; - const char *env; - FILE *f; + _cleanup_fclose_ FILE *f = NULL; udev = new0(struct udev, 1); if (udev == NULL) return NULL; udev->refcount = 1; - udev->log_fn = log_stderr; - udev->log_priority = LOG_INFO; - udev_list_init(udev, &udev->properties_list, true); f = fopen("/etc/udev/udev.conf", "re"); if (f != NULL) { char line[UTIL_LINE_SIZE]; - int line_nr = 0; + unsigned line_nr = 0; while (fgets(line, sizeof(line), f)) { size_t len; @@ -153,7 +124,7 @@ _public_ struct udev *udev_new(void) /* split key/value */ val = strchr(key, '='); if (val == NULL) { - udev_err(udev, "missing <key>=<value> in /etc/udev/udev.conf[%i]; skip line\n", line_nr); + log_debug("/etc/udev/udev.conf:%u: missing assignment, skipping line.", line_nr); continue; } val[0] = '\0'; @@ -185,7 +156,7 @@ _public_ struct udev *udev_new(void) /* unquote */ if (val[0] == '"' || val[0] == '\'') { if (val[len-1] != val[0]) { - udev_err(udev, "inconsistent quoting in /etc/udev/udev.conf[%i]; skip line\n", line_nr); + log_debug("/etc/udev/udev.conf:%u: inconsistent quoting, skipping line.", line_nr); continue; } val[len-1] = '\0'; @@ -193,18 +164,18 @@ _public_ struct udev *udev_new(void) } if (streq(key, "udev_log")) { - udev_set_log_priority(udev, util_log_priority(val)); + int prio; + + prio = util_log_priority(val); + if (prio < 0) + log_debug("/etc/udev/udev.conf:%u: invalid log level '%s', ignoring.", line_nr, val); + else + log_set_max_level(prio); continue; } } - fclose(f); } - /* environment overrides config */ - env = secure_getenv("UDEV_LOG"); - if (env != NULL) - udev_set_log_priority(udev, util_log_priority(env)); - return udev; } @@ -216,8 +187,7 @@ _public_ struct udev *udev_new(void) * * Returns: the passed udev library context **/ -_public_ struct udev *udev_ref(struct udev *udev) -{ +_public_ struct udev *udev_ref(struct udev *udev) { if (udev == NULL) return NULL; udev->refcount++; @@ -233,14 +203,12 @@ _public_ struct udev *udev_ref(struct udev *udev) * * Returns: the passed udev library context if it has still an active reference, or #NULL otherwise. **/ -_public_ struct udev *udev_unref(struct udev *udev) -{ +_public_ struct udev *udev_unref(struct udev *udev) { if (udev == NULL) return NULL; udev->refcount--; if (udev->refcount > 0) return udev; - udev_list_cleanup(&udev->properties_list); free(udev); return NULL; } @@ -248,68 +216,37 @@ _public_ struct udev *udev_unref(struct udev *udev) /** * udev_set_log_fn: * @udev: udev library context - * @log_fn: function to be called for logging messages + * @log_fn: function to be called for log messages * - * The built-in logging writes to stderr. It can be - * overridden by a custom function, to plug log messages - * into the users' logging functionality. + * This function is deprecated. * **/ _public_ void udev_set_log_fn(struct udev *udev, void (*log_fn)(struct udev *udev, int priority, const char *file, int line, const char *fn, - const char *format, va_list args)) -{ - udev->log_fn = log_fn; - udev_dbg(udev, "custom logging function %p registered\n", log_fn); + const char *format, va_list args)) { + return; } /** * udev_get_log_priority: * @udev: udev library context * - * The initial logging priority is read from the udev config file - * at startup. + * This function is deprecated. * - * Returns: the current logging priority **/ -_public_ int udev_get_log_priority(struct udev *udev) -{ - return udev->log_priority; +_public_ int udev_get_log_priority(struct udev *udev) { + return log_get_max_level(); } /** * udev_set_log_priority: * @udev: udev library context - * @priority: the new logging priority + * @priority: the new log priority + * + * This function is deprecated. * - * Set the current logging priority. The value controls which messages - * are logged. **/ -_public_ void udev_set_log_priority(struct udev *udev, int priority) -{ - char num[32]; - - udev->log_priority = priority; - snprintf(num, sizeof(num), "%u", udev->log_priority); - udev_add_property(udev, "UDEV_LOG", num); -} - -struct udev_list_entry *udev_add_property(struct udev *udev, const char *key, const char *value) -{ - if (value == NULL) { - struct udev_list_entry *list_entry; - - list_entry = udev_get_properties_list_entry(udev); - list_entry = udev_list_entry_get_by_name(list_entry, key); - if (list_entry != NULL) - udev_list_entry_delete(list_entry); - return NULL; - } - return udev_list_entry_add(&udev->properties_list, key, value); -} - -struct udev_list_entry *udev_get_properties_list_entry(struct udev *udev) -{ - return udev_list_get_entry(&udev->properties_list); +_public_ void udev_set_log_priority(struct udev *udev, int priority) { + log_set_max_level(priority); } diff --git a/src/libudev/libudev.h b/src/libudev/libudev.h index 4f2f11502c..a94505c09e 100644 --- a/src/libudev/libudev.h +++ b/src/libudev/libudev.h @@ -41,9 +41,9 @@ struct udev *udev_new(void); void udev_set_log_fn(struct udev *udev, void (*log_fn)(struct udev *udev, int priority, const char *file, int line, const char *fn, - const char *format, va_list args)); -int udev_get_log_priority(struct udev *udev); -void udev_set_log_priority(struct udev *udev, int priority); + const char *format, va_list args)) __attribute__ ((deprecated)); +int udev_get_log_priority(struct udev *udev) __attribute__ ((deprecated)); +void udev_set_log_priority(struct udev *udev, int priority) __attribute__ ((deprecated)); void *udev_get_userdata(struct udev *udev); void udev_set_userdata(struct udev *udev, void *userdata); diff --git a/src/locale/localectl.c b/src/locale/localectl.c index 3690f9fc89..58b8984bc8 100644 --- a/src/locale/localectl.c +++ b/src/locale/localectl.c @@ -110,7 +110,7 @@ static void print_overriden_variables(void) { NULL); if (r < 0 && r != -ENOENT) { - log_warning("Failed to read /proc/cmdline: %s", strerror(-r)); + log_warning_errno(r, "Failed to read /proc/cmdline: %m"); goto finish; } @@ -118,11 +118,11 @@ static void print_overriden_variables(void) { if (variables[j]) { if (print_warning) { log_warning("Warning: Settings on kernel command line override system locale settings in /etc/locale.conf.\n" - " Command Line: %s=%s\n", locale_variable_to_string(j), variables[j]); + " Command Line: %s=%s", locale_variable_to_string(j), variables[j]); print_warning = false; } else - log_warning(" %s=%s\n", locale_variable_to_string(j), variables[j]); + log_warning(" %s=%s", locale_variable_to_string(j), variables[j]); } finish: for (j = 0; j < _VARIABLE_LC_MAX; j++) @@ -178,7 +178,7 @@ static int show_status(sd_bus *bus, char **args, unsigned n) { map, &info); if (r < 0) { - log_error("Could not get properties: %s", strerror(-r)); + log_error_errno(r, "Could not get properties: %m"); goto fail; } @@ -234,10 +234,8 @@ static int list_locales(sd_bus *bus, char **args, unsigned n) { assert(args); r = get_locales(&l); - if (r < 0) { - log_error("Failed to read list of locales: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to read list of locales: %m"); pager_open_if_enabled(); strv_print(l); @@ -309,10 +307,8 @@ static int nftw_cb( *e = 0; r = set_consume(keymaps, p); - if (r < 0 && r != -EEXIST) { - log_error("Can't add keymap: %s", strerror(-r)); - return r; - } + if (r < 0 && r != -EEXIST) + return log_error_errno(r, "Can't add keymap: %m"); return 0; } @@ -405,10 +401,8 @@ static int list_x11_keymaps(sd_bus *bus, char **args, unsigned n) { } f = fopen("/usr/share/X11/xkb/rules/base.lst", "re"); - if (!f) { - log_error("Failed to open keyboard mapping list. %m"); - return -errno; - } + if (!f) + return log_error_errno(errno, "Failed to open keyboard mapping list. %m"); if (streq(args[0], "list-x11-keymap-models")) look_for = MODELS; @@ -505,7 +499,7 @@ static void help(void) { " list-locales Show known locales\n" " set-keymap MAP [MAP] Set virtual console keyboard mapping\n" " list-keymaps Show known virtual console keyboard mappings\n" - " set-x11-keymap LAYOUT [MODEL] [VARIANT] [OPTIONS]\n" + " set-x11-keymap LAYOUT [MODEL [VARIANT [OPTIONS]]]\n" " Set X11 keyboard mapping\n" " list-x11-keymap-models Show known X11 keyboard mapping models\n" " list-x11-keymap-layouts Show known X11 keyboard mapping layouts\n" @@ -683,7 +677,7 @@ int main(int argc, char*argv[]) { r = bus_open_transport(arg_transport, arg_host, false, &bus); if (r < 0) { - log_error("Failed to create bus connection: %s", strerror(-r)); + log_error_errno(r, "Failed to create bus connection: %m"); goto finish; } diff --git a/src/locale/localed.c b/src/locale/localed.c index 552ffdf87a..8d60d0ff13 100644 --- a/src/locale/localed.c +++ b/src/locale/localed.c @@ -41,6 +41,10 @@ #include "event-util.h" #include "locale-util.h" +#ifdef HAVE_XKBCOMMON +#include <xkbcommon/xkbcommon.h> +#endif + enum { /* We don't list LC_ALL here on purpose. People should be * using LANG instead. */ @@ -224,7 +228,7 @@ static int x11_read_data(Context *c) { if (in_section && first_word(l, "Option")) { _cleanup_strv_free_ char **a = NULL; - r = strv_split_quoted(&a, l); + r = strv_split_quoted(&a, l, false); if (r < 0) return r; @@ -247,7 +251,7 @@ static int x11_read_data(Context *c) { } else if (!in_section && first_word(l, "Section")) { _cleanup_strv_free_ char **a = NULL; - r = strv_split_quoted(&a, l); + r = strv_split_quoted(&a, l, false); if (r < 0) return -ENOMEM; @@ -371,7 +375,7 @@ static int locale_update_system_manager(Context *c, sd_bus *bus) { r = sd_bus_call(bus, m, 0, &error, NULL); if (r < 0) - log_error("Failed to update the manager environment: %s", strerror(-r)); + log_error_errno(r, "Failed to update the manager environment: %m"); return 0; } @@ -533,7 +537,7 @@ static int read_next_mapping(FILE *f, unsigned *n, char ***a) { if (l[0] == 0 || l[0] == '#') continue; - r = strv_split_quoted(&b, l); + r = strv_split_quoted(&b, l, false); if (r < 0) return r; @@ -606,10 +610,8 @@ static int vconsole_convert_to_x11(Context *c, sd_bus *bus) { int r; r = x11_write_data(c); - if (r < 0) { - log_error("Failed to set X11 keyboard layout: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to set X11 keyboard layout: %m"); log_info("Changed X11 keyboard layout to '%s' model '%s' variant '%s' options '%s'", strempty(c->x11_layout), @@ -786,7 +788,7 @@ static int x11_convert_to_vconsole(Context *c, sd_bus *bus) { if (modified) { r = vconsole_write_data(c); if (r < 0) - log_error("Failed to set virtual console keymap: %s", strerror(-r)); + log_error_errno(r, "Failed to set virtual console keymap: %m"); log_info("Changed virtual console keymap to '%s' toggle '%s'", strempty(c->vc_keymap), strempty(c->vc_keymap_toggle)); @@ -919,7 +921,7 @@ static int method_set_locale(sd_bus *bus, sd_bus_message *m, void *userdata, sd_ r = locale_write_data(c, &settings); if (r < 0) { - log_error("Failed to set locale: %s", strerror(-r)); + log_error_errno(r, "Failed to set locale: %m"); return sd_bus_error_set_errnof(error, r, "Failed to set locale: %s", strerror(-r)); } @@ -979,7 +981,7 @@ static int method_set_vc_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata r = vconsole_write_data(c); if (r < 0) { - log_error("Failed to set virtual console keymap: %s", strerror(-r)); + log_error_errno(r, "Failed to set virtual console keymap: %m"); return sd_bus_error_set_errnof(error, r, "Failed to set virtual console keymap: %s", strerror(-r)); } @@ -988,7 +990,7 @@ static int method_set_vc_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata r = vconsole_reload(bus); if (r < 0) - log_error("Failed to request keymap reload: %s", strerror(-r)); + log_error_errno(r, "Failed to request keymap reload: %m"); sd_bus_emit_properties_changed(bus, "/org/freedesktop/locale1", @@ -998,13 +1000,61 @@ static int method_set_vc_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata if (convert) { r = vconsole_convert_to_x11(c, bus); if (r < 0) - log_error("Failed to convert keymap data: %s", strerror(-r)); + log_error_errno(r, "Failed to convert keymap data: %m"); } } return sd_bus_reply_method_return(m, NULL); } +#ifdef HAVE_XKBCOMMON +static void log_xkb(struct xkb_context *ctx, enum xkb_log_level lvl, const char *format, va_list args) { + const char *fmt; + + fmt = strappenda("libxkbcommon: ", format); + log_internalv(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, fmt, args); +} + +static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) { + const struct xkb_rule_names rmlvo = { + .model = model, + .layout = layout, + .variant = variant, + .options = options, + }; + struct xkb_context *ctx = NULL; + struct xkb_keymap *km = NULL; + int r; + + /* compile keymap from RMLVO information to check out its validity */ + + ctx = xkb_context_new(XKB_CONTEXT_NO_ENVIRONMENT_NAMES); + if (!ctx) { + r = -ENOMEM; + goto exit; + } + + xkb_context_set_log_fn(ctx, log_xkb); + + km = xkb_keymap_new_from_names(ctx, &rmlvo, XKB_KEYMAP_COMPILE_NO_FLAGS); + if (!km) { + r = -EINVAL; + goto exit; + } + + r = 0; + +exit: + xkb_keymap_unref(km); + xkb_context_unref(ctx); + return r; +} +#else +static int verify_xkb_rmlvo(const char *model, const char *layout, const char *variant, const char *options) { + return 0; +} +#endif + static int method_set_x11_keyboard(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) { Context *c = userdata; const char *layout, *model, *variant, *options; @@ -1044,6 +1094,13 @@ static int method_set_x11_keyboard(sd_bus *bus, sd_bus_message *m, void *userdat if (r == 0) return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + r = verify_xkb_rmlvo(model, layout, variant, options); + if (r < 0) { + log_error_errno(r, "Cannot compile XKB keymap for new x11 keyboard layout ('%s' / '%s' / '%s' / '%s'): %m", + strempty(model), strempty(layout), strempty(variant), strempty(options)); + return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot compile XKB keymap, refusing"); + } + if (free_and_strdup(&c->x11_layout, layout) < 0 || free_and_strdup(&c->x11_model, model) < 0 || free_and_strdup(&c->x11_variant, variant) < 0 || @@ -1052,7 +1109,7 @@ static int method_set_x11_keyboard(sd_bus *bus, sd_bus_message *m, void *userdat r = x11_write_data(c); if (r < 0) { - log_error("Failed to set X11 keyboard layout: %s", strerror(-r)); + log_error_errno(r, "Failed to set X11 keyboard layout: %m"); return sd_bus_error_set_errnof(error, r, "Failed to set X11 keyboard layout: %s", strerror(-r)); } @@ -1070,7 +1127,7 @@ static int method_set_x11_keyboard(sd_bus *bus, sd_bus_message *m, void *userdat if (convert) { r = x11_convert_to_vconsole(c, bus); if (r < 0) - log_error("Failed to convert keymap data: %s", strerror(-r)); + log_error_errno(r, "Failed to convert keymap data: %m"); } } @@ -1101,28 +1158,20 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { assert(_bus); r = sd_bus_default_system(&bus); - if (r < 0) { - log_error("Failed to get system bus connection: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get system bus connection: %m"); r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/locale1", "org.freedesktop.locale1", locale_vtable, c); - if (r < 0) { - log_error("Failed to register object: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to register object: %m"); r = sd_bus_request_name(bus, "org.freedesktop.locale1", 0); - if (r < 0) { - log_error("Failed to register name: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to register name: %m"); r = sd_bus_attach_event(bus, event, 0); - if (r < 0) { - log_error("Failed to attach bus to event loop: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to attach bus to event loop: %m"); *_bus = bus; bus = NULL; @@ -1151,7 +1200,7 @@ int main(int argc, char *argv[]) { r = sd_event_default(&event); if (r < 0) { - log_error("Failed to allocate event loop: %s", strerror(-r)); + log_error_errno(r, "Failed to allocate event loop: %m"); goto finish; } @@ -1163,13 +1212,13 @@ int main(int argc, char *argv[]) { r = context_read_data(&context); if (r < 0) { - log_error("Failed to read locale data: %s", strerror(-r)); + log_error_errno(r, "Failed to read locale data: %m"); goto finish; } r = bus_event_loop_with_idle(event, bus, "org.freedesktop.locale1", DEFAULT_EXIT_USEC, NULL, NULL); if (r < 0) { - log_error("Failed to run event loop: %s", strerror(-r)); + log_error_errno(r, "Failed to run event loop: %m"); goto finish; } diff --git a/src/login/inhibit.c b/src/login/inhibit.c index d5ea1d913c..44bda34aff 100644 --- a/src/login/inhibit.c +++ b/src/login/inhibit.c @@ -36,7 +36,7 @@ static const char* arg_what = "idle:sleep:shutdown"; static const char* arg_who = NULL; static const char* arg_why = "Unknown reason"; -static const char* arg_mode = "block"; +static const char* arg_mode = NULL; static enum { ACTION_INHIBIT, @@ -97,6 +97,9 @@ static int print_inhibitors(sd_bus *bus, sd_bus_error *error) { while ((r = sd_bus_message_read(reply, "(ssssuu)", &what, &who, &why, &mode, &uid, &pid)) > 0) { _cleanup_free_ char *comm = NULL, *u = NULL; + if (arg_mode && !streq(mode, arg_mode)) + continue; + get_process_comm(pid, &comm); u = uid_to_name(uid); @@ -205,7 +208,7 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached("Unhandled option"); } - if (arg_action == ACTION_INHIBIT && argc == 1) + if (arg_action == ACTION_INHIBIT && optind == argc) arg_action = ACTION_LIST; else if (arg_action == ACTION_INHIBIT && optind >= argc) { @@ -232,7 +235,7 @@ int main(int argc, char *argv[]) { r = sd_bus_default_system(&bus); if (r < 0) { - log_error("Failed to connect to bus: %s", strerror(-r)); + log_error_errno(r, "Failed to connect to bus: %m"); return EXIT_FAILURE; } @@ -252,6 +255,9 @@ int main(int argc, char *argv[]) { if (!arg_who) arg_who = w = strv_join(argv + optind, " "); + if (!arg_mode) + arg_mode = "block"; + fd = inhibit(bus, &error); if (fd < 0) { log_error("Failed to inhibit: %s", bus_error_message(&error, -r)); @@ -260,7 +266,7 @@ int main(int argc, char *argv[]) { pid = fork(); if (pid < 0) { - log_error("Failed to fork: %m"); + log_error_errno(errno, "Failed to fork: %m"); return EXIT_FAILURE; } @@ -270,11 +276,11 @@ int main(int argc, char *argv[]) { close_all_fds(NULL, 0); execvp(argv[optind], argv + optind); - log_error("Failed to execute %s: %m", argv[optind]); + log_error_errno(errno, "Failed to execute %s: %m", argv[optind]); _exit(EXIT_FAILURE); } - r = wait_for_terminate_and_warn(argv[optind], pid); + r = wait_for_terminate_and_warn(argv[optind], pid, true); return r < 0 ? EXIT_FAILURE : r; } diff --git a/src/login/loginctl.c b/src/login/loginctl.c index fcdf6275e9..6505eb89c1 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -384,10 +384,8 @@ static int print_session_status_info(sd_bus *bus, const char *path, bool *new_li int r; r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i); - if (r < 0) { - log_error("Could not get properties: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not get properties: %m"); if (*new_line) printf("\n"); @@ -498,7 +496,7 @@ static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i); if (r < 0) { - log_error("Could not get properties: %s", strerror(-r)); + log_error_errno(r, "Could not get properties: %m"); goto finish; } @@ -562,7 +560,7 @@ static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line) r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i); if (r < 0) { - log_error("Could not get properties: %s", strerror(-r)); + log_error_errno(r, "Could not get properties: %m"); goto finish; } @@ -617,7 +615,7 @@ static int show_properties(sd_bus *bus, const char *path, bool *new_line) { r = bus_print_all_properties(bus, "org.freedesktop.login1", path, arg_property, arg_all); if (r < 0) - log_error("Could not get properties: %s", strerror(-r)); + log_error_errno(r, "Could not get properties: %m"); return r; } @@ -699,10 +697,8 @@ static int show_user(sd_bus *bus, char **args, unsigned n) { uid_t uid; r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL); - if (r < 0) { - log_error("Failed to look up user %s: %s", args[i], strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to look up user %s: %m", args[i]); r = sd_bus_call_method( bus, @@ -859,10 +855,8 @@ static int enable_linger(sd_bus *bus, char **args, unsigned n) { uid_t uid; r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL); - if (r < 0) { - log_error("Failed to look up user %s: %s", args[i], strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to look up user %s: %m", args[i]); r = sd_bus_call_method ( bus, @@ -892,10 +886,8 @@ static int terminate_user(sd_bus *bus, char **args, unsigned n) { uid_t uid; r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL); - if (r < 0) { - log_error("Failed to look up user %s: %s", args[i], strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to look up user %s: %m", args[i]); r = sd_bus_call_method ( bus, @@ -928,10 +920,8 @@ static int kill_user(sd_bus *bus, char **args, unsigned n) { uid_t uid; r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL); - if (r < 0) { - log_error("Failed to look up user %s: %s", args[i], strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to look up user %s: %m", args[i]); r = sd_bus_call_method ( bus, @@ -1308,7 +1298,7 @@ int main(int argc, char *argv[]) { r = bus_open_transport(arg_transport, arg_host, false, &bus); if (r < 0) { - log_error("Failed to create bus connection: %s", strerror(-r)); + log_error_errno(r, "Failed to create bus connection: %m"); goto finish; } diff --git a/src/login/logind-button.c b/src/login/logind-button.c index 57e619efe6..e22b106b3c 100644 --- a/src/login/logind-button.c +++ b/src/login/logind-button.c @@ -159,8 +159,8 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u case KEY_POWER: case KEY_POWER2: log_struct(LOG_INFO, - "MESSAGE=Power key pressed.", - MESSAGE_ID(SD_MESSAGE_POWER_KEY), + LOG_MESSAGE("Power key pressed."), + LOG_MESSAGE_ID(SD_MESSAGE_POWER_KEY), NULL); manager_handle_action(b->manager, INHIBIT_HANDLE_POWER_KEY, b->manager->handle_power_key, b->manager->power_key_ignore_inhibited, true); @@ -174,8 +174,8 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u case KEY_SLEEP: log_struct(LOG_INFO, - "MESSAGE=Suspend key pressed.", - MESSAGE_ID(SD_MESSAGE_SUSPEND_KEY), + LOG_MESSAGE("Suspend key pressed."), + LOG_MESSAGE_ID(SD_MESSAGE_SUSPEND_KEY), NULL); manager_handle_action(b->manager, INHIBIT_HANDLE_SUSPEND_KEY, b->manager->handle_suspend_key, b->manager->suspend_key_ignore_inhibited, true); @@ -183,8 +183,8 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u case KEY_SUSPEND: log_struct(LOG_INFO, - "MESSAGE=Hibernate key pressed.", - MESSAGE_ID(SD_MESSAGE_HIBERNATE_KEY), + LOG_MESSAGE("Hibernate key pressed."), + LOG_MESSAGE_ID(SD_MESSAGE_HIBERNATE_KEY), NULL); manager_handle_action(b->manager, INHIBIT_HANDLE_HIBERNATE_KEY, b->manager->handle_hibernate_key, b->manager->hibernate_key_ignore_inhibited, true); @@ -195,8 +195,8 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u if (ev.code == SW_LID) { log_struct(LOG_INFO, - "MESSAGE=Lid closed.", - MESSAGE_ID(SD_MESSAGE_LID_CLOSED), + LOG_MESSAGE("Lid closed."), + LOG_MESSAGE_ID(SD_MESSAGE_LID_CLOSED), NULL); b->lid_closed = true; @@ -205,8 +205,8 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u } else if (ev.code == SW_DOCK) { log_struct(LOG_INFO, - "MESSAGE=System docked.", - MESSAGE_ID(SD_MESSAGE_SYSTEM_DOCKED), + LOG_MESSAGE("System docked."), + LOG_MESSAGE_ID(SD_MESSAGE_SYSTEM_DOCKED), NULL); b->docked = true; @@ -216,8 +216,8 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u if (ev.code == SW_LID) { log_struct(LOG_INFO, - "MESSAGE=Lid opened.", - MESSAGE_ID(SD_MESSAGE_LID_OPENED), + LOG_MESSAGE("Lid opened."), + LOG_MESSAGE_ID(SD_MESSAGE_LID_OPENED), NULL); b->lid_closed = false; @@ -225,8 +225,8 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u } else if (ev.code == SW_DOCK) { log_struct(LOG_INFO, - "MESSAGE=System undocked.", - MESSAGE_ID(SD_MESSAGE_SYSTEM_UNDOCKED), + LOG_MESSAGE("System undocked."), + LOG_MESSAGE_ID(SD_MESSAGE_SYSTEM_UNDOCKED), NULL); b->docked = false; @@ -250,20 +250,18 @@ int button_open(Button *b) { p = strappenda("/dev/input/", b->name); b->fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); - if (b->fd < 0) { - log_warning("Failed to open %s: %m", b->name); - return -errno; - } + if (b->fd < 0) + return log_warning_errno(errno, "Failed to open %s: %m", b->name); if (ioctl(b->fd, EVIOCGNAME(sizeof(name)), name) < 0) { - log_error("Failed to get input name: %m"); + log_error_errno(errno, "Failed to get input name: %m"); r = -errno; goto fail; } r = sd_event_add_io(b->manager->event, &b->io_event_source, b->fd, EPOLLIN, button_dispatch, b); if (r < 0) { - log_error("Failed to add button event: %s", strerror(-r)); + log_error_errno(r, "Failed to add button event: %m"); goto fail; } diff --git a/src/login/logind-core.c b/src/login/logind-core.c index ed7ea5da31..88694f9bac 100644 --- a/src/login/logind-core.c +++ b/src/login/logind-core.c @@ -551,7 +551,7 @@ bool manager_is_docked_or_multiple_displays(Manager *m) { * assume that we are docked. */ n = manager_count_displays(m); if (n < 0) - log_warning("Display counting failed: %s", strerror(-n)); + log_warning_errno(n, "Display counting failed: %m"); else if (n > 1) { log_debug("Multiple (%i) displays connected.", n); return true; diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index f18d21055a..48395f6f94 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -41,7 +41,7 @@ #include "bus-util.h" #include "bus-error.h" #include "logind.h" -#include "bus-errors.h" +#include "bus-common-errors.h" #include "udev-util.h" static int property_get_idle_hint( @@ -1159,7 +1159,7 @@ static int flush_devices(Manager *m) { d = opendir("/etc/udev/rules.d"); if (!d) { if (errno != ENOENT) - log_warning("Failed to open /etc/udev/rules.d: %m"); + log_warning_errno(errno, "Failed to open /etc/udev/rules.d: %m"); } else { struct dirent *de; @@ -1175,7 +1175,7 @@ static int flush_devices(Manager *m) { continue; if (unlinkat(dirfd(d), de->d_name, 0) < 0) - log_warning("Failed to unlink %s: %m", de->d_name); + log_warning_errno(errno, "Failed to unlink %s: %m", de->d_name); } } @@ -1300,9 +1300,11 @@ static int bus_manager_log_shutdown( q = NULL; } - return log_struct(LOG_NOTICE, MESSAGE_ID(SD_MESSAGE_SHUTDOWN), + return log_struct(LOG_NOTICE, + LOG_MESSAGE_ID(SD_MESSAGE_SHUTDOWN), p, - q, NULL); + q, + NULL); } static int lid_switch_ignore_handler(sd_event_source *e, uint64_t usec, void *userdata) { diff --git a/src/login/logind-inhibit.c b/src/login/logind-inhibit.c index a9e14af8db..84fee0e773 100644 --- a/src/login/logind-inhibit.c +++ b/src/login/logind-inhibit.c @@ -138,7 +138,7 @@ int inhibitor_save(Inhibitor *i) { finish: if (r < 0) - log_error("Failed to save inhibit data %s: %s", i->state_file, strerror(-r)); + log_error_errno(r, "Failed to save inhibit data %s: %m", i->state_file); return r; } diff --git a/src/login/logind-seat-dbus.c b/src/login/logind-seat-dbus.c index 1a2f09c615..ff87f0f741 100644 --- a/src/login/logind-seat-dbus.c +++ b/src/login/logind-seat-dbus.c @@ -26,7 +26,7 @@ #include "util.h" #include "bus-util.h" #include "strv.h" -#include "bus-errors.h" +#include "bus-common-errors.h" #include "bus-label.h" #include "logind.h" #include "logind-seat.h" diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c index 9992195151..8eb5e3ee0a 100644 --- a/src/login/logind-seat.c +++ b/src/login/logind-seat.c @@ -154,7 +154,7 @@ int seat_save(Seat *s) { finish: if (r < 0) - log_error("Failed to save seat data %s: %s", s->state_file, strerror(-r)); + log_error_errno(r, "Failed to save seat data %s: %m", s->state_file); return r; } @@ -201,7 +201,7 @@ int seat_preallocate_vts(Seat *s) { q = vt_allocate(i); if (q < 0) { - log_error("Failed to preallocate VT %i: %s", i, strerror(-q)); + log_error_errno(q, "Failed to preallocate VT %i: %m", i); r = q; } } @@ -221,7 +221,7 @@ int seat_apply_acls(Seat *s, Session *old_active) { !!s->active, s->active ? s->active->user->uid : 0); if (r < 0) - log_error("Failed to apply ACLs: %s", strerror(-r)); + log_error_errno(r, "Failed to apply ACLs: %m"); return r; } @@ -400,9 +400,9 @@ int seat_start(Seat *s) { return 0; log_struct(LOG_INFO, - MESSAGE_ID(SD_MESSAGE_SEAT_START), + LOG_MESSAGE_ID(SD_MESSAGE_SEAT_START), "SEAT_ID=%s", s->id, - "MESSAGE=New seat %s.", s->id, + LOG_MESSAGE("New seat %s.", s->id), NULL); /* Initialize VT magic stuff */ @@ -428,9 +428,9 @@ int seat_stop(Seat *s, bool force) { if (s->started) log_struct(LOG_INFO, - MESSAGE_ID(SD_MESSAGE_SEAT_STOP), + LOG_MESSAGE_ID(SD_MESSAGE_SEAT_STOP), "SEAT_ID=%s", s->id, - "MESSAGE=Removed seat %s.", s->id, + LOG_MESSAGE("Removed seat %s.", s->id), NULL); seat_stop_sessions(s, force); diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c index 58836fce25..8607d03265 100644 --- a/src/login/logind-session-dbus.c +++ b/src/login/logind-session-dbus.c @@ -26,7 +26,7 @@ #include "util.h" #include "strv.h" #include "bus-util.h" -#include "bus-errors.h" +#include "bus-common-errors.h" #include "bus-label.h" #include "logind.h" diff --git a/src/login/logind-session.c b/src/login/logind-session.c index 65bbb77750..ea1831dac6 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -292,7 +292,7 @@ int session_save(Session *s) { finish: if (r < 0) - log_error("Failed to save session data %s: %s", s->state_file, strerror(-r)); + log_error_errno(r, "Failed to save session data %s: %m", s->state_file); return r; } @@ -337,10 +337,8 @@ int session_load(Session *s) { "CONTROLLER", &controller, NULL); - if (r < 0) { - log_error("Failed to read %s: %s", s->state_file, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to read %s: %m", s->state_file); if (!s->user) { uid_t u; @@ -549,11 +547,11 @@ int session_start(Session *s) { return r; log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO, - MESSAGE_ID(SD_MESSAGE_SESSION_START), + LOG_MESSAGE_ID(SD_MESSAGE_SESSION_START), "SESSION_ID=%s", s->id, "USER_ID=%s", s->user->name, "LEADER="PID_FMT, s->leader, - "MESSAGE=New session %s of user %s.", s->id, s->user->name, + LOG_MESSAGE("New session %s of user %s.", s->id, s->user->name), NULL); if (!dual_timestamp_is_set(&s->timestamp)) @@ -652,11 +650,11 @@ int session_finalize(Session *s) { if (s->started) log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO, - MESSAGE_ID(SD_MESSAGE_SESSION_STOP), + LOG_MESSAGE_ID(SD_MESSAGE_SESSION_STOP), "SESSION_ID=%s", s->id, "USER_ID=%s", s->user->name, "LEADER="PID_FMT, s->leader, - "MESSAGE=Removed session %s.", s->id, + LOG_MESSAGE("Removed session %s.", s->id), NULL); s->timer_event_source = sd_event_source_unref(s->timer_event_source); @@ -969,10 +967,8 @@ static int session_open_vt(Session *s) { sprintf(path, "/dev/tty%u", s->vtnr); s->vtfd = open(path, O_RDWR | O_CLOEXEC | O_NONBLOCK | O_NOCTTY); - if (s->vtfd < 0) { - log_error("cannot open VT %s of session %s: %m", path, s->id); - return -errno; - } + if (s->vtfd < 0) + return log_error_errno(errno, "cannot open VT %s of session %s: %m", path, s->id); return s->vtfd; } @@ -991,21 +987,21 @@ int session_prepare_vt(Session *s) { r = fchown(vt, s->user->uid, -1); if (r < 0) { r = -errno; - log_error("Cannot change owner of /dev/tty%u: %m", s->vtnr); + log_error_errno(errno, "Cannot change owner of /dev/tty%u: %m", s->vtnr); goto error; } r = ioctl(vt, KDSKBMODE, K_OFF); if (r < 0) { r = -errno; - log_error("Cannot set K_OFF on /dev/tty%u: %m", s->vtnr); + log_error_errno(errno, "Cannot set K_OFF on /dev/tty%u: %m", s->vtnr); goto error; } r = ioctl(vt, KDSETMODE, KD_GRAPHICS); if (r < 0) { r = -errno; - log_error("Cannot set KD_GRAPHICS on /dev/tty%u: %m", s->vtnr); + log_error_errno(errno, "Cannot set KD_GRAPHICS on /dev/tty%u: %m", s->vtnr); goto error; } @@ -1018,7 +1014,7 @@ int session_prepare_vt(Session *s) { r = ioctl(vt, VT_SETMODE, &mode); if (r < 0) { r = -errno; - log_error("Cannot set VT_PROCESS on /dev/tty%u: %m", s->vtnr); + log_error_errno(errno, "Cannot set VT_PROCESS on /dev/tty%u: %m", s->vtnr); goto error; } @@ -1075,7 +1071,7 @@ void session_leave_vt(Session *s) { session_device_pause_all(s); r = ioctl(s->vtfd, VT_RELDISP, 1); if (r < 0) - log_debug("Cannot release VT of session %s: %m", s->id); + log_debug_errno(errno, "Cannot release VT of session %s: %m", s->id); } bool session_is_controller(Session *s, const char *sender) { diff --git a/src/login/logind-user.c b/src/login/logind-user.c index 1205b48abb..9ff1302663 100644 --- a/src/login/logind-user.c +++ b/src/login/logind-user.c @@ -252,7 +252,7 @@ int user_save(User *u) { finish: if (r < 0) - log_error("Failed to save user data %s: %s", u->state_file, strerror(-r)); + log_error_errno(r, "Failed to save user data %s: %m", u->state_file); return r; } @@ -278,7 +278,7 @@ int user_load(User *u) { if (r == -ENOENT) return 0; - log_error("Failed to read %s: %s", u->state_file, strerror(-r)); + log_error_errno(r, "Failed to read %s: %m", u->state_file); return r; } @@ -310,10 +310,8 @@ static int user_mkdir_runtime_path(User *u) { assert(u); r = mkdir_safe_label("/run/user", 0755, 0, 0); - if (r < 0) { - log_error("Failed to create /run/user: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to create /run/user: %m"); if (!u->runtime_path) { if (asprintf(&p, "/run/user/" UID_FMT, u->uid) < 0) @@ -338,7 +336,7 @@ static int user_mkdir_runtime_path(User *u) { r = mount("tmpfs", p, "tmpfs", MS_NODEV|MS_NOSUID, t); if (r < 0) { - log_error("Failed to mount per-user tmpfs directory %s: %s", p, strerror(-r)); + log_error_errno(r, "Failed to mount per-user tmpfs directory %s: %m", p); goto fail; } } @@ -510,14 +508,14 @@ static int user_remove_runtime_path(User *u) { r = rm_rf(u->runtime_path, false, false, false); if (r < 0) - log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r)); + log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path); if (umount2(u->runtime_path, MNT_DETACH) < 0) - log_error("Failed to unmount user runtime directory %s: %m", u->runtime_path); + log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path); r = rm_rf(u->runtime_path, false, true, false); if (r < 0) - log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r)); + log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path); free(u->runtime_path); u->runtime_path = NULL; diff --git a/src/login/logind.c b/src/login/logind.c index 8f00c46339..b44f376427 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -290,7 +290,7 @@ static int manager_enumerate_seats(Manager *m) { if (errno == ENOENT) return 0; - log_error("Failed to open /run/systemd/seats: %m"); + log_error_errno(errno, "Failed to open /run/systemd/seats: %m"); return -errno; } @@ -327,7 +327,7 @@ static int manager_enumerate_linger_users(Manager *m) { if (errno == ENOENT) return 0; - log_error("Failed to open /var/lib/systemd/linger/: %m"); + log_error_errno(errno, "Failed to open /var/lib/systemd/linger/: %m"); return -errno; } @@ -339,7 +339,7 @@ static int manager_enumerate_linger_users(Manager *m) { k = manager_add_user_by_name(m, de->d_name, NULL); if (k < 0) { - log_notice("Couldn't add lingering user %s: %s", de->d_name, strerror(-k)); + log_notice_errno(k, "Couldn't add lingering user %s: %m", de->d_name); r = k; } } @@ -363,7 +363,7 @@ static int manager_enumerate_users(Manager *m) { if (errno == ENOENT) return 0; - log_error("Failed to open /run/systemd/users: %m"); + log_error_errno(errno, "Failed to open /run/systemd/users: %m"); return -errno; } @@ -375,7 +375,7 @@ static int manager_enumerate_users(Manager *m) { k = manager_add_user_by_name(m, de->d_name, &u); if (k < 0) { - log_error("Failed to add user by file name %s: %s", de->d_name, strerror(-k)); + log_error_errno(k, "Failed to add user by file name %s: %m", de->d_name); r = k; continue; @@ -404,7 +404,7 @@ static int manager_enumerate_sessions(Manager *m) { if (errno == ENOENT) return 0; - log_error("Failed to open /run/systemd/sessions: %m"); + log_error_errno(errno, "Failed to open /run/systemd/sessions: %m"); return -errno; } @@ -423,7 +423,7 @@ static int manager_enumerate_sessions(Manager *m) { k = manager_add_session(m, de->d_name, &s); if (k < 0) { - log_error("Failed to add session by file name %s: %s", de->d_name, strerror(-k)); + log_error_errno(k, "Failed to add session by file name %s: %m", de->d_name); r = k; continue; @@ -451,7 +451,7 @@ static int manager_enumerate_inhibitors(Manager *m) { if (errno == ENOENT) return 0; - log_error("Failed to open /run/systemd/inhibit: %m"); + log_error_errno(errno, "Failed to open /run/systemd/inhibit: %m"); return -errno; } @@ -464,7 +464,7 @@ static int manager_enumerate_inhibitors(Manager *m) { k = manager_add_inhibitor(m, de->d_name, &i); if (k < 0) { - log_notice("Couldn't add inhibitor %s: %s", de->d_name, strerror(-k)); + log_notice_errno(k, "Couldn't add inhibitor %s: %m", de->d_name); r = k; continue; } @@ -568,7 +568,7 @@ static int manager_reserve_vt(Manager *m) { /* Don't complain on VT-less systems */ if (errno != ENOENT) - log_warning("Failed to pin reserved VT: %m"); + log_warning_errno(errno, "Failed to pin reserved VT: %m"); return -errno; } @@ -583,52 +583,36 @@ static int manager_connect_bus(Manager *m) { assert(!m->bus); r = sd_bus_default_system(&m->bus); - if (r < 0) { - log_error("Failed to connect to system bus: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to connect to system bus: %m"); r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/login1", "org.freedesktop.login1.Manager", manager_vtable, m); - if (r < 0) { - log_error("Failed to add manager object vtable: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add manager object vtable: %m"); r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/login1/seat", "org.freedesktop.login1.Seat", seat_vtable, seat_object_find, m); - if (r < 0) { - log_error("Failed to add seat object vtable: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add seat object vtable: %m"); r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/login1/seat", seat_node_enumerator, m); - if (r < 0) { - log_error("Failed to add seat enumerator: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add seat enumerator: %m"); r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/login1/session", "org.freedesktop.login1.Session", session_vtable, session_object_find, m); - if (r < 0) { - log_error("Failed to add session object vtable: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add session object vtable: %m"); r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/login1/session", session_node_enumerator, m); - if (r < 0) { - log_error("Failed to add session enumerator: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add session enumerator: %m"); r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/login1/user", "org.freedesktop.login1.User", user_vtable, user_object_find, m); - if (r < 0) { - log_error("Failed to add user object vtable: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add user object vtable: %m"); r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/login1/user", user_node_enumerator, m); - if (r < 0) { - log_error("Failed to add user enumerator: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add user enumerator: %m"); r = sd_bus_add_match(m->bus, NULL, @@ -638,10 +622,8 @@ static int manager_connect_bus(Manager *m) { "member='NameOwnerChanged'," "path='/org/freedesktop/DBus'", match_name_owner_changed, m); - if (r < 0) { - log_error("Failed to add match for NameOwnerChanged: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add match for NameOwnerChanged: %m"); r = sd_bus_add_match(m->bus, NULL, @@ -651,10 +633,8 @@ static int manager_connect_bus(Manager *m) { "member='JobRemoved'," "path='/org/freedesktop/systemd1'", match_job_removed, m); - if (r < 0) { - log_error("Failed to add match for JobRemoved: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add match for JobRemoved: %m"); r = sd_bus_add_match(m->bus, NULL, @@ -664,10 +644,8 @@ static int manager_connect_bus(Manager *m) { "member='UnitRemoved'," "path='/org/freedesktop/systemd1'", match_unit_removed, m); - if (r < 0) { - log_error("Failed to add match for UnitRemoved: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add match for UnitRemoved: %m"); r = sd_bus_add_match(m->bus, NULL, @@ -676,10 +654,8 @@ static int manager_connect_bus(Manager *m) { "interface='org.freedesktop.DBus.Properties'," "member='PropertiesChanged'", match_properties_changed, m); - if (r < 0) { - log_error("Failed to add match for PropertiesChanged: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add match for PropertiesChanged: %m"); r = sd_bus_add_match(m->bus, NULL, @@ -689,10 +665,8 @@ static int manager_connect_bus(Manager *m) { "member='Reloading'," "path='/org/freedesktop/systemd1'", match_reloading, m); - if (r < 0) { - log_error("Failed to add match for Reloading: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add match for Reloading: %m"); r = sd_bus_call_method( m->bus, @@ -708,16 +682,12 @@ static int manager_connect_bus(Manager *m) { } r = sd_bus_request_name(m->bus, "org.freedesktop.login1", 0); - if (r < 0) { - log_error("Failed to register name: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to register name: %m"); r = sd_bus_attach_event(m->bus, m->event, 0); - if (r < 0) { - log_error("Failed to attach bus to event loop: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to attach bus to event loop: %m"); return 0; } @@ -783,7 +753,7 @@ static int manager_connect_console(Manager *m) { if (errno == ENOENT) return 0; - log_error("Failed to open /sys/class/tty/tty0/active: %m"); + log_error_errno(errno, "Failed to open /sys/class/tty/tty0/active: %m"); return -errno; } @@ -806,16 +776,12 @@ static int manager_connect_console(Manager *m) { } r = ignore_signals(SIGRTMIN + 1, -1); - if (r < 0) { - log_error("Cannot ignore SIGRTMIN + 1: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Cannot ignore SIGRTMIN + 1: %m"); r = sigprocmask_many(SIG_BLOCK, SIGRTMIN, -1); - if (r < 0) { - log_error("Cannot block SIGRTMIN: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Cannot block SIGRTMIN: %m"); r = sd_event_add_signal(m->event, NULL, SIGRTMIN, manager_vt_switch, m); if (r < 0) @@ -1016,28 +982,20 @@ static int manager_dispatch_idle_action(sd_event_source *s, uint64_t t, void *us CLOCK_MONOTONIC, elapse, USEC_PER_SEC*30, manager_dispatch_idle_action, m); - if (r < 0) { - log_error("Failed to add idle event source: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add idle event source: %m"); r = sd_event_source_set_priority(m->idle_action_event_source, SD_EVENT_PRIORITY_IDLE+10); - if (r < 0) { - log_error("Failed to set idle event source priority: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to set idle event source priority: %m"); } else { r = sd_event_source_set_time(m->idle_action_event_source, elapse); - if (r < 0) { - log_error("Failed to set idle event timer: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to set idle event timer: %m"); r = sd_event_source_set_enabled(m->idle_action_event_source, SD_EVENT_ONESHOT); - if (r < 0) { - log_error("Failed to enable idle event timer: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to enable idle event timer: %m"); } return 0; @@ -1061,10 +1019,8 @@ int manager_startup(Manager *m) { /* Connect to udev */ r = manager_connect_udev(m); - if (r < 0) { - log_error("Failed to create udev watchers: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to create udev watchers: %m"); /* Connect to the bus */ r = manager_connect_bus(m); @@ -1073,39 +1029,37 @@ int manager_startup(Manager *m) { /* Instantiate magic seat 0 */ r = manager_add_seat(m, "seat0", &m->seat0); - if (r < 0) { - log_error("Failed to add seat0: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add seat0: %m"); r = manager_set_lid_switch_ignore(m, 0 + IGNORE_LID_SWITCH_STARTUP_USEC); if (r < 0) - log_warning("Failed to set up lid switch ignore event source: %s", strerror(-r)); + log_warning_errno(r, "Failed to set up lid switch ignore event source: %m"); /* Deserialize state */ r = manager_enumerate_devices(m); if (r < 0) - log_warning("Device enumeration failed: %s", strerror(-r)); + log_warning_errno(r, "Device enumeration failed: %m"); r = manager_enumerate_seats(m); if (r < 0) - log_warning("Seat enumeration failed: %s", strerror(-r)); + log_warning_errno(r, "Seat enumeration failed: %m"); r = manager_enumerate_users(m); if (r < 0) - log_warning("User enumeration failed: %s", strerror(-r)); + log_warning_errno(r, "User enumeration failed: %m"); r = manager_enumerate_sessions(m); if (r < 0) - log_warning("Session enumeration failed: %s", strerror(-r)); + log_warning_errno(r, "Session enumeration failed: %m"); r = manager_enumerate_inhibitors(m); if (r < 0) - log_warning("Inhibitor enumeration failed: %s", strerror(-r)); + log_warning_errno(r, "Inhibitor enumeration failed: %m"); r = manager_enumerate_buttons(m); if (r < 0) - log_warning("Button enumeration failed: %s", strerror(-r)); + log_warning_errno(r, "Button enumeration failed: %m"); /* Remove stale objects before we start them */ manager_gc(m, false); @@ -1171,10 +1125,11 @@ int manager_run(Manager *m) { static int manager_parse_config_file(Manager *m) { assert(m); - return config_parse(NULL, "/etc/systemd/logind.conf", NULL, - "Login\0", - config_item_perf_lookup, logind_gperf_lookup, - false, false, true, m); + return config_parse_many("/etc/systemd/logind.conf", + CONF_DIRS_NULSTR("systemd/logind.conf"), + "Login\0", + config_item_perf_lookup, logind_gperf_lookup, + false, m); } int main(int argc, char *argv[]) { @@ -1213,7 +1168,7 @@ int main(int argc, char *argv[]) { r = manager_startup(m); if (r < 0) { - log_error("Failed to fully start up daemon: %s", strerror(-r)); + log_error_errno(r, "Failed to fully start up daemon: %m"); goto finish; } diff --git a/src/login/logind.conf b/src/login/logind.conf index 4608a2c0e2..6b1943a2d1 100644 --- a/src/login/logind.conf +++ b/src/login/logind.conf @@ -5,6 +5,9 @@ # the Free Software Foundation; either version 2.1 of the License, or # (at your option) any later version. # +# You can override the directives in this file by creating files in +# /etc/systemd/logind.conf.d/*.conf. +# # See logind.conf(5) for details [Login] diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c index 0c71177deb..111e2b7c4c 100644 --- a/src/login/pam_systemd.c +++ b/src/login/pam_systemd.c @@ -114,7 +114,7 @@ static int get_user_data( } *ret_pw = pw; - *ret_username = username ? username : pw->pw_name; + *ret_username = username; return PAM_SUCCESS; } @@ -180,11 +180,10 @@ static int export_legacy_dbus_address( int r; /* skip export if kdbus is not active */ - if (access("/dev/kdbus", F_OK) < 0) + if (access("/sys/fs/kdbus", F_OK) < 0) return PAM_SUCCESS; - if (asprintf(&s, KERNEL_USER_BUS_FMT ";" UNIX_USER_BUS_FMT, - uid, runtime) < 0) { + if (asprintf(&s, KERNEL_USER_BUS_ADDRESS_FMT ";" UNIX_USER_BUS_ADDRESS_FMT, uid, runtime) < 0) { pam_syslog(handle, LOG_ERR, "Failed to set bus variable."); return PAM_BUF_ERR; } diff --git a/src/login/test-inhibit.c b/src/login/test-inhibit.c index 21d2339616..5379ffcaa1 100644 --- a/src/login/test-inhibit.c +++ b/src/login/test-inhibit.c @@ -42,11 +42,11 @@ static int inhibit(sd_bus *bus, const char *what) { &error, &reply, "ssss", what, who, reason, mode); - assert(r >= 0); + assert_se(r >= 0); r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_UNIX_FD, &fd); - assert(r >= 0); - assert(fd >= 0); + assert_se(r >= 0); + assert_se(fd >= 0); return dup(fd); } @@ -67,10 +67,10 @@ static void print_inhibitors(sd_bus *bus) { &error, &reply, ""); - assert(r >= 0); + assert_se(r >= 0); r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssuu)"); - assert(r >= 0); + assert_se(r >= 0); while ((r = sd_bus_message_read(reply, "(ssssuu)", &what, &who, &why, &mode, &uid, &pid)) > 0) { printf("what=<%s> who=<%s> why=<%s> mode=<%s> uid=<"UID_FMT"> pid=<"PID_FMT">\n", @@ -78,7 +78,7 @@ static void print_inhibitors(sd_bus *bus) { n++; } - assert(r >= 0); + assert_se(r >= 0); printf("%u inhibitors\n", n); } @@ -89,16 +89,16 @@ int main(int argc, char*argv[]) { int r; r = sd_bus_open_system(&bus); - assert(r >= 0); + assert_se(r >= 0); print_inhibitors(bus); fd1 = inhibit(bus, "sleep"); - assert(fd1 >= 0); + assert_se(fd1 >= 0); print_inhibitors(bus); fd2 = inhibit(bus, "idle:shutdown"); - assert(fd2 >= 0); + assert_se(fd2 >= 0); print_inhibitors(bus); safe_close(fd1); diff --git a/src/login/user-sessions.c b/src/login/user-sessions.c index ca5de41f40..6edb823e8c 100644 --- a/src/login/user-sessions.c +++ b/src/login/user-sessions.c @@ -44,7 +44,7 @@ int main(int argc, char*argv[]) { int r = 0; if (unlink("/run/nologin") < 0 && errno != ENOENT) { - log_error("Failed to remove /run/nologin file: %m"); + log_error_errno(errno, "Failed to remove /run/nologin file: %m"); r = -errno; } @@ -55,7 +55,7 @@ int main(int argc, char*argv[]) { * exist), don't complain */ if (errno != EROFS || access("/etc/nologin", F_OK) >= 0) { - log_error("Failed to remove /etc/nologin file: %m"); + log_error_errno(errno, "Failed to remove /etc/nologin file: %m"); return EXIT_FAILURE; } } @@ -68,7 +68,7 @@ int main(int argc, char*argv[]) { r = write_string_file_atomic("/run/nologin", "System is going down."); if (r < 0) { - log_error("Failed to create /run/nologin: %s", strerror(-r)); + log_error_errno(r, "Failed to create /run/nologin: %m"); return EXIT_FAILURE; } diff --git a/src/machine-id-commit/machine-id-commit.c b/src/machine-id-commit/machine-id-commit.c new file mode 100644 index 0000000000..c7e4de8889 --- /dev/null +++ b/src/machine-id-commit/machine-id-commit.c @@ -0,0 +1,105 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2014 Didier Roche + + 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 <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <getopt.h> +#include <errno.h> + +#include "machine-id-setup.h" +#include "log.h" +#include "build.h" + +static const char *arg_root = ""; + +static void help(void) { + printf("%s [OPTIONS...]\n\n" + "Commit a transient /etc/machine-id on disk if writable.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --root=ROOT Filesystem root\n", + program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_ROOT, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "root", required_argument, NULL, ARG_ROOT }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "hqcv", options, NULL)) >= 0) + switch (c) { + + case 'h': + help(); + return 0; + + case ARG_VERSION: + puts(PACKAGE_STRING); + puts(SYSTEMD_FEATURES); + return 0; + + case ARG_ROOT: + arg_root = optarg; + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (optind < argc) { + log_error("Extraneous arguments"); + return -EINVAL; + } + + return 1; +} + +int main(int argc, char *argv[]) { + int r; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + + return machine_id_commit(arg_root) < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index f12ce98a84..72ae6c6535 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -27,7 +27,7 @@ #include "bus-util.h" #include "bus-label.h" #include "strv.h" -#include "bus-errors.h" +#include "bus-common-errors.h" #include "copy.h" #include "fileio.h" #include "in-addr-util.h" @@ -207,7 +207,7 @@ int bus_machine_method_get_addresses(sd_bus *bus, sd_bus_message *message, void if (r < 0) _exit(EXIT_FAILURE); - n = local_addresses(NULL, 0, &addresses); + n = local_addresses(NULL, 0, AF_UNSPEC, &addresses); if (n < 0) _exit(EXIT_FAILURE); diff --git a/src/machine/machine.c b/src/machine/machine.c index 13d3448adf..0d84213eea 100644 --- a/src/machine/machine.c +++ b/src/machine/machine.c @@ -216,7 +216,7 @@ finish: if (temp_path) unlink(temp_path); - log_error("Failed to save machine data %s: %s", m->state_file, strerror(-r)); + log_error_errno(r, "Failed to save machine data %s: %m", m->state_file); } return r; @@ -259,8 +259,7 @@ int machine_load(Machine *m) { if (r == -ENOENT) return 0; - log_error("Failed to read %s: %s", m->state_file, strerror(-r)); - return r; + return log_error_errno(r, "Failed to read %s: %m", m->state_file); } if (id) @@ -377,10 +376,10 @@ int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) { return r; log_struct(LOG_INFO, - MESSAGE_ID(SD_MESSAGE_MACHINE_START), + LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_START), "NAME=%s", m->name, "LEADER="PID_FMT, m->leader, - "MESSAGE=New machine %s.", m->name, + LOG_MESSAGE("New machine %s.", m->name), NULL); if (!dual_timestamp_is_set(&m->timestamp)) @@ -426,10 +425,10 @@ int machine_stop(Machine *m) { if (m->started) log_struct(LOG_INFO, - MESSAGE_ID(SD_MESSAGE_MACHINE_STOP), + LOG_MESSAGE_ID(SD_MESSAGE_MACHINE_STOP), "NAME=%s", m->name, "LEADER="PID_FMT, m->leader, - "MESSAGE=Machine %s terminated.", m->name, + LOG_MESSAGE("Machine %s terminated.", m->name), NULL); /* Kill cgroup */ diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 7491d69c8b..f604263d8f 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -44,6 +44,7 @@ #include "cgroup-show.h" #include "cgroup-util.h" #include "ptyfwd.h" +#include "event-util.h" static char **arg_property = NULL; static bool arg_all = false; @@ -413,10 +414,8 @@ static int show_info(const char *verb, sd_bus *bus, const char *path, bool *new_ path, map, &info); - if (r < 0) { - log_error("Could not get properties: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not get properties: %m"); if (*new_line) printf("\n"); @@ -444,7 +443,7 @@ static int show_properties(sd_bus *bus, const char *path, bool *new_line) { r = bus_print_all_properties(bus, "org.freedesktop.machine1", path, arg_property, arg_all); if (r < 0) - log_error("Could not get properties: %s", strerror(-r)); + log_error_errno(r, "Could not get properties: %m"); return r; } @@ -662,12 +661,14 @@ static int login_machine(sd_bus *bus, char **args, unsigned n) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *reply2 = NULL, *reply3 = NULL; _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_bus_close_unref_ sd_bus *container_bus = NULL; + _cleanup_(pty_forward_freep) PTYForward *forward = NULL; + _cleanup_event_unref_ sd_event *event = NULL; _cleanup_close_ int master = -1; _cleanup_free_ char *getty = NULL; const char *path, *pty, *p; uint32_t leader; sigset_t mask; - int r; + int r, ret = 0; assert(bus); assert(args); @@ -677,6 +678,14 @@ static int login_machine(sd_bus *bus, char **args, unsigned n) { return -ENOTSUP; } + r = sd_event_default(&event); + if (r < 0) + return log_error_errno(r, "Failed to get event loop: %m"); + + r = sd_bus_attach_event(bus, event, 0); + if (r < 0) + return log_error_errno(r, "Failed to attach bus to event loop: %m"); + r = sd_bus_call_method( bus, "org.freedesktop.machine1", @@ -704,26 +713,20 @@ static int login_machine(sd_bus *bus, char **args, unsigned n) { &error, &reply2, "u"); - if (r < 0) { - log_error("Failed to retrieve PID of leader: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to retrieve PID of leader: %m"); r = sd_bus_message_read(reply2, "u", &leader); if (r < 0) return bus_log_parse_error(r); master = openpt_in_namespace(leader, O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY); - if (master < 0) { - log_error("Failed to acquire pseudo tty: %s", strerror(-master)); - return master; - } + if (master < 0) + return log_error_errno(master, "Failed to acquire pseudo tty: %m"); pty = ptsname(master); - if (!pty) { - log_error("Failed to get pty name: %m"); - return -errno; - } + if (!pty) + return log_error_errno(errno, "Failed to get pty name: %m"); p = startswith(pty, "/dev/pts/"); if (!p) { @@ -732,19 +735,15 @@ static int login_machine(sd_bus *bus, char **args, unsigned n) { } r = sd_bus_open_system_container(&container_bus, args[1]); - if (r < 0) { - log_error("Failed to get container bus: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get container bus: %m"); getty = strjoin("container-getty@", p, ".service", NULL); if (!getty) return log_oom(); - if (unlockpt(master) < 0) { - log_error("Failed to unlock tty: %m"); - return -errno; - } + if (unlockpt(master) < 0) + return log_error_errno(errno, "Failed to unlock tty: %m"); r = sd_bus_call_method(container_bus, "org.freedesktop.systemd1", @@ -766,17 +765,25 @@ static int login_machine(sd_bus *bus, char **args, unsigned n) { log_info("Connected to container %s. Press ^] three times within 1s to exit session.", args[1]); - r = process_pty(master, &mask, 0, 0); - if (r < 0) { - log_error("Failed to process pseudo tty: %s", strerror(-r)); - return r; - } + sd_event_add_signal(event, NULL, SIGINT, NULL, NULL); + sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); + + r = pty_forward_new(event, master, &forward); + if (r < 0) + return log_error_errno(r, "Failed to create PTY forwarder: %m"); + + r = sd_event_loop(event); + if (r < 0) + return log_error_errno(r, "Failed to run event loop: %m"); + + forward = pty_forward_free(forward); fputc('\n', stdout); log_info("Connection to container %s terminated.", args[1]); - return 0; + sd_event_get_exit_code(event, &ret); + return ret; } static void help(void) { @@ -1002,7 +1009,7 @@ int main(int argc, char*argv[]) { r = bus_open_transport(arg_transport, arg_host, false, &bus); if (r < 0) { - log_error("Failed to create bus connection: %s", strerror(-r)); + log_error_errno(r, "Failed to create bus connection: %m"); goto finish; } diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 3c7d4be8d2..0b57b3699c 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -36,7 +36,7 @@ #include "utf8.h" #include "unit-name.h" #include "bus-util.h" -#include "bus-errors.h" +#include "bus-common-errors.h" #include "time-util.h" #include "cgroup-util.h" #include "machined.h" @@ -442,8 +442,8 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_METHOD("GetMachineByPID", "u", "o", method_get_machine_by_pid, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ListMachines", NULL, "a(ssso)", method_list_machines, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CreateMachine", "sayssusa(sv)", "o", method_create_machine, 0), - SD_BUS_METHOD("RegisterMachine", "sayssus", "o", method_register_machine, 0), SD_BUS_METHOD("CreateMachineWithNetwork", "sayssusaia(sv)", "o", method_create_machine_with_network, 0), + SD_BUS_METHOD("RegisterMachine", "sayssus", "o", method_register_machine, 0), SD_BUS_METHOD("RegisterMachineWithNetwork", "sayssusai", "o", method_register_machine_with_network, 0), SD_BUS_METHOD("KillMachine", "ssi", NULL, method_kill_machine, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)), SD_BUS_METHOD("TerminateMachine", "s", NULL, method_terminate_machine, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)), @@ -622,6 +622,10 @@ int manager_start_scope( if (r < 0) return r; + r = sd_bus_message_append(m, "(sv)", "Delegate", "b", 1); + if (r < 0) + return r; + if (more_properties) { r = sd_bus_message_copy(m, more_properties, true); if (r < 0) diff --git a/src/machine/machined.c b/src/machine/machined.c index 966475b242..ef59497fb2 100644 --- a/src/machine/machined.c +++ b/src/machine/machined.c @@ -96,7 +96,7 @@ int manager_enumerate_machines(Manager *m) { if (errno == ENOENT) return 0; - log_error("Failed to open /run/systemd/machines: %m"); + log_error_errno(errno, "Failed to open /run/systemd/machines: %m"); return -errno; } @@ -113,7 +113,7 @@ int manager_enumerate_machines(Manager *m) { k = manager_add_machine(m, de->d_name, &machine); if (k < 0) { - log_error("Failed to add machine by file name %s: %s", de->d_name, strerror(-k)); + log_error_errno(k, "Failed to add machine by file name %s: %m", de->d_name); r = k; continue; @@ -137,28 +137,20 @@ static int manager_connect_bus(Manager *m) { assert(!m->bus); r = sd_bus_default_system(&m->bus); - if (r < 0) { - log_error("Failed to connect to system bus: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to connect to system bus: %m"); r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", manager_vtable, m); - if (r < 0) { - log_error("Failed to add manager object vtable: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add manager object vtable: %m"); r = sd_bus_add_fallback_vtable(m->bus, NULL, "/org/freedesktop/machine1/machine", "org.freedesktop.machine1.Machine", machine_vtable, machine_object_find, m); - if (r < 0) { - log_error("Failed to add machine object vtable: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add machine object vtable: %m"); r = sd_bus_add_node_enumerator(m->bus, NULL, "/org/freedesktop/machine1/machine", machine_node_enumerator, m); - if (r < 0) { - log_error("Failed to add machine enumerator: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add machine enumerator: %m"); r = sd_bus_add_match(m->bus, NULL, @@ -169,10 +161,8 @@ static int manager_connect_bus(Manager *m) { "path='/org/freedesktop/systemd1'", match_job_removed, m); - if (r < 0) { - log_error("Failed to add match for JobRemoved: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add match for JobRemoved: %m"); r = sd_bus_add_match(m->bus, NULL, @@ -183,10 +173,8 @@ static int manager_connect_bus(Manager *m) { "path='/org/freedesktop/systemd1'", match_unit_removed, m); - if (r < 0) { - log_error("Failed to add match for UnitRemoved: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add match for UnitRemoved: %m"); r = sd_bus_add_match(m->bus, NULL, @@ -196,10 +184,8 @@ static int manager_connect_bus(Manager *m) { "member='PropertiesChanged'", match_properties_changed, m); - if (r < 0) { - log_error("Failed to add match for PropertiesChanged: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add match for PropertiesChanged: %m"); r = sd_bus_add_match(m->bus, NULL, @@ -210,10 +196,8 @@ static int manager_connect_bus(Manager *m) { "path='/org/freedesktop/systemd1'", match_reloading, m); - if (r < 0) { - log_error("Failed to add match for Reloading: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add match for Reloading: %m"); r = sd_bus_call_method( m->bus, @@ -229,16 +213,12 @@ static int manager_connect_bus(Manager *m) { } r = sd_bus_request_name(m->bus, "org.freedesktop.machine1", 0); - if (r < 0) { - log_error("Failed to register name: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to register name: %m"); r = sd_bus_attach_event(m->bus, m->event, 0); - if (r < 0) { - log_error("Failed to attach bus to event loop: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to attach bus to event loop: %m"); return 0; } @@ -335,7 +315,7 @@ int main(int argc, char *argv[]) { r = manager_startup(m); if (r < 0) { - log_error("Failed to fully start up daemon: %s", strerror(-r)); + log_error_errno(r, "Failed to fully start up daemon: %m"); goto finish; } diff --git a/src/modules-load/modules-load.c b/src/modules-load/modules-load.c index c77b092a62..5f678789ce 100644 --- a/src/modules-load/modules-load.c +++ b/src/modules-load/modules-load.c @@ -38,21 +38,13 @@ static char **arg_proc_cmdline_modules = NULL; -static const char conf_file_dirs[] = - "/etc/modules-load.d\0" - "/run/modules-load.d\0" - "/usr/local/lib/modules-load.d\0" - "/usr/lib/modules-load.d\0" -#ifdef HAVE_SPLIT_USR - "/lib/modules-load.d\0" -#endif - ; +static const char conf_file_dirs[] = CONF_DIRS_NULSTR("modules-load"); static void systemd_kmod_log(void *data, int priority, const char *file, int line, const char *fn, const char *format, va_list args) { DISABLE_WARNING_FORMAT_NONLITERAL; - log_metav(priority, file, line, fn, format, args); + log_internalv(priority, 0, file, line, fn, format, args); REENABLE_WARNING; } @@ -89,10 +81,8 @@ static int load_module(struct kmod_ctx *ctx, const char *m) { log_debug("load: %s", m); r = kmod_module_new_from_lookup(ctx, m, &modlist); - if (r < 0) { - log_error("Failed to lookup alias '%s': %s", m, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to lookup alias '%s': %m", m); if (!modlist) { log_error("Failed to find module '%s'", m); @@ -124,8 +114,7 @@ static int load_module(struct kmod_ctx *ctx, const char *m) { else if (err == KMOD_PROBE_APPLY_BLACKLIST) log_info("Module '%s' is blacklisted", kmod_module_get_name(mod)); else { - log_error("Failed to insert '%s': %s", kmod_module_get_name(mod), - strerror(-err)); + log_error_errno(err, "Failed to insert '%s': %m", kmod_module_get_name(mod)); r = err; } } @@ -150,8 +139,7 @@ static int apply_file(struct kmod_ctx *ctx, const char *path, bool ignore_enoent if (ignore_enoent && r == -ENOENT) return 0; - log_error("Failed to open %s, ignoring: %s", path, strerror(-r)); - return r; + return log_error_errno(r, "Failed to open %s, ignoring: %m", path); } log_debug("apply: %s", path); @@ -163,7 +151,7 @@ static int apply_file(struct kmod_ctx *ctx, const char *path, bool ignore_enoent if (feof(f)) break; - log_error("Failed to read file '%s', ignoring: %m", path); + log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path); return -errno; } @@ -243,8 +231,9 @@ int main(int argc, char *argv[]) { umask(0022); - if (parse_proc_cmdline(parse_proc_cmdline_item) < 0) - return EXIT_FAILURE; + r = parse_proc_cmdline(parse_proc_cmdline_item); + if (r < 0) + log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); ctx = kmod_new(NULL, NULL); if (!ctx) { @@ -278,7 +267,7 @@ int main(int argc, char *argv[]) { k = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); if (k < 0) { - log_error("Failed to enumerate modules-load.d files: %s", strerror(-k)); + log_error_errno(k, "Failed to enumerate modules-load.d files: %m"); if (r == 0) r = k; goto finish; diff --git a/src/network/networkctl.c b/src/network/networkctl.c index b374121fbc..815ea1698d 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -34,6 +34,7 @@ #include "arphrd-list.h" #include "local-addresses.h" #include "socket-util.h" +#include "ether-addr-util.h" static bool arg_no_pager = false; static bool arg_legend = true; @@ -190,16 +191,12 @@ static int list_links(char **args, unsigned n) { pager_open_if_enabled(); r = sd_rtnl_open(&rtnl, 0); - if (r < 0) { - log_error("Failed to connect to netlink: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); udev = udev_new(); - if (!udev) { - log_error("Failed to connect to udev: %m"); - return -errno; - } + if (!udev) + return log_error_errno(errno, "Failed to connect to udev: %m"); r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0); if (r < 0) @@ -210,10 +207,8 @@ static int list_links(char **args, unsigned n) { return rtnl_log_create_error(r); r = sd_rtnl_call(rtnl, req, 0, &reply); - if (r < 0) { - log_error("Failed to enumerate links: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to enumerate links: %m"); if (arg_legend) printf("%3s %-16s %-18s %-11s %-10s\n", "IDX", "LINK", "TYPE", "OPERATIONAL", "SETUP"); @@ -253,11 +248,171 @@ static int list_links(char **args, unsigned n) { return 0; } +/* IEEE Organizationally Unique Identifier vendor string */ +static int ieee_oui(struct udev_hwdb *hwdb, struct ether_addr *mac, char **ret) { + struct udev_list_entry *entry; + char *description; + char str[strlen("OUI:XXYYXXYYXXYY") + 1]; + + /* skip commonly misused 00:00:00 (Xerox) prefix */ + if (memcmp(mac, "\0\0\0", 3) == 0) + return -EINVAL; + + snprintf(str, sizeof(str), "OUI:" ETHER_ADDR_FORMAT_STR, ETHER_ADDR_FORMAT_VAL(*mac)); + + udev_list_entry_foreach(entry, udev_hwdb_get_properties_list_entry(hwdb, str, 0)) + if (strcmp(udev_list_entry_get_name(entry), "ID_OUI_FROM_DATABASE") == 0) { + description = strdup(udev_list_entry_get_value(entry)); + if (!description) + return -ENOMEM; + + *ret = description; + return 0; + } + + return -ENODATA; +} + +static int get_gateway_description(sd_rtnl *rtnl, struct udev_hwdb *hwdb, int ifindex, int family, + union in_addr_union *gateway, char **gateway_description) { + _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL; + sd_rtnl_message *m; + int r; + + assert(rtnl); + assert(ifindex >= 0); + assert(family == AF_INET || family == AF_INET6); + assert(gateway); + assert(gateway_description); + + r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_GETNEIGH, ifindex, family); + if (r < 0) + return r; + + r = sd_rtnl_message_request_dump(req, true); + if (r < 0) + return r; + + r = sd_rtnl_call(rtnl, req, 0, &reply); + if (r < 0) + return r; + + for (m = reply; m; m = sd_rtnl_message_next(m)) { + union in_addr_union gw = {}; + struct ether_addr mac = {}; + uint16_t type; + int ifi, fam; + + r = sd_rtnl_message_get_errno(m); + if (r < 0) { + log_error_errno(r, "got error: %m"); + continue; + } + + r = sd_rtnl_message_get_type(m, &type); + if (r < 0) { + log_error_errno(r, "could not get type: %m"); + continue; + } + + if (type != RTM_NEWNEIGH) { + log_error("type is not RTM_NEWNEIGH"); + continue; + } + + r = sd_rtnl_message_neigh_get_family(m, &fam); + if (r < 0) { + log_error_errno(r, "could not get family: %m"); + continue; + } + + if (fam != family) { + log_error("family is not correct"); + continue; + } + + r = sd_rtnl_message_neigh_get_ifindex(m, &ifi); + if (r < 0) { + log_error_errno(r, "could not get ifindex: %m"); + continue; + } + + if (ifindex > 0 && ifi != ifindex) + continue; + + switch (fam) { + case AF_INET: + r = sd_rtnl_message_read_in_addr(m, NDA_DST, &gw.in); + if (r < 0) + continue; + + break; + case AF_INET6: + r = sd_rtnl_message_read_in6_addr(m, NDA_DST, &gw.in6); + if (r < 0) + continue; + + break; + default: + continue; + } + + if (!in_addr_equal(fam, &gw, gateway)) + continue; + + r = sd_rtnl_message_read_ether_addr(m, NDA_LLADDR, &mac); + if (r < 0) + continue; + + r = ieee_oui(hwdb, &mac, gateway_description); + if (r < 0) + continue; + + return 0; + } + + return -ENODATA; +} + +static int dump_gateways(sd_rtnl *rtnl, struct udev_hwdb *hwdb, const char *prefix, int ifindex) { + _cleanup_free_ struct local_address *local = NULL; + int r, n, i; + + n = local_gateways(rtnl, ifindex, AF_UNSPEC, &local); + if (n < 0) + return n; + + for (i = 0; i < n; i++) { + _cleanup_free_ char *gateway = NULL, *description = NULL; + + r = in_addr_to_string(local[i].family, &local[i].address, &gateway); + if (r < 0) + return r; + + r = get_gateway_description(rtnl, hwdb, ifindex, local[i].family, &local[i].address, &description); + if (r < 0) + log_debug_errno(r, "Could not get description of gateway: %m"); + + if (description) + printf("%*s%s (%s)\n", + (int) strlen(prefix), + i == 0 ? prefix : "", + gateway, description); + else + printf("%*s%s\n", + (int) strlen(prefix), + i == 0 ? prefix : "", + gateway); + } + + return 0; +} + static int dump_addresses(sd_rtnl *rtnl, const char *prefix, int ifindex) { _cleanup_free_ struct local_address *local = NULL; int r, n, i; - n = local_addresses(rtnl, ifindex, &local); + n = local_addresses(rtnl, ifindex, AF_UNSPEC, &local); if (n < 0) return n; @@ -293,6 +448,7 @@ static int link_status_one(sd_rtnl *rtnl, struct udev *udev, const char *name) { _cleanup_free_ char *setup_state = NULL, *operational_state = NULL; _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL; _cleanup_udev_device_unref_ struct udev_device *d = NULL; + _cleanup_udev_hwdb_unref_ struct udev_hwdb *hwdb = NULL; char devid[2 + DECIMAL_STR_MAX(int)]; _cleanup_free_ char *t = NULL, *network = NULL; const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL; @@ -322,10 +478,8 @@ static int link_status_one(sd_rtnl *rtnl, struct udev *udev, const char *name) { return rtnl_log_create_error(r); r = sd_rtnl_call(rtnl, req, 0, &reply); - if (r < 0) { - log_error("Failed to query link: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to query link: %m"); r = sd_rtnl_message_link_get_ifindex(reply, &ifindex); if (r < 0) @@ -428,6 +582,10 @@ static int link_status_one(sd_rtnl *rtnl, struct udev *udev, const char *name) { if (mtu > 0) printf(" MTU: %u\n", mtu); + hwdb = udev_hwdb_new(udev); + + dump_gateways(rtnl, hwdb, " Gateway: ", ifindex); + dump_addresses(rtnl, " Address: ", ifindex); if (!strv_isempty(dns)) @@ -447,16 +605,12 @@ static int link_status(char **args, unsigned n) { int r; r = sd_rtnl_open(&rtnl, 0); - if (r < 0) { - log_error("Failed to connect to netlink: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); udev = udev_new(); - if (!udev) { - log_error("Failed to connect to udev: %m"); - return -errno; - } + if (!udev) + return log_error_errno(errno, "Failed to connect to udev: %m"); if (n <= 1 && !arg_all) { _cleanup_free_ char *operational_state = NULL; @@ -470,7 +624,7 @@ static int link_status(char **args, unsigned n) { printf(" State: %s%s%s\n", on_color_operational, strna(operational_state), off_color_operational); - c = local_addresses(rtnl, 0, &addresses); + c = local_addresses(rtnl, 0, AF_UNSPEC, &addresses); for (i = 0; i < c; i++) { _cleanup_free_ char *pretty = NULL; @@ -513,10 +667,8 @@ static int link_status(char **args, unsigned n) { return rtnl_log_create_error(r); r = sd_rtnl_call(rtnl, req, 0, &reply); - if (r < 0) { - log_error("Failed to enumerate links: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to enumerate links: %m"); c = decode_and_sort_links(reply, &links); if (c < 0) diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index ce85109d22..a85e8fa21e 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -117,33 +117,23 @@ int address_drop(Address *address, Link *link, r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_DELADDR, link->ifindex, address->family); - if (r < 0) { - log_error("Could not allocate RTM_DELADDR message: %s", - strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not allocate RTM_DELADDR message: %m"); r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen); - if (r < 0) { - log_error("Could not set prefixlen: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not set prefixlen: %m"); if (address->family == AF_INET) r = sd_rtnl_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in); else if (address->family == AF_INET6) r = sd_rtnl_message_append_in6_addr(req, IFA_LOCAL, &address->in_addr.in6); - if (r < 0) { - log_error("Could not append IFA_LOCAL attribute: %s", - strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not append IFA_LOCAL attribute: %m"); r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL); - if (r < 0) { - log_error("Could not send rtnetlink message: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not send rtnetlink message: %m"); link_ref(link); @@ -163,70 +153,47 @@ int address_update(Address *address, Link *link, r = sd_rtnl_message_new_addr_update(link->manager->rtnl, &req, link->ifindex, address->family); - if (r < 0) { - log_error("Could not allocate RTM_NEWADDR message: %s", - strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not allocate RTM_NEWADDR message: %m"); r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen); - if (r < 0) { - log_error("Could not set prefixlen: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not set prefixlen: %m"); r = sd_rtnl_message_addr_set_flags(req, IFA_F_PERMANENT); - if (r < 0) { - log_error("Could not set flags: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not set flags: %m"); r = sd_rtnl_message_addr_set_scope(req, address->scope); - if (r < 0) { - log_error("Could not set scope: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not set scope: %m"); if (address->family == AF_INET) r = sd_rtnl_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in); else if (address->family == AF_INET6) r = sd_rtnl_message_append_in6_addr(req, IFA_LOCAL, &address->in_addr.in6); - if (r < 0) { - log_error("Could not append IFA_LOCAL attribute: %s", - strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not append IFA_LOCAL attribute: %m"); if (address->family == AF_INET) { r = sd_rtnl_message_append_in_addr(req, IFA_BROADCAST, &address->broadcast); - if (r < 0) { - log_error("Could not append IFA_BROADCAST attribute: %s", - strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not append IFA_BROADCAST attribute: %m"); } if (address->label) { r = sd_rtnl_message_append_string(req, IFA_LABEL, address->label); - if (r < 0) { - log_error("Could not append IFA_LABEL attribute: %s", - strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not append IFA_LABEL attribute: %m"); } r = sd_rtnl_message_append_cache_info(req, IFA_CACHEINFO, &address->cinfo); - if (r < 0) { - log_error("Could not append IFA_CACHEINFO attribute: %s", - strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not append IFA_CACHEINFO attribute: %m"); r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL); - if (r < 0) { - log_error("Could not send rtnetlink message: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not send rtnetlink message: %m"); link_ref(link); @@ -251,11 +218,11 @@ static int address_acquire(Link *link, Address *original, Address **ret) { * Then let's acquire something more useful from the pool. */ r = manager_address_pool_acquire(link->manager, original->family, original->prefixlen, &in_addr); if (r < 0) { - log_error_link(link, "Failed to acquire address from pool: %s", strerror(-r)); + log_link_error(link, "Failed to acquire address from pool: %s", strerror(-r)); return r; } if (r == 0) { - log_error_link(link, "Couldn't find free address for interface, all taken."); + log_link_error(link, "Couldn't find free address for interface, all taken."); return -EBUSY; } @@ -312,83 +279,57 @@ int address_configure(Address *address, Link *link, r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_NEWADDR, link->ifindex, address->family); - if (r < 0) { - log_error("Could not allocate RTM_NEWADDR message: %s", - strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not allocate RTM_NEWADDR message: %m"); r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen); - if (r < 0) { - log_error("Could not set prefixlen: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not set prefixlen: %m"); r = sd_rtnl_message_addr_set_flags(req, IFA_F_PERMANENT); - if (r < 0) { - log_error("Could not set flags: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not set flags: %m"); r = sd_rtnl_message_addr_set_scope(req, address->scope); - if (r < 0) { - log_error("Could not set scope: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not set scope: %m"); if (address->family == AF_INET) r = sd_rtnl_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in); else if (address->family == AF_INET6) r = sd_rtnl_message_append_in6_addr(req, IFA_LOCAL, &address->in_addr.in6); - if (r < 0) { - log_error("Could not append IFA_LOCAL attribute: %s", - strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not append IFA_LOCAL attribute: %m"); if (!in_addr_is_null(address->family, &address->in_addr_peer)) { if (address->family == AF_INET) r = sd_rtnl_message_append_in_addr(req, IFA_ADDRESS, &address->in_addr_peer.in); else if (address->family == AF_INET6) r = sd_rtnl_message_append_in6_addr(req, IFA_ADDRESS, &address->in_addr_peer.in6); - if (r < 0) { - log_error("Could not append IFA_ADDRESS attribute: %s", - strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not append IFA_ADDRESS attribute: %m"); } else { if (address->family == AF_INET) { r = sd_rtnl_message_append_in_addr(req, IFA_BROADCAST, &address->broadcast); - if (r < 0) { - log_error("Could not append IFA_BROADCAST attribute: %s", - strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not append IFA_BROADCAST attribute: %m"); } } if (address->label) { r = sd_rtnl_message_append_string(req, IFA_LABEL, address->label); - if (r < 0) { - log_error("Could not append IFA_LABEL attribute: %s", - strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not append IFA_LABEL attribute: %m"); } r = sd_rtnl_message_append_cache_info(req, IFA_CACHEINFO, &address->cinfo); - if (r < 0) { - log_error("Could not append IFA_CACHEINFO attribute: %s", - strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not append IFA_CACHEINFO attribute: %m"); r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL); - if (r < 0) { - log_error("Could not send rtnetlink message: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not send rtnetlink message: %m"); link_ref(link); diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 63bfa86f99..1c2edc5b87 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -38,7 +38,7 @@ static int dhcp4_route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, r = sd_rtnl_message_get_errno(m); if (r < 0 && r != -EEXIST) { - log_error_link(link, "could not set DHCPv4 route: %s", + log_link_error(link, "could not set DHCPv4 route: %s", strerror(-r)); link_enter_failed(link); } @@ -61,7 +61,7 @@ static int link_set_dhcp_routes(Link *link) { r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway); if (r < 0 && r != -ENOENT) { - log_warning_link(link, + log_link_warning(link, "DHCP error: could not get gateway: %s", strerror(-r)); return r; @@ -73,7 +73,7 @@ static int link_set_dhcp_routes(Link *link) { r = sd_dhcp_lease_get_address(link->dhcp_lease, &address); if (r < 0) { - log_warning_link(link, + log_link_warning(link, "DHCP error: could not get address: %s", strerror(-r)); return r; @@ -81,7 +81,7 @@ static int link_set_dhcp_routes(Link *link) { r = route_new_dynamic(&route, RTPROT_DHCP); if (r < 0) { - log_error_link(link, + log_link_error(link, "Could not allocate route: %s", strerror(-r)); return r; @@ -89,7 +89,7 @@ static int link_set_dhcp_routes(Link *link) { r = route_new_dynamic(&route_gw, RTPROT_DHCP); if (r < 0) { - log_error_link(link, + log_link_error(link, "Could not allocate route: %s", strerror(-r)); return r; @@ -107,7 +107,7 @@ static int link_set_dhcp_routes(Link *link) { r = route_configure(route_gw, link, &dhcp4_route_handler); if (r < 0) { - log_warning_link(link, + log_link_warning(link, "could not set host route: %s", strerror(-r)); return r; @@ -122,7 +122,7 @@ static int link_set_dhcp_routes(Link *link) { r = route_configure(route, link, &dhcp4_route_handler); if (r < 0) { - log_warning_link(link, + log_link_warning(link, "could not set routes: %s", strerror(-r)); link_enter_failed(link); @@ -136,7 +136,7 @@ static int link_set_dhcp_routes(Link *link) { if (n == -ENOENT) return 0; if (n < 0) { - log_warning_link(link, + log_link_warning(link, "DHCP error: could not get routes: %s", strerror(-n)); @@ -148,7 +148,7 @@ static int link_set_dhcp_routes(Link *link) { r = route_new_dynamic(&route, RTPROT_DHCP); if (r < 0) { - log_error_link(link, "Could not allocate route: %s", + log_link_error(link, "Could not allocate route: %s", strerror(-r)); return r; } @@ -161,7 +161,7 @@ static int link_set_dhcp_routes(Link *link) { r = route_configure(route, link, &dhcp4_route_handler); if (r < 0) { - log_warning_link(link, + log_link_warning(link, "could not set host route: %s", strerror(-r)); return r; @@ -184,7 +184,7 @@ static int dhcp_lease_lost(Link *link) { assert(link); assert(link->dhcp_lease); - log_warning_link(link, "DHCP lease lost"); + log_link_warning(link, "DHCP lease lost"); if (link->network->dhcp_routes) { struct sd_dhcp_route *routes; @@ -258,7 +258,7 @@ static int dhcp_lease_lost(Link *link) { if (r >= 0 && link->original_mtu != mtu) { r = link_set_mtu(link, link->original_mtu); if (r < 0) { - log_warning_link(link, + log_link_warning(link, "DHCP error: could not reset MTU"); link_enter_failed(link); return r; @@ -273,7 +273,7 @@ static int dhcp_lease_lost(Link *link) { if (r >= 0 && hostname) { r = link_set_hostname(link, ""); if (r < 0) - log_error_link(link, + log_link_error(link, "Failed to reset transient hostname"); } } @@ -293,14 +293,11 @@ static int dhcp4_address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, r = sd_rtnl_message_get_errno(m); if (r < 0 && r != -EEXIST) { - log_error_link(link, "could not set DHCPv4 address: %s", + log_link_error(link, "could not set DHCPv4 address: %s", strerror(-r)); link_enter_failed(link); - } else if (r >= 0) { - /* calling handler directly so take a ref */ - link_ref(link); - link_get_address_handler(rtnl, m, link); - } + } else if (r >= 0) + link_rtnl_process_address(rtnl, m, link->manager); link_set_dhcp_routes(link); @@ -354,7 +351,7 @@ static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) { r = sd_dhcp_client_get_lease(client, &lease); if (r < 0) { - log_warning_link(link, "DHCP error: no lease %s", + log_link_warning(link, "DHCP error: no lease %s", strerror(-r)); return r; } @@ -365,14 +362,14 @@ static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) { r = sd_dhcp_lease_get_address(lease, &address); if (r < 0) { - log_warning_link(link, "DHCP error: no address: %s", + log_link_warning(link, "DHCP error: no address: %s", strerror(-r)); return r; } r = sd_dhcp_lease_get_netmask(lease, &netmask); if (r < 0) { - log_warning_link(link, "DHCP error: no netmask: %s", + log_link_warning(link, "DHCP error: no netmask: %s", strerror(-r)); return r; } @@ -381,7 +378,7 @@ static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) { r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime); if (r < 0) { - log_warning_link(link, + log_link_warning(link, "DHCP error: no lifetime: %s", strerror(-r)); return r; @@ -390,7 +387,7 @@ static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) { r = dhcp4_update_address(link, &address, &netmask, lifetime); if (r < 0) { - log_warning_link(link, "could not update IP address: %s", + log_link_warning(link, "could not update IP address: %s", strerror(-r)); link_enter_failed(link); return r; @@ -413,21 +410,21 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { r = sd_dhcp_client_get_lease(client, &lease); if (r < 0) { - log_warning_link(link, "DHCP error: no lease: %s", + log_link_warning(link, "DHCP error: no lease: %s", strerror(-r)); return r; } r = sd_dhcp_lease_get_address(lease, &address); if (r < 0) { - log_warning_link(link, "DHCP error: no address: %s", + log_link_warning(link, "DHCP error: no address: %s", strerror(-r)); return r; } r = sd_dhcp_lease_get_netmask(lease, &netmask); if (r < 0) { - log_warning_link(link, "DHCP error: no netmask: %s", + log_link_warning(link, "DHCP error: no netmask: %s", strerror(-r)); return r; } @@ -436,13 +433,13 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { r = sd_dhcp_lease_get_router(lease, &gateway); if (r < 0 && r != -ENOENT) { - log_warning_link(link, "DHCP error: could not get gateway: %s", + log_link_warning(link, "DHCP error: could not get gateway: %s", strerror(-r)); return r; } if (r >= 0) - log_struct_link(LOG_INFO, link, + log_link_struct(link, LOG_INFO, "MESSAGE=%-*s: DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u", IFNAMSIZ, link->ifname, @@ -457,7 +454,7 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { ADDRESS_FMT_VAL(gateway), NULL); else - log_struct_link(LOG_INFO, link, + log_link_struct(link, LOG_INFO, "MESSAGE=%-*s: DHCPv4 address %u.%u.%u.%u/%u", IFNAMSIZ, link->ifname, @@ -478,7 +475,7 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { if (r >= 0) { r = link_set_mtu(link, mtu); if (r < 0) - log_error_link(link, "Failed to set MTU " + log_link_error(link, "Failed to set MTU " "to %" PRIu16, mtu); } } @@ -490,7 +487,7 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { if (r >= 0) { r = link_set_hostname(link, hostname); if (r < 0) - log_error_link(link, + log_link_error(link, "Failed to set transient hostname to '%s'", hostname); } @@ -500,7 +497,7 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime); if (r < 0) { - log_warning_link(link, + log_link_warning(link, "DHCP error: no lifetime: %s", strerror(-r)); return r; @@ -509,7 +506,7 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { r = dhcp4_update_address(link, &address, &netmask, lifetime); if (r < 0) { - log_warning_link(link, "could not update IP address: %s", + log_link_warning(link, "could not update IP address: %s", strerror(-r)); link_enter_failed(link); return r; @@ -533,7 +530,7 @@ static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { case DHCP_EVENT_STOP: case DHCP_EVENT_IP_CHANGE: if (link->network->dhcp_critical) { - log_error_link(link, + log_link_error(link, "DHCPv4 connection considered system critical, ignoring request to reconfigure it."); return; } @@ -571,11 +568,11 @@ static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { break; default: if (event < 0) - log_warning_link(link, + log_link_warning(link, "DHCP error: client failed: %s", strerror(-event)); else - log_warning_link(link, + log_link_warning(link, "DHCP unknown event: %d", event); break; diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c new file mode 100644 index 0000000000..c31bd4ec30 --- /dev/null +++ b/src/network/networkd-dhcp6.c @@ -0,0 +1,210 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright (C) 2014 Intel Corporation. All rights reserved. + + 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 <netinet/ether.h> +#include <linux/if.h> + +#include "networkd-link.h" +#include "network-internal.h" + +#include "sd-icmp6-nd.h" +#include "sd-dhcp6-client.h" + +static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { + Link *link = userdata; + + assert(link); + assert(link->network); + assert(link->manager); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return; + + switch(event) { + case DHCP6_EVENT_STOP: + case DHCP6_EVENT_RESEND_EXPIRE: + case DHCP6_EVENT_RETRANS_MAX: + case DHCP6_EVENT_IP_ACQUIRE: + case DHCP6_EVENT_INFORMATION_REQUEST: + log_link_debug(link, "DHCPv6 event %d", event); + + break; + + default: + if (event < 0) + log_link_warning(link, "DHCPv6 error: %s", + strerror(-event)); + else + log_link_warning(link, "DHCPv6 unknown event: %d", + event); + return; + } +} + +static int dhcp6_configure(Link *link, int event) { + int r; + bool information_request; + + assert_return(link, -EINVAL); + + if (link->dhcp6_client) { + if (event != ICMP6_EVENT_ROUTER_ADVERTISMENT_MANAGED) + return 0; + + r = sd_dhcp6_client_get_information_request(link->dhcp6_client, + &information_request); + if (r < 0) { + log_link_warning(link, "Could not get DHCPv6 Information request setting"); + link->dhcp6_client = + sd_dhcp6_client_unref(link->dhcp6_client); + return r; + } + + if (!information_request) + return r; + + r = sd_dhcp6_client_set_information_request(link->dhcp6_client, + false); + if (r < 0) { + log_link_warning(link, "Could not unset DHCPv6 Information request"); + link->dhcp6_client = + sd_dhcp6_client_unref(link->dhcp6_client); + return r; + } + + r = sd_dhcp6_client_start(link->dhcp6_client); + if (r < 0) { + log_link_warning(link, "Could not restart DHCPv6 after enabling Information request"); + link->dhcp6_client = + sd_dhcp6_client_unref(link->dhcp6_client); + return r; + } + + return r; + } + + r = sd_dhcp6_client_new(&link->dhcp6_client); + if (r < 0) + return r; + + r = sd_dhcp6_client_attach_event(link->dhcp6_client, NULL, 0); + if (r < 0) { + link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client); + return r; + } + + r = sd_dhcp6_client_set_mac(link->dhcp6_client, + (const uint8_t *) &link->mac, + sizeof (link->mac), ARPHRD_ETHER); + if (r < 0) { + link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client); + return r; + } + + r = sd_dhcp6_client_set_index(link->dhcp6_client, link->ifindex); + if (r < 0) { + link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client); + return r; + } + + r = sd_dhcp6_client_set_callback(link->dhcp6_client, dhcp6_handler, + link); + if (r < 0) { + link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client); + return r; + } + + if (event == ICMP6_EVENT_ROUTER_ADVERTISMENT_OTHER) { + r = sd_dhcp6_client_set_information_request(link->dhcp6_client, + true); + if (r < 0) { + link->dhcp6_client = + sd_dhcp6_client_unref(link->dhcp6_client); + return r; + } + } + + r = sd_dhcp6_client_start(link->dhcp6_client); + if (r < 0) + link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client); + + return r; +} + +static void icmp6_router_handler(sd_icmp6_nd *nd, int event, void *userdata) { + Link *link = userdata; + + assert(link); + assert(link->network); + assert(link->manager); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return; + + switch(event) { + case ICMP6_EVENT_ROUTER_ADVERTISMENT_NONE: + return; + + case ICMP6_EVENT_ROUTER_ADVERTISMENT_TIMEOUT: + case ICMP6_EVENT_ROUTER_ADVERTISMENT_OTHER: + case ICMP6_EVENT_ROUTER_ADVERTISMENT_MANAGED: + break; + + default: + if (event < 0) + log_link_warning(link, "ICMPv6 error: %s", + strerror(-event)); + else + log_link_warning(link, "ICMPv6 unknown event: %d", + event); + + return; + } + + dhcp6_configure(link, event); +} + +int icmp6_configure(Link *link) { + int r; + + assert_return(link, -EINVAL); + + r = sd_icmp6_nd_new(&link->icmp6_router_discovery); + if (r < 0) + return r; + + r = sd_icmp6_nd_attach_event(link->icmp6_router_discovery, NULL, 0); + if (r < 0) + return r; + + r = sd_icmp6_nd_set_mac(link->icmp6_router_discovery, &link->mac); + if (r < 0) + return r; + + r = sd_icmp6_nd_set_index(link->icmp6_router_discovery, link->ifindex); + if (r < 0) + return r; + + r = sd_icmp6_nd_set_callback(link->icmp6_router_discovery, + icmp6_router_handler, link); + + return r; +} diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index 5467bc372e..339bf4d190 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -40,12 +40,11 @@ static int ipv4ll_address_lost(Link *link) { if (r < 0) return 0; - log_debug_link(link, "IPv4 link-local release %u.%u.%u.%u", - ADDRESS_FMT_VAL(addr)); + log_link_debug(link, "IPv4 link-local release %u.%u.%u.%u", ADDRESS_FMT_VAL(addr)); r = address_new_dynamic(&address); if (r < 0) { - log_error_link(link, "Could not allocate address: %s", strerror(-r)); + log_link_error(link, "Could not allocate address: %s", strerror(-r)); return r; } @@ -58,7 +57,7 @@ static int ipv4ll_address_lost(Link *link) { r = route_new_dynamic(&route, RTPROT_UNSPEC); if (r < 0) { - log_error_link(link, "Could not allocate route: %s", + log_link_error(link, "Could not allocate route: %s", strerror(-r)); return r; } @@ -83,7 +82,7 @@ static int ipv4ll_route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdat r = sd_rtnl_message_get_errno(m); if (r < 0 && r != -EEXIST) { - log_error_link(link, "could not set ipv4ll route: %s", strerror(-r)); + log_link_error(link, "could not set ipv4ll route: %s", strerror(-r)); link_enter_failed(link); } @@ -104,13 +103,10 @@ static int ipv4ll_address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userd r = sd_rtnl_message_get_errno(m); if (r < 0 && r != -EEXIST) { - log_error_link(link, "could not set ipv4ll address: %s", strerror(-r)); + log_link_error(link, "could not set ipv4ll address: %s", strerror(-r)); link_enter_failed(link); - } else if (r >= 0) { - /* calling handler directly so take a ref */ - link_ref(link); - link_get_address_handler(rtnl, m, link); - } + } else if (r >= 0) + link_rtnl_process_address(rtnl, m, link->manager); link->ipv4ll_address = true; @@ -135,7 +131,7 @@ static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) { else if (r < 0) return r; - log_debug_link(link, "IPv4 link-local claim %u.%u.%u.%u", + log_link_debug(link, "IPv4 link-local claim %u.%u.%u.%u", ADDRESS_FMT_VAL(address)); r = address_new_dynamic(&ll_addr); @@ -200,9 +196,9 @@ static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata){ break; default: if (event < 0) - log_warning_link(link, "IPv4 link-local error: %s", strerror(-event)); + log_link_warning(link, "IPv4 link-local error: %s", strerror(-event)); else - log_warning_link(link, "IPv4 link-local unknown event: %d", event); + log_link_warning(link, "IPv4 link-local unknown event: %d", event); break; } } diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 26ef0feaae..08f724e127 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -89,7 +89,7 @@ static int link_update_flags(Link *link, sd_rtnl_message *m) { r = sd_rtnl_message_link_get_flags(m, &flags); if (r < 0) { - log_warning_link(link, "Could not get link flags"); + log_link_warning(link, "Could not get link flags"); return r; } @@ -103,7 +103,7 @@ static int link_update_flags(Link *link, sd_rtnl_message *m) { return 0; if (link->flags != flags) { - log_debug_link(link, "flags change:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + log_link_debug(link, "flags change:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", FLAG_STRING("LOOPBACK", IFF_LOOPBACK, link->flags, flags), FLAG_STRING("MASTER", IFF_MASTER, link->flags, flags), FLAG_STRING("SLAVE", IFF_SLAVE, link->flags, flags), @@ -136,12 +136,12 @@ static int link_update_flags(Link *link, sd_rtnl_message *m) { /* link flags are currently at most 18 bits, let's align to * printing 20 */ if (unknown_flags_added) - log_debug_link(link, + log_link_debug(link, "unknown link flags gained: %#.5x (ignoring)", unknown_flags_added); if (unknown_flags_removed) - log_debug_link(link, + log_link_debug(link, "unknown link flags lost: %#.5x (ignoring)", unknown_flags_removed); } @@ -194,7 +194,7 @@ static int link_new(Manager *manager, sd_rtnl_message *message, Link **ret) { r = sd_rtnl_message_read_ether_addr(message, IFLA_ADDRESS, &link->mac); if (r < 0) - log_debug_link(link, "MAC address not found for new device, continuing without"); + log_link_debug(link, "MAC address not found for new device, continuing without"); r = asprintf(&link->state_file, "/run/systemd/netif/links/%d", link->ifindex); @@ -299,7 +299,7 @@ void link_drop(Link *link) { link->state = LINK_STATE_LINGER; - log_debug_link(link, "link removed"); + log_link_debug(link, "link removed"); link_unref(link); @@ -309,7 +309,7 @@ void link_drop(Link *link) { static void link_enter_unmanaged(Link *link) { assert(link); - log_debug_link(link, "unmanaged"); + log_link_debug(link, "unmanaged"); link->state = LINK_STATE_UNMANAGED; @@ -329,7 +329,7 @@ static int link_stop_clients(Link *link) { if (link->dhcp_client) { k = sd_dhcp_client_stop(link->dhcp_client); if (k < 0) { - log_warning_link(link, "Could not stop DHCPv4 client: %s", + log_link_warning(link, "Could not stop DHCPv4 client: %s", strerror(-r)); r = k; } @@ -338,7 +338,7 @@ static int link_stop_clients(Link *link) { if (link->ipv4ll) { k = sd_ipv4ll_stop(link->ipv4ll); if (k < 0) { - log_warning_link(link, "Could not stop IPv4 link-local: %s", + log_link_warning(link, "Could not stop IPv4 link-local: %s", strerror(-r)); r = k; } @@ -349,7 +349,7 @@ static int link_stop_clients(Link *link) { if (link->dhcp6_client) { k = sd_dhcp6_client_stop(link->dhcp6_client); if (k < 0) { - log_warning_link(link, "Could not stop DHCPv6 client: %s", + log_link_warning(link, "Could not stop DHCPv6 client: %s", strerror(-r)); r = k; } @@ -357,7 +357,7 @@ static int link_stop_clients(Link *link) { k = sd_icmp6_nd_stop(link->icmp6_router_discovery); if (k < 0) { - log_warning_link(link, + log_link_warning(link, "Could not stop ICMPv6 router discovery: %s", strerror(-r)); r = k; @@ -373,7 +373,7 @@ void link_enter_failed(Link *link) { if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return; - log_warning_link(link, "failed"); + log_link_warning(link, "failed"); link->state = LINK_STATE_FAILED; @@ -425,13 +425,13 @@ static int link_enter_configured(Link *link) { address = link_find_dhcp_server_address(link); if (!address) { - log_warning_link(link, + log_link_warning(link, "Failed to find suitable address for DHCPv4 server instance."); link_enter_failed(link); return 0; } - log_debug_link(link, "offering DHCPv4 leases"); + log_link_debug(link, "offering DHCPv4 leases"); r = sd_dhcp_server_set_address(link->dhcp_server, &address->in_addr.in, @@ -460,7 +460,7 @@ static int link_enter_configured(Link *link) { r = sd_dhcp_server_start(link->dhcp_server); if (r < 0) { - log_warning_link(link, "could not start DHCPv4 server " + log_link_warning(link, "could not start DHCPv4 server " "instance: %s", strerror(-r)); link_enter_failed(link); @@ -469,7 +469,7 @@ static int link_enter_configured(Link *link) { } } - log_info_link(link, "link configured"); + log_link_info(link, "link configured"); link->state = LINK_STATE_CONFIGURED; @@ -515,15 +515,10 @@ static int route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { r = sd_rtnl_message_get_errno(m); if (r < 0 && r != -EEXIST) - log_struct_link(LOG_WARNING, link, - "MESSAGE=%-*s: could not set route: %s", - IFNAMSIZ, - link->ifname, strerror(-r), - "ERRNO=%d", -r, - NULL); + log_link_warning_errno(link, -r, "%-*s: could not set route: %m", IFNAMSIZ, link->ifname); if (link->link_messages == 0) { - log_debug_link(link, "routes set"); + log_link_debug(link, "routes set"); link->static_configured = true; link_client_handler(link); } @@ -544,7 +539,7 @@ static int link_enter_set_routes(Link *link) { LIST_FOREACH(routes, rt, link->network->static_routes) { r = route_configure(rt, link, &route_handler); if (r < 0) { - log_warning_link(link, + log_link_warning(link, "could not set routes: %s", strerror(-r)); link_enter_failed(link); @@ -558,7 +553,7 @@ static int link_enter_set_routes(Link *link) { link->static_configured = true; link_client_handler(link); } else - log_debug_link(link, "setting routes"); + log_link_debug(link, "setting routes"); return 0; } @@ -576,38 +571,7 @@ int link_route_drop_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { r = sd_rtnl_message_get_errno(m); if (r < 0 && r != -ESRCH) - log_struct_link(LOG_WARNING, link, - "MESSAGE=%-*s: could not drop route: %s", - IFNAMSIZ, - link->ifname, strerror(-r), - "ERRNO=%d", -r, - NULL); - - return 1; -} - -int link_get_address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { - _cleanup_link_unref_ Link *link = userdata; - int r; - - assert(rtnl); - assert(m); - assert(link); - assert(link->manager); - - for (; m; m = sd_rtnl_message_next(m)) { - r = sd_rtnl_message_get_errno(m); - if (r < 0) { - log_debug_link(link, "getting address failed: %s", - strerror(-r)); - continue; - } - - r = link_rtnl_process_address(rtnl, m, link->manager); - if (r < 0) - log_warning_link(link, "could not process address: %s", - strerror(-r)); - } + log_link_warning_errno(link, -r, "%-*s: could not drop route: %m", IFNAMSIZ, link->ifname); return 1; } @@ -631,20 +595,12 @@ static int address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { r = sd_rtnl_message_get_errno(m); if (r < 0 && r != -EEXIST) - log_struct_link(LOG_WARNING, link, - "MESSAGE=%-*s: could not set address: %s", - IFNAMSIZ, - link->ifname, strerror(-r), - "ERRNO=%d", -r, - NULL); - else if (r >= 0) { - /* calling handler directly so take a ref */ - link_ref(link); - link_get_address_handler(rtnl, m, link); - } + log_link_warning_errno(link, -r, "%-*s: could not set address: %m", IFNAMSIZ, link->ifname); + else if (r >= 0) + link_rtnl_process_address(rtnl, m, link->manager); if (link->link_messages == 0) { - log_debug_link(link, "addresses set"); + log_link_debug(link, "addresses set"); link_enter_set_routes(link); } @@ -664,7 +620,7 @@ static int link_enter_set_addresses(Link *link) { LIST_FOREACH(addresses, ad, link->network->static_addresses) { r = address_configure(ad, link, &address_handler); if (r < 0) { - log_warning_link(link, + log_link_warning(link, "could not set addresses: %s", strerror(-r)); link_enter_failed(link); @@ -677,7 +633,7 @@ static int link_enter_set_addresses(Link *link) { if (link->link_messages == 0) { link_enter_set_routes(link); } else - log_debug_link(link, "setting addresses"); + log_link_debug(link, "setting addresses"); return 0; } @@ -695,14 +651,30 @@ int link_address_drop_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) r = sd_rtnl_message_get_errno(m); if (r < 0 && r != -EADDRNOTAVAIL) - log_struct_link(LOG_WARNING, link, - "MESSAGE=%-*s: could not drop address: %s", + log_link_warning_errno(link, -r, "%-*s: could not drop address: %m", IFNAMSIZ, link->ifname); + + return 1; +} + +static int link_set_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { + _cleanup_link_unref_ Link *link = userdata; + int r; + + log_link_debug(link, "set link"); + + r = sd_rtnl_message_get_errno(m); + if (r < 0 && r != -EEXIST) { + log_link_struct(link, LOG_ERR, + "MESSAGE=%-*s: could not join netdev: %s", IFNAMSIZ, link->ifname, strerror(-r), "ERRNO=%d", -r, NULL); + link_enter_failed(link); + return 1; + } - return 1; + return 0; } static int set_hostname_handler(sd_bus *bus, sd_bus_message *m, void *userdata, @@ -717,7 +689,7 @@ static int set_hostname_handler(sd_bus *bus, sd_bus_message *m, void *userdata, r = sd_bus_message_get_errno(m); if (r > 0) - log_warning_link(link, "Could not set hostname: %s", + log_link_warning(link, "Could not set hostname: %s", strerror(r)); return 1; @@ -731,11 +703,11 @@ int link_set_hostname(Link *link, const char *hostname) { assert(link->manager); assert(hostname); - log_debug_link(link, "Setting transient hostname: '%s'", hostname); + log_link_debug(link, "Setting transient hostname: '%s'", hostname); if (!link->manager->bus) { /* TODO: replace by assert when we can rely on kdbus */ - log_info_link(link, + log_link_info(link, "Not connected to system bus, ignoring transient hostname."); return 0; } @@ -757,7 +729,7 @@ int link_set_hostname(Link *link, const char *hostname) { r = sd_bus_call_async(link->manager->bus, NULL, m, set_hostname_handler, link, 0); if (r < 0) { - log_error_link(link, "Could not set transient hostname: %s", + log_link_error(link, "Could not set transient hostname: %s", strerror(-r)); return r; } @@ -780,11 +752,7 @@ static int set_mtu_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { r = sd_rtnl_message_get_errno(m); if (r < 0) - log_struct_link(LOG_WARNING, link, - "MESSAGE=%-*s: could not set MTU: %s", - IFNAMSIZ, link->ifname, strerror(-r), - "ERRNO=%d", -r, - NULL); + log_link_warning_errno(link, -r, "%-*s: could not set MTU: %m", IFNAMSIZ, link->ifname); return 1; } @@ -797,25 +765,25 @@ int link_set_mtu(Link *link, uint32_t mtu) { assert(link->manager); assert(link->manager->rtnl); - log_debug_link(link, "setting MTU: %" PRIu32, mtu); + log_link_debug(link, "setting MTU: %" PRIu32, mtu); r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex); if (r < 0) { - log_error_link(link, "Could not allocate RTM_SETLINK message"); + log_link_error(link, "Could not allocate RTM_SETLINK message"); return r; } r = sd_rtnl_message_append_u32(req, IFLA_MTU, mtu); if (r < 0) { - log_error_link(link, "Could not append MTU: %s", strerror(-r)); + log_link_error(link, "Could not append MTU: %s", strerror(-r)); return r; } r = sd_rtnl_call_async(link->manager->rtnl, req, set_mtu_handler, link, 0, NULL); if (r < 0) { - log_error_link(link, + log_link_error(link, "Could not send rtnetlink message: %s", strerror(-r)); return r; @@ -826,104 +794,67 @@ int link_set_mtu(Link *link, uint32_t mtu) { return 0; } -static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) { - Link *link = userdata; - - assert(link); - assert(link->network); - assert(link->manager); - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return; - - switch(event) { - case DHCP6_EVENT_STOP: - case DHCP6_EVENT_RESEND_EXPIRE: - case DHCP6_EVENT_RETRANS_MAX: - case DHCP6_EVENT_IP_ACQUIRE: - log_debug_link(link, "DHCPv6 event %d", event); - - break; - - default: - if (event < 0) - log_warning_link(link, "DHCPv6 error: %s", - strerror(-event)); - else - log_warning_link(link, "DHCPv6 unknown event: %d", - event); - return; - } -} - -static void icmp6_router_handler(sd_icmp6_nd *nd, int event, void *userdata) { - Link *link = userdata; +static int link_set_bridge(Link *link) { + _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL; int r; assert(link); assert(link->network); - assert(link->manager); - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return; - - switch(event) { - case ICMP6_EVENT_ROUTER_ADVERTISMENT_NONE: - case ICMP6_EVENT_ROUTER_ADVERTISMENT_OTHER: - return; - - case ICMP6_EVENT_ROUTER_ADVERTISMENT_TIMEOUT: - case ICMP6_EVENT_ROUTER_ADVERTISMENT_MANAGED: - break; - default: - if (event < 0) - log_warning_link(link, "ICMPv6 error: %s", - strerror(-event)); - else - log_warning_link(link, "ICMPv6 unknown event: %d", - event); + if(link->network->cost == 0) + return 0; - return; + r = sd_rtnl_message_new_link(link->manager->rtnl, &req, + RTM_SETLINK, link->ifindex); + if (r < 0) { + log_link_error(link, "Could not allocate RTM_SETLINK message"); + return r; } - if (link->dhcp6_client) - return; - - r = sd_dhcp6_client_new(&link->dhcp6_client); - if (r < 0) - return; - - r = sd_dhcp6_client_attach_event(link->dhcp6_client, NULL, 0); + r = sd_rtnl_message_link_set_family(req, PF_BRIDGE); if (r < 0) { - link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client); - return; + log_link_error(link, + "Could not set message family %s", strerror(-r)); + return r; } - r = sd_dhcp6_client_set_mac(link->dhcp6_client, - (const uint8_t *) &link->mac, - sizeof (link->mac), ARPHRD_ETHER); + r = sd_rtnl_message_open_container(req, IFLA_PROTINFO); if (r < 0) { - link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client); - return; + log_link_error(link, + "Could not append IFLA_PROTINFO attribute: %s", + strerror(-r)); + return r; + } + + if(link->network->cost != 0) { + r = sd_rtnl_message_append_u32(req, IFLA_BRPORT_COST, link->network->cost); + if (r < 0) { + log_link_error(link, + "Could not append IFLA_BRPORT_COST attribute: %s", + strerror(-r)); + return r; + } } - r = sd_dhcp6_client_set_index(link->dhcp6_client, link->ifindex); + r = sd_rtnl_message_close_container(req); if (r < 0) { - link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client); - return; + log_link_error(link, + "Could not append IFLA_LINKINFO attribute: %s", + strerror(-r)); + return r; } - r = sd_dhcp6_client_set_callback(link->dhcp6_client, dhcp6_handler, - link); + r = sd_rtnl_call_async(link->manager->rtnl, req, link_set_handler, link, 0, NULL); if (r < 0) { - link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client); - return; + log_link_error(link, + "Could not send rtnetlink message: %s", + strerror(-r)); + return r; } - r = sd_dhcp6_client_start(link->dhcp6_client); - if (r < 0) - link->dhcp6_client = sd_dhcp6_client_unref(link->dhcp6_client); + link_ref(link); + + return r; } static int link_acquire_conf(Link *link) { @@ -937,11 +868,11 @@ static int link_acquire_conf(Link *link) { if (link_ipv4ll_enabled(link)) { assert(link->ipv4ll); - log_debug_link(link, "acquiring IPv4 link-local address"); + log_link_debug(link, "acquiring IPv4 link-local address"); r = sd_ipv4ll_start(link->ipv4ll); if (r < 0) { - log_warning_link(link, "could not acquire IPv4 " + log_link_warning(link, "could not acquire IPv4 " "link-local address"); return r; } @@ -950,11 +881,11 @@ static int link_acquire_conf(Link *link) { if (link_dhcp4_enabled(link)) { assert(link->dhcp_client); - log_debug_link(link, "acquiring DHCPv4 lease"); + log_link_debug(link, "acquiring DHCPv4 lease"); r = sd_dhcp_client_start(link->dhcp_client); if (r < 0) { - log_warning_link(link, "could not acquire DHCPv4 " + log_link_warning(link, "could not acquire DHCPv4 " "lease"); return r; } @@ -963,11 +894,11 @@ static int link_acquire_conf(Link *link) { if (link_dhcp6_enabled(link)) { assert(link->icmp6_router_discovery); - log_debug_link(link, "discovering IPv6 routers"); + log_link_debug(link, "discovering IPv6 routers"); r = sd_icmp6_router_solicitation_start(link->icmp6_router_discovery); if (r < 0) { - log_warning_link(link, + log_link_warning(link, "could not start IPv6 router discovery"); return r; } @@ -1003,12 +934,7 @@ static int link_up_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) { if (r < 0) { /* we warn but don't fail the link, as it may be brought up later */ - log_struct_link(LOG_WARNING, link, - "MESSAGE=%-*s: could not bring up interface: %s", - IFNAMSIZ, - link->ifname, strerror(-r), - "ERRNO=%d", -r, - NULL); + log_link_warning_errno(link, -r, "%-*s: could not bring up interface: %m", IFNAMSIZ, link->ifname); } return 1; @@ -1019,29 +945,46 @@ static int link_up(Link *link) { int r; assert(link); + assert(link->network); assert(link->manager); assert(link->manager->rtnl); - log_debug_link(link, "bringing link up"); + log_link_debug(link, "bringing link up"); r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex); if (r < 0) { - log_error_link(link, "Could not allocate RTM_SETLINK message"); + log_link_error(link, "Could not allocate RTM_SETLINK message"); return r; } r = sd_rtnl_message_link_set_flags(req, IFF_UP, IFF_UP); if (r < 0) { - log_error_link(link, "Could not set link flags: %s", + log_link_error(link, "Could not set link flags: %s", strerror(-r)); return r; } + if (link->network->mac) { + r = sd_rtnl_message_append_ether_addr(req, IFLA_ADDRESS, link->network->mac); + if (r < 0) { + log_link_error(link, "Could not set MAC address: %s", strerror(-r)); + return r; + } + } + + if (link->network->mtu) { + r = sd_rtnl_message_append_u32(req, IFLA_MTU, link->network->mtu); + if (r < 0) { + log_link_error(link, "Could not set MTU: %s", strerror(-r)); + return r; + } + } + r = sd_rtnl_call_async(link->manager->rtnl, req, link_up_handler, link, 0, NULL); if (r < 0) { - log_error_link(link, + log_link_error(link, "Could not send rtnetlink message: %s", strerror(-r)); return r; @@ -1066,6 +1009,15 @@ static int link_joined(Link *link) { } } + if(link->network->bridge) { + r = link_set_bridge(link); + if (r < 0) { + log_link_error(link, + "Could not set bridge message: %s", + strerror(-r)); + } + } + return link_enter_set_addresses(link); } @@ -1084,16 +1036,11 @@ static int netdev_join_handler(sd_rtnl *rtnl, sd_rtnl_message *m, r = sd_rtnl_message_get_errno(m); if (r < 0 && r != -EEXIST) { - log_struct_link(LOG_ERR, link, - "MESSAGE=%-*s: could not join netdev: %s", - IFNAMSIZ, - link->ifname, strerror(-r), - "ERRNO=%d", -r, - NULL); + log_link_error_errno(link, -r, "%-*s: could not join netdev: %m", IFNAMSIZ, link->ifname); link_enter_failed(link); return 1; } else - log_debug_link(link, "joined netdev"); + log_link_debug(link, "joined netdev"); if (link->enslaving <= 0) link_joined(link); @@ -1120,7 +1067,7 @@ static int link_enter_join_netdev(Link *link) { return link_joined(link); if (link->network->bond) { - log_struct_link(LOG_DEBUG, link, + log_link_struct(link, LOG_DEBUG, "MESSAGE=%-*s: enslaving by '%s'", IFNAMSIZ, link->ifname, link->network->bond->ifname, @@ -1129,7 +1076,7 @@ static int link_enter_join_netdev(Link *link) { r = netdev_join(link->network->bond, link, &netdev_join_handler); if (r < 0) { - log_struct_link(LOG_WARNING, link, + log_link_struct(link, LOG_WARNING, "MESSAGE=%-*s: could not join netdev '%s': %s", IFNAMSIZ, link->ifname, link->network->bond->ifname, @@ -1144,7 +1091,7 @@ static int link_enter_join_netdev(Link *link) { } if (link->network->bridge) { - log_struct_link(LOG_DEBUG, link, + log_link_struct(link, LOG_DEBUG, "MESSAGE=%-*s: enslaving by '%s'", IFNAMSIZ, link->ifname, link->network->bridge->ifname, @@ -1154,7 +1101,7 @@ static int link_enter_join_netdev(Link *link) { r = netdev_join(link->network->bridge, link, &netdev_join_handler); if (r < 0) { - log_struct_link(LOG_WARNING, link, + log_link_struct(link, LOG_WARNING, "MESSAGE=%-*s: could not join netdev '%s': %s", IFNAMSIZ, link->ifname, link->network->bridge->ifname, @@ -1169,7 +1116,7 @@ static int link_enter_join_netdev(Link *link) { } HASHMAP_FOREACH(netdev, link->network->stacked_netdevs, i) { - log_struct_link(LOG_DEBUG, link, + log_link_struct(link, LOG_DEBUG, "MESSAGE=%-*s: enslaving by '%s'", IFNAMSIZ, link->ifname, netdev->ifname, NETDEVIF(netdev), @@ -1177,7 +1124,7 @@ static int link_enter_join_netdev(Link *link) { r = netdev_join(netdev, link, &netdev_join_handler); if (r < 0) { - log_struct_link(LOG_WARNING, link, + log_link_struct(link, LOG_WARNING, "MESSAGE=%-*s: could not join netdev '%s': %s", IFNAMSIZ, link->ifname, netdev->ifname, @@ -1223,27 +1170,7 @@ static int link_configure(Link *link) { } if (link_dhcp6_enabled(link)) { - r = sd_icmp6_nd_new(&link->icmp6_router_discovery); - if (r < 0) - return r; - - r = sd_icmp6_nd_attach_event(link->icmp6_router_discovery, - NULL, 0); - if (r < 0) - return r; - - r = sd_icmp6_nd_set_mac(link->icmp6_router_discovery, - &link->mac); - if (r < 0) - return r; - - r = sd_icmp6_nd_set_index(link->icmp6_router_discovery, - link->ifindex); - if (r < 0) - return r; - - r = sd_icmp6_nd_set_callback(link->icmp6_router_discovery, - icmp6_router_handler, link); + r = icmp6_configure(link); if (r < 0) return r; } @@ -1270,7 +1197,7 @@ static int link_initialized_and_synced(sd_rtnl *rtnl, sd_rtnl_message *m, if (link->state != LINK_STATE_PENDING) return 1; - log_debug_link(link, "link state is up-to-date"); + log_link_debug(link, "link state is up-to-date"); r = network_get(link->manager, link->udev_device, link->ifname, &link->mac, &network); @@ -1282,13 +1209,13 @@ static int link_initialized_and_synced(sd_rtnl *rtnl, sd_rtnl_message *m, if (link->flags & IFF_LOOPBACK) { if (network->ipv4ll) - log_debug_link(link, "ignoring IPv4LL for loopback link"); + log_link_debug(link, "ignoring IPv4LL for loopback link"); if (network->dhcp != DHCP_SUPPORT_NONE) - log_debug_link(link, "ignoring DHCP clients for loopback link"); + log_link_debug(link, "ignoring DHCP clients for loopback link"); if (network->dhcp_server) - log_debug_link(link, "ignoring DHCP server for loopback link"); + log_link_debug(link, "ignoring DHCP server for loopback link"); } r = network_apply(link->manager, network, link); @@ -1317,7 +1244,7 @@ int link_initialized(Link *link, struct udev_device *device) { if (link->udev_device) return 0; - log_debug_link(link, "udev initialized link"); + log_link_debug(link, "udev initialized link"); link->udev_device = udev_device_ref(device); @@ -1341,8 +1268,7 @@ int link_initialized(Link *link, struct udev_device *device) { return 0; } -int link_rtnl_process_address(sd_rtnl *rtnl, sd_rtnl_message *message, - void *userdata) { +int link_rtnl_process_address(sd_rtnl *rtnl, sd_rtnl_message *message, void *userdata) { Manager *m = userdata; Link *link = NULL; uint16_t type; @@ -1358,6 +1284,14 @@ int link_rtnl_process_address(sd_rtnl *rtnl, sd_rtnl_message *message, assert(message); assert(m); + if (sd_rtnl_message_is_error(message)) { + r = sd_rtnl_message_get_errno(message); + if (r < 0) + log_warning_errno(r, "rtnl: failed to receive address: %m"); + + return 0; + } + r = sd_rtnl_message_get_type(message, &type); if (r < 0) { log_warning("rtnl: could not get message type"); @@ -1365,13 +1299,16 @@ int link_rtnl_process_address(sd_rtnl *rtnl, sd_rtnl_message *message, } r = sd_rtnl_message_addr_get_ifindex(message, &ifindex); - if (r < 0 || ifindex <= 0) { - log_warning("rtnl: received address message without valid ifindex, ignoring"); + if (r < 0) { + log_warning_errno(r, "rtnl: could not get ifindex: %m"); + return 0; + } else if (ifindex <= 0) { + log_warning("rtnl: received address message with invalid ifindex: %d", ifindex); return 0; } else { r = link_get(m, ifindex, &link); if (r < 0 || !link) { - log_warning("rtnl: received address for a nonexistent link (%d), ignoring", ifindex); + log_warning("rtnl: received address for nonexistent link (%d), ignoring", ifindex); return 0; } } @@ -1382,28 +1319,28 @@ int link_rtnl_process_address(sd_rtnl *rtnl, sd_rtnl_message *message, r = sd_rtnl_message_addr_get_family(message, &address->family); if (r < 0 || !IN_SET(address->family, AF_INET, AF_INET6)) { - log_warning_link(link, + log_link_warning(link, "rtnl: received address with invalid family, ignoring"); return 0; } r = sd_rtnl_message_addr_get_prefixlen(message, &address->prefixlen); if (r < 0) { - log_warning_link(link, + log_link_warning(link, "rtnl: received address with invalid prefixlen, ignoring"); return 0; } r = sd_rtnl_message_addr_get_scope(message, &address->scope); if (r < 0) { - log_warning_link(link, + log_link_warning(link, "rtnl: received address with invalid scope, ignoring"); return 0; } r = sd_rtnl_message_addr_get_flags(message, &address->flags); if (r < 0) { - log_warning_link(link, + log_link_warning(link, "rtnl: received address with invalid flags, ignoring"); return 0; } @@ -1413,7 +1350,7 @@ int link_rtnl_process_address(sd_rtnl *rtnl, sd_rtnl_message *message, r = sd_rtnl_message_read_in_addr(message, IFA_LOCAL, &address->in_addr.in); if (r < 0) { - log_warning_link(link, + log_link_warning(link, "rtnl: received address without valid address, ignoring"); return 0; } @@ -1424,7 +1361,7 @@ int link_rtnl_process_address(sd_rtnl *rtnl, sd_rtnl_message *message, r = sd_rtnl_message_read_in6_addr(message, IFA_ADDRESS, &address->in_addr.in6); if (r < 0) { - log_warning_link(link, + log_link_warning(link, "rtnl: received address without valid address, ignoring"); return 0; } @@ -1437,7 +1374,7 @@ int link_rtnl_process_address(sd_rtnl *rtnl, sd_rtnl_message *message, if (!inet_ntop(address->family, &address->in_addr, buf, INET6_ADDRSTRLEN)) { - log_warning_link(link, "could not print address"); + log_link_warning(link, "could not print address"); return 0; } @@ -1467,10 +1404,10 @@ int link_rtnl_process_address(sd_rtnl *rtnl, sd_rtnl_message *message, switch (type) { case RTM_NEWADDR: if (!address_dropped) - log_debug_link(link, "added address: %s/%u (valid for %s)", + log_link_debug(link, "added address: %s/%u (valid for %s)", buf, address->prefixlen, valid_str); else - log_debug_link(link, "updated address: %s/%u (valid for %s)", + log_link_debug(link, "updated address: %s/%u (valid for %s)", buf, address->prefixlen, valid_str); LIST_PREPEND(addresses, link->addresses, address); @@ -1481,12 +1418,12 @@ int link_rtnl_process_address(sd_rtnl *rtnl, sd_rtnl_message *message, break; case RTM_DELADDR: if (address_dropped) { - log_debug_link(link, "removed address: %s/%u (valid for %s)", + log_link_debug(link, "removed address: %s/%u (valid for %s)", buf, address->prefixlen, valid_str); link_save(link); } else - log_warning_link(link, + log_link_warning(link, "removing non-existent address: %s/%u (valid for %s)", buf, address->prefixlen, valid_str); @@ -1500,7 +1437,6 @@ int link_rtnl_process_address(sd_rtnl *rtnl, sd_rtnl_message *message, int link_add(Manager *m, sd_rtnl_message *message, Link **ret) { Link *link; - _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL; _cleanup_udev_device_unref_ struct udev_device *device = NULL; char ifindex_str[2 + DECIMAL_STR_MAX(int)]; int r; @@ -1516,33 +1452,21 @@ int link_add(Manager *m, sd_rtnl_message *message, Link **ret) { link = *ret; - log_debug_link(link, "link %d added", link->ifindex); - - r = sd_rtnl_message_new_addr(m->rtnl, &req, RTM_GETADDR, link->ifindex, - 0); - if (r < 0) - return r; - - r = sd_rtnl_call_async(m->rtnl, req, link_get_address_handler, link, 0, - NULL); - if (r < 0) - return r; - - link_ref(link); + log_link_debug(link, "link %d added", link->ifindex); if (detect_container(NULL) <= 0) { /* not in a container, udev will be around */ sprintf(ifindex_str, "n%d", link->ifindex); device = udev_device_new_from_device_id(m->udev, ifindex_str); if (!device) { - log_warning_link(link, + log_link_warning(link, "could not find udev device: %m"); return -errno; } if (udev_device_get_is_initialized(device) <= 0) { /* not yet ready */ - log_debug_link(link, "link pending udev initialization..."); + log_link_debug(link, "link pending udev initialization..."); return 0; } @@ -1574,13 +1498,13 @@ int link_update(Link *link, sd_rtnl_message *m) { if (link->state == LINK_STATE_LINGER) { link_ref(link); - log_info_link(link, "link readded"); + log_link_info(link, "link readded"); link->state = LINK_STATE_ENSLAVING; } r = sd_rtnl_message_read_string(m, IFLA_IFNAME, &ifname); if (r >= 0 && !streq(ifname, link->ifname)) { - log_info_link(link, "renamed to %s", ifname); + log_link_info(link, "renamed to %s", ifname); free(link->ifname); link->ifname = strdup(ifname); @@ -1593,7 +1517,7 @@ int link_update(Link *link, sd_rtnl_message *m) { link->mtu = mtu; if (!link->original_mtu) { link->original_mtu = mtu; - log_debug_link(link, "saved original MTU: %" + log_link_debug(link, "saved original MTU: %" PRIu32, link->original_mtu); } @@ -1601,7 +1525,7 @@ int link_update(Link *link, sd_rtnl_message *m) { r = sd_dhcp_client_set_mtu(link->dhcp_client, link->mtu); if (r < 0) { - log_warning_link(link, + log_link_warning(link, "Could not update MTU in DHCP client: %s", strerror(-r)); return r; @@ -1619,7 +1543,7 @@ int link_update(Link *link, sd_rtnl_message *m) { memcpy(link->mac.ether_addr_octet, mac.ether_addr_octet, ETH_ALEN); - log_debug_link(link, "MAC address: " + log_link_debug(link, "MAC address: " "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", mac.ether_addr_octet[0], mac.ether_addr_octet[1], @@ -1631,7 +1555,7 @@ int link_update(Link *link, sd_rtnl_message *m) { if (link->ipv4ll) { r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac); if (r < 0) { - log_warning_link(link, + log_link_warning(link, "Could not update MAC address in IPv4LL client: %s", strerror(-r)); return r; @@ -1644,7 +1568,7 @@ int link_update(Link *link, sd_rtnl_message *m) { sizeof (link->mac), ARPHRD_ETHER); if (r < 0) { - log_warning_link(link, + log_link_warning(link, "Could not update MAC address in DHCP client: %s", strerror(-r)); return r; @@ -1657,7 +1581,7 @@ int link_update(Link *link, sd_rtnl_message *m) { sizeof (link->mac), ARPHRD_ETHER); if (r < 0) { - log_warning_link(link, + log_link_warning(link, "Could not update MAC address in DHCPv6 client: %s", strerror(-r)); return r; @@ -1676,7 +1600,7 @@ int link_update(Link *link, sd_rtnl_message *m) { carrier_lost = had_carrier && !link_has_carrier(link); if (carrier_gained) { - log_info_link(link, "gained carrier"); + log_link_info(link, "gained carrier"); if (link->network) { r = link_acquire_conf(link); @@ -1686,7 +1610,7 @@ int link_update(Link *link, sd_rtnl_message *m) { } } } else if (carrier_lost) { - log_info_link(link, "lost carrier"); + log_link_info(link, "lost carrier"); r = link_stop_clients(link); if (r < 0) { @@ -1857,7 +1781,7 @@ int link_save(Link *link) { if (link->dhcp_lease) { assert(link->network); - r = dhcp_lease_save(link->dhcp_lease, link->lease_file); + r = sd_dhcp_lease_save(link->dhcp_lease, link->lease_file); if (r < 0) goto fail; @@ -1878,7 +1802,7 @@ int link_save(Link *link) { return 0; fail: - log_error_link(link, "Failed to save link data to %s: %s", link->state_file, strerror(-r)); + log_link_error(link, "Failed to save link data to %s: %s", link->state_file, strerror(-r)); unlink(link->state_file); unlink(temp_path); return r; diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 7acf404f87..05c34eef19 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -99,7 +99,6 @@ int link_get(Manager *m, int ifindex, Link **ret); int link_add(Manager *manager, sd_rtnl_message *message, Link **ret); void link_drop(Link *link); -int link_get_address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata); int link_address_drop_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata); int link_route_drop_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata); @@ -120,6 +119,7 @@ int link_set_hostname(Link *link, const char *hostname); int ipv4ll_configure(Link *link); int dhcp4_configure(Link *link); +int icmp6_configure(Link *link); const char* link_state_to_string(LinkState s) _const_; LinkState link_state_from_string(const char *s) _pure_; @@ -132,14 +132,22 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Link*, link_unref); /* Macros which append INTERFACE= to the message */ -#define log_full_link(level, link, fmt, ...) log_meta_object(level, __FILE__, __LINE__, __func__, "INTERFACE=", link->ifname, "%-*s: " fmt, IFNAMSIZ, link->ifname, ##__VA_ARGS__) -#define log_debug_link(link, ...) log_full_link(LOG_DEBUG, link, ##__VA_ARGS__) -#define log_info_link(link, ...) log_full_link(LOG_INFO, link, ##__VA_ARGS__) -#define log_notice_link(link, ...) log_full_link(LOG_NOTICE, link, ##__VA_ARGS__) -#define log_warning_link(link, ...) log_full_link(LOG_WARNING, link, ##__VA_ARGS__) -#define log_error_link(link, ...) log_full_link(LOG_ERR, link, ##__VA_ARGS__) +#define log_link_full(link, level, error, fmt, ...) \ + log_object_internal(level, error, __FILE__, __LINE__, __func__, "INTERFACE=", link->ifname, "%-*s: " fmt, IFNAMSIZ, link->ifname, ##__VA_ARGS__) -#define log_struct_link(level, link, ...) log_struct(level, "INTERFACE=%s", link->ifname, __VA_ARGS__) +#define log_link_debug(link, ...) log_link_full(link, LOG_DEBUG, 0, ##__VA_ARGS__) +#define log_link_info(link, ...) log_link_full(link, LOG_INFO, 0, ##__VA_ARGS__) +#define log_link_notice(link, ...) log_link_full(link, LOG_NOTICE, 0, ##__VA_ARGS__) +#define log_link_warning(link, ...) log_link_full(link, LOG_WARNING, 0, ##__VA_ARGS__) +#define log_link_error(link, ...) log_link_full(link, LOG_ERR, 0, ##__VA_ARGS__) + +#define log_link_debug_errno(link, error, ...) log_link_full(link, LOG_DEBUG, error, ##__VA_ARGS__) +#define log_link_info_errno(link, error, ...) log_link_full(link, LOG_INFO, error, ##__VA_ARGS__) +#define log_link_notice_errno(link, error, ...) log_link_full(link, LOG_NOTICE, error, ##__VA_ARGS__) +#define log_link_warning_errno(link, error, ...) log_link_full(link, LOG_WARNING, error, ##__VA_ARGS__) +#define log_link_error_errno(link, error, ...) log_link_full(link, LOG_ERR, error, ##__VA_ARGS__) + +#define log_link_struct(link, level, ...) log_struct(level, "INTERFACE=%s", link->ifname, __VA_ARGS__) #define ADDRESS_FMT_VAL(address) \ (address).s_addr & 0xFF, \ diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 2213ad717c..fe9008a3df 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -36,6 +36,9 @@ #include "sd-rtnl.h" +/* use 8 MB for receive socket kernel queue. */ +#define RCVBUF_SIZE (8*1024*1024) + const char* const network_dirs[] = { "/etc/systemd/network", "/run/systemd/network", @@ -98,6 +101,10 @@ int manager_new(Manager **ret) { if (r < 0) return r; + r = sd_rtnl_inc_rcvbuf(m->rtnl, RCVBUF_SIZE); + if (r < 0) + return r; + r = sd_bus_default_system(&m->bus); if (r < 0 && r != -ENOENT) /* TODO: drop when we can rely on kdbus */ return r; @@ -229,22 +236,33 @@ static int manager_rtnl_process_link(sd_rtnl *rtnl, sd_rtnl_message *message, vo assert(message); assert(m); + if (sd_rtnl_message_is_error(message)) { + r = sd_rtnl_message_get_errno(message); + if (r < 0) + log_warning_errno(r, "rtnl: could not receive link: %m"); + + return 0; + } + r = sd_rtnl_message_get_type(message, &type); if (r < 0) { - log_warning("rtnl: could not get message type"); + log_warning_errno(r, "rtnl: could not get message type: %m"); return 0; } r = sd_rtnl_message_link_get_ifindex(message, &ifindex); - if (r < 0 || ifindex <= 0) { - log_warning("rtnl: received link message without valid ifindex"); + if (r < 0) { + log_warning_errno(r, "rtnl: could not get ifindex: %m"); + return 0; + } else if (ifindex <= 0) { + log_warning("rtnl: received link message with invalid ifindex: %d", ifindex); return 0; } else link_get(m, ifindex, &link); r = sd_rtnl_message_read_string(message, IFLA_IFNAME, &name); - if (r < 0 || !name) { - log_warning("rtnl: received link message without valid ifname"); + if (r < 0) { + log_warning_errno(r, "rtnl: received link message without ifname: %m"); return 0; } else netdev_get(m, name, &netdev); @@ -255,8 +273,7 @@ static int manager_rtnl_process_link(sd_rtnl *rtnl, sd_rtnl_message *message, vo /* link is new, so add it */ r = link_add(m, message, &link); if (r < 0) { - log_debug("could not add new link: %s", - strerror(-r)); + log_debug_errno(r, "could not add new link: %m"); return 0; } } @@ -265,7 +282,7 @@ static int manager_rtnl_process_link(sd_rtnl *rtnl, sd_rtnl_message *message, vo /* netdev exists, so make sure the ifindex matches */ r = netdev_set_ifindex(netdev, message); if (r < 0) { - log_debug("could not set ifindex on netdev"); + log_debug_errno(r, "could not set ifindex on netdev: %m"); return 0; } } @@ -292,7 +309,7 @@ static int manager_rtnl_process_link(sd_rtnl *rtnl, sd_rtnl_message *message, vo int manager_rtnl_enumerate_links(Manager *m) { _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL; sd_rtnl_message *link; - int r, k; + int r; assert(m); assert(m->rtnl); @@ -310,16 +327,40 @@ int manager_rtnl_enumerate_links(Manager *m) { return r; for (link = reply; link; link = sd_rtnl_message_next(link)) { - uint16_t type; + int k; - k = sd_rtnl_message_get_type(link, &type); + k = manager_rtnl_process_link(m->rtnl, link, m); if (k < 0) - return k; + r = k; + } - if (type != RTM_NEWLINK) - continue; + return r; +} - k = manager_rtnl_process_link(m->rtnl, link, m); +int manager_rtnl_enumerate_addresses(Manager *m) { + _cleanup_rtnl_message_unref_ sd_rtnl_message *req = NULL, *reply = NULL; + sd_rtnl_message *addr; + int r; + + assert(m); + assert(m->rtnl); + + r = sd_rtnl_message_new_addr(m->rtnl, &req, RTM_GETADDR, 0, 0); + if (r < 0) + return r; + + r = sd_rtnl_message_request_dump(req, true); + if (r < 0) + return r; + + r = sd_rtnl_call(m->rtnl, req, 0, &reply); + if (r < 0) + return r; + + for (addr = reply; addr; addr = sd_rtnl_message_next(addr)) { + int k; + + k = link_rtnl_process_address(m->rtnl, addr, m); if (k < 0) r = k; } @@ -349,10 +390,8 @@ int manager_udev_listen(Manager *m) { assert(m->udev_monitor); r = udev_monitor_filter_add_match_subsystem_devtype(m->udev_monitor, "net", NULL); - if (r < 0) { - log_error("Could not add udev monitor filter: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not add udev monitor filter: %m"); r = udev_monitor_enable_receiving(m->udev_monitor); if (r < 0) { @@ -368,7 +407,7 @@ int manager_udev_listen(Manager *m) { if (r < 0) return r; - r = sd_event_source_set_name(m->udev_event_source, "networkd-udev"); + r = sd_event_source_set_description(m->udev_event_source, "networkd-udev"); if (r < 0) return r; @@ -590,7 +629,7 @@ int manager_save(Manager *m) { return 0; fail: - log_error("Failed to save network state to %s: %s", m->state_file, strerror(-r)); + log_error_errno(r, "Failed to save network state to %s: %m", m->state_file); unlink(m->state_file); unlink(temp_path); return r; diff --git a/src/network/networkd-netdev-bond.c b/src/network/networkd-netdev-bond.c index 46408213b7..88321ef84c 100644 --- a/src/network/networkd-netdev-bond.c +++ b/src/network/networkd-netdev-bond.c @@ -116,7 +116,7 @@ static int netdev_bond_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_m r = sd_rtnl_message_append_u8(m, IFLA_BOND_MODE, bond_mode_to_kernel(b->mode)); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_BOND_MODE attribute: %s", strerror(-r)); return r; @@ -127,7 +127,7 @@ static int netdev_bond_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_m r = sd_rtnl_message_append_u8(m, IFLA_BOND_XMIT_HASH_POLICY, bond_xmit_hash_policy_to_kernel(b->xmit_hash_policy)); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_BOND_XMIT_HASH_POLICY attribute: %s", strerror(-r)); return r; @@ -138,7 +138,7 @@ static int netdev_bond_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_m b->mode == NETDEV_BOND_MODE_802_3AD) { r = sd_rtnl_message_append_u8(m, IFLA_BOND_AD_LACP_RATE, b->lacp_rate ); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_BOND_AD_LACP_RATE attribute: %s", strerror(-r)); return r; @@ -148,7 +148,7 @@ static int netdev_bond_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_m if (b->miimon != 0) { r = sd_rtnl_message_append_u32(m, IFLA_BOND_MIIMON, b->miimon / USEC_PER_MSEC); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_BOND_BOND_MIIMON attribute: %s", strerror(-r)); return r; @@ -158,7 +158,7 @@ static int netdev_bond_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_m if (b->downdelay != 0) { r = sd_rtnl_message_append_u32(m, IFLA_BOND_DOWNDELAY, b->downdelay / USEC_PER_MSEC); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_BOND_DOWNDELAY attribute: %s", strerror(-r)); return r; @@ -168,7 +168,7 @@ static int netdev_bond_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_m if (b->updelay != 0) { r = sd_rtnl_message_append_u32(m, IFLA_BOND_UPDELAY, b->updelay / USEC_PER_MSEC); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_BOND_UPDELAY attribute: %s", strerror(-r)); return r; diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf index c524ee5798..b311ebe4fb 100644 --- a/src/network/networkd-netdev-gperf.gperf +++ b/src/network/networkd-netdev-gperf.gperf @@ -18,42 +18,47 @@ struct ConfigPerfItem; %struct-type %includes %% -Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(NetDev, match_host) -Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(NetDev, match_virt) -Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(NetDev, match_kernel) -Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(NetDev, match_arch) -NetDev.Description, config_parse_string, 0, offsetof(NetDev, description) -NetDev.Name, config_parse_ifname, 0, offsetof(NetDev, ifname) -NetDev.Kind, config_parse_netdev_kind, 0, offsetof(NetDev, kind) -NetDev.MTUBytes, config_parse_iec_size, 0, offsetof(NetDev, mtu) -NetDev.MACAddress, config_parse_hwaddr, 0, offsetof(NetDev, mac) -VLAN.Id, config_parse_uint64, 0, offsetof(VLan, id) -MACVLAN.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode) -Tunnel.Local, config_parse_tunnel_address, 0, offsetof(Tunnel, local) -Tunnel.Remote, config_parse_tunnel_address, 0, offsetof(Tunnel, remote) -Tunnel.TOS, config_parse_unsigned, 0, offsetof(Tunnel, tos) -Tunnel.TTL, config_parse_unsigned, 0, offsetof(Tunnel, ttl) -Tunnel.DiscoverPathMTU, config_parse_bool, 0, offsetof(Tunnel, pmtudisc) -Peer.Name, config_parse_ifname, 0, offsetof(Veth, ifname_peer) -Peer.MACAddress, config_parse_hwaddr, 0, offsetof(Veth, mac_peer) -VXLAN.Id, config_parse_uint64, 0, offsetof(VxLan, id) -VXLAN.Group, config_parse_tunnel_address, 0, offsetof(VxLan, group) -VXLAN.TOS, config_parse_unsigned, 0, offsetof(VxLan, tos) -VXLAN.TTL, config_parse_unsigned, 0, offsetof(VxLan, ttl) -VXLAN.MacLearning, config_parse_bool, 0, offsetof(VxLan, learning) -Tun.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue) -Tun.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue) -Tun.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info) -Tun.User, config_parse_string, 0, offsetof(TunTap, user_name) -Tun.Group, config_parse_string, 0, offsetof(TunTap, group_name) -Tap.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue) -Tap.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue) -Tap.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info) -Tap.User, config_parse_string, 0, offsetof(TunTap, user_name) -Tap.Group, config_parse_string, 0, offsetof(TunTap, group_name) -Bond.Mode, config_parse_bond_mode, 0, offsetof(Bond, mode) -Bond.TransmitHashPolicy, config_parse_bond_xmit_hash_policy, 0, offsetof(Bond, xmit_hash_policy) -Bond.LACPTransmitRate, config_parse_bond_lacp_rate, 0, offsetof(Bond, lacp_rate) -Bond.MIIMonitorSec, config_parse_sec, 0, offsetof(Bond, miimon) -Bond.UpDelaySec, config_parse_sec, 0, offsetof(Bond, updelay) -Bond.DownDelaySec, config_parse_sec, 0, offsetof(Bond, downdelay) +Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(NetDev, match_host) +Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(NetDev, match_virt) +Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(NetDev, match_kernel) +Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(NetDev, match_arch) +NetDev.Description, config_parse_string, 0, offsetof(NetDev, description) +NetDev.Name, config_parse_ifname, 0, offsetof(NetDev, ifname) +NetDev.Kind, config_parse_netdev_kind, 0, offsetof(NetDev, kind) +NetDev.MTUBytes, config_parse_iec_size, 0, offsetof(NetDev, mtu) +NetDev.MACAddress, config_parse_hwaddr, 0, offsetof(NetDev, mac) +VLAN.Id, config_parse_uint64, 0, offsetof(VLan, id) +MACVLAN.Mode, config_parse_macvlan_mode, 0, offsetof(MacVlan, mode) +Tunnel.Local, config_parse_tunnel_address, 0, offsetof(Tunnel, local) +Tunnel.Remote, config_parse_tunnel_address, 0, offsetof(Tunnel, remote) +Tunnel.TOS, config_parse_unsigned, 0, offsetof(Tunnel, tos) +Tunnel.TTL, config_parse_unsigned, 0, offsetof(Tunnel, ttl) +Tunnel.DiscoverPathMTU, config_parse_bool, 0, offsetof(Tunnel, pmtudisc) +Peer.Name, config_parse_ifname, 0, offsetof(Veth, ifname_peer) +Peer.MACAddress, config_parse_hwaddr, 0, offsetof(Veth, mac_peer) +VXLAN.Id, config_parse_uint64, 0, offsetof(VxLan, id) +VXLAN.Group, config_parse_vxlan_group_address, 0, offsetof(VxLan, group) +VXLAN.TOS, config_parse_unsigned, 0, offsetof(VxLan, tos) +VXLAN.TTL, config_parse_unsigned, 0, offsetof(VxLan, ttl) +VXLAN.MacLearning, config_parse_bool, 0, offsetof(VxLan, learning) +VXLAN.ARPProxy, config_parse_bool, 0, offsetof(VxLan, arp_proxy) +VXLAN.L2MissNotification, config_parse_bool, 0, offsetof(VxLan, l2miss) +VXLAN.L3MissNotification, config_parse_bool, 0, offsetof(VxLan, l3miss) +VXLAN.RouteShortCircuit, config_parse_bool, 0, offsetof(VxLan, route_short_circuit) +VXLAN.FDBAgeingSec, config_parse_sec, 0, offsetof(VxLan, fdb_ageing) +Tun.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue) +Tun.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue) +Tun.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info) +Tun.User, config_parse_string, 0, offsetof(TunTap, user_name) +Tun.Group, config_parse_string, 0, offsetof(TunTap, group_name) +Tap.OneQueue, config_parse_bool, 0, offsetof(TunTap, one_queue) +Tap.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue) +Tap.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info) +Tap.User, config_parse_string, 0, offsetof(TunTap, user_name) +Tap.Group, config_parse_string, 0, offsetof(TunTap, group_name) +Bond.Mode, config_parse_bond_mode, 0, offsetof(Bond, mode) +Bond.TransmitHashPolicy, config_parse_bond_xmit_hash_policy, 0, offsetof(Bond, xmit_hash_policy) +Bond.LACPTransmitRate, config_parse_bond_lacp_rate, 0, offsetof(Bond, lacp_rate) +Bond.MIIMonitorSec, config_parse_sec, 0, offsetof(Bond, miimon) +Bond.UpDelaySec, config_parse_sec, 0, offsetof(Bond, updelay) +Bond.DownDelaySec, config_parse_sec, 0, offsetof(Bond, downdelay) diff --git a/src/network/networkd-netdev-macvlan.c b/src/network/networkd-netdev-macvlan.c index 2e5554a172..198fb575ee 100644 --- a/src/network/networkd-netdev-macvlan.c +++ b/src/network/networkd-netdev-macvlan.c @@ -48,7 +48,7 @@ static int netdev_macvlan_fill_message_create(NetDev *netdev, Link *link, sd_rtn if (m->mode != _NETDEV_MACVLAN_MODE_INVALID) { r = sd_rtnl_message_append_u32(req, IFLA_MACVLAN_MODE, m->mode); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_MACVLAN_MODE attribute: %s", strerror(-r)); return r; diff --git a/src/network/networkd-netdev-tunnel.c b/src/network/networkd-netdev-tunnel.c index 174fe234fd..31d34644ed 100644 --- a/src/network/networkd-netdev-tunnel.c +++ b/src/network/networkd-netdev-tunnel.c @@ -45,7 +45,7 @@ static int netdev_ipip_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_m r = sd_rtnl_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_IPTUN_LINK attribute: %s", strerror(-r)); return r; @@ -53,7 +53,7 @@ static int netdev_ipip_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_m r = sd_rtnl_message_append_in_addr(m, IFLA_IPTUN_LOCAL, &t->local.in); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_IPTUN_LOCAL attribute: %s", strerror(-r)); return r; @@ -61,7 +61,7 @@ static int netdev_ipip_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_m r = sd_rtnl_message_append_in_addr(m, IFLA_IPTUN_REMOTE, &t->remote.in); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_IPTUN_REMOTE attribute: %s", strerror(-r)); return r; @@ -69,12 +69,20 @@ static int netdev_ipip_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_m r = sd_rtnl_message_append_u8(m, IFLA_IPTUN_TTL, t->ttl); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_IPTUN_TTL attribute: %s", strerror(-r)); return r; } + r = sd_rtnl_message_append_u8(m, IFLA_IPTUN_PMTUDISC, t->pmtudisc); + if (r < 0) { + log_netdev_error(netdev, + "Could not append IFLA_IPTUN_PMTUDISC attribute: %s", + strerror(-r)); + return r; + } + return r; } @@ -90,7 +98,7 @@ static int netdev_sit_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_me r = sd_rtnl_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_IPTUN_LINK attribute: %s", strerror(-r)); return r; @@ -98,7 +106,7 @@ static int netdev_sit_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_me r = sd_rtnl_message_append_in_addr(m, IFLA_IPTUN_LOCAL, &t->local.in); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_IPTUN_LOCAL attribute: %s", strerror(-r)); return r; @@ -106,7 +114,7 @@ static int netdev_sit_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_me r = sd_rtnl_message_append_in_addr(m, IFLA_IPTUN_REMOTE, &t->remote.in); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_IPTUN_REMOTE attribute: %s", strerror(-r)); return r; @@ -114,12 +122,20 @@ static int netdev_sit_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_me r = sd_rtnl_message_append_u8(m, IFLA_IPTUN_TTL, t->ttl); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_IPTUN_TTL attribute: %s", strerror(-r)); return r; } + r = sd_rtnl_message_append_u8(m, IFLA_IPTUN_PMTUDISC, t->pmtudisc); + if (r < 0) { + log_netdev_error(netdev, + "Could not append IFLA_IPTUN_PMTUDISC attribute: %s", + strerror(-r)); + return r; + } + return r; } @@ -135,7 +151,7 @@ static int netdev_gre_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_me r = sd_rtnl_message_append_u32(m, IFLA_GRE_LINK, link->ifindex); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_GRE_LINK attribute: %s", strerror(-r)); return r; @@ -143,7 +159,7 @@ static int netdev_gre_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_me r = sd_rtnl_message_append_in_addr(m, IFLA_GRE_LOCAL, &t->local.in); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_GRE_LOCAL attribute: %s", strerror(-r)); return r; @@ -151,7 +167,7 @@ static int netdev_gre_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_me r = sd_rtnl_message_append_in_addr(m, IFLA_GRE_REMOTE, &t->remote.in); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_GRE_REMOTE attribute: %s", strerror(-r)); return r; @@ -159,7 +175,7 @@ static int netdev_gre_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_me r = sd_rtnl_message_append_u8(m, IFLA_GRE_TTL, t->ttl); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_GRE_TTL attribute: %s", strerror(-r)); return r; @@ -167,12 +183,20 @@ static int netdev_gre_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_me r = sd_rtnl_message_append_u8(m, IFLA_GRE_TOS, t->tos); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_GRE_TOS attribute: %s", strerror(-r)); return r; } + r = sd_rtnl_message_append_u8(m, IFLA_GRE_PMTUDISC, t->pmtudisc); + if (r < 0) { + log_netdev_error(netdev, + "Could not append IFLA_GRE_PMTUDISC attribute: %s", + strerror(-r)); + return r; + } + return r; } @@ -188,7 +212,7 @@ static int netdev_vti_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_me r = sd_rtnl_message_append_u32(m, IFLA_VTI_LINK, link->ifindex); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_IPTUN_LINK attribute: %s", strerror(-r)); return r; @@ -196,7 +220,7 @@ static int netdev_vti_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_me r = sd_rtnl_message_append_in_addr(m, IFLA_VTI_LOCAL, &t->local.in); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_IPTUN_LOCAL attribute: %s", strerror(-r)); return r; @@ -204,7 +228,7 @@ static int netdev_vti_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_me r = sd_rtnl_message_append_in_addr(m, IFLA_VTI_REMOTE, &t->remote.in); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_IPTUN_REMOTE attribute: %s", strerror(-r)); return r; @@ -238,11 +262,6 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) { assert(t); - if (t->local.in.s_addr == INADDR_ANY) { - log_warning("Tunnel without local address configured in %s. Ignoring", filename); - return -EINVAL; - } - if (t->remote.in.s_addr == INADDR_ANY) { log_warning("Tunnel without remote address configured in %s. Ignoring", filename); return -EINVAL; diff --git a/src/network/networkd-netdev-tuntap.c b/src/network/networkd-netdev-tuntap.c index eaf5df4971..4f449aea48 100644 --- a/src/network/networkd-netdev-tuntap.c +++ b/src/network/networkd-netdev-tuntap.c @@ -70,13 +70,13 @@ static int netdev_tuntap_add(NetDev *netdev, struct ifreq *ifr) { fd = open(TUN_DEV, O_RDWR); if (fd < 0) { - log_error_netdev(netdev, "Failed to open tun dev: %m"); + log_netdev_error(netdev, "Failed to open tun dev: %m"); return -errno; } r = ioctl(fd, TUNSETIFF, ifr); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "TUNSETIFF failed on tun dev: %s", strerror(-r)); return r; @@ -95,14 +95,14 @@ static int netdev_tuntap_add(NetDev *netdev, struct ifreq *ifr) { r = get_user_creds(&user, &uid, NULL, NULL, NULL); if (r < 0) { - log_error("Cannot resolve user name %s: %s", - t->user_name, strerror(-r)); + log_error_errno(r, "Cannot resolve user name %s: %m", + t->user_name); return 0; } r = ioctl(fd, TUNSETOWNER, uid); if ( r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "TUNSETOWNER failed on tun dev: %s", strerror(-r)); } @@ -114,14 +114,14 @@ static int netdev_tuntap_add(NetDev *netdev, struct ifreq *ifr) { r = get_group_creds(&group, &gid); if (r < 0) { - log_error("Cannot resolve group name %s: %s", - t->group_name, strerror(-r)); + log_error_errno(r, "Cannot resolve group name %s: %m", + t->group_name); return 0; } r = ioctl(fd, TUNSETGROUP, gid); if( r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "TUNSETGROUP failed on tun dev: %s", strerror(-r)); return r; @@ -131,7 +131,7 @@ static int netdev_tuntap_add(NetDev *netdev, struct ifreq *ifr) { r = ioctl(fd, TUNSETPERSIST, 1); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "TUNSETPERSIST failed on tun dev: %s", strerror(-r)); return r; @@ -171,17 +171,8 @@ static void tuntap_done(NetDev *netdev) { } static int tuntap_verify(NetDev *netdev, const char *filename) { - TunTap *t = NULL; - assert(netdev); - if (netdev->kind == NETDEV_KIND_TUN) - t = TUN(netdev); - else - t = TAP(netdev); - - assert(t); - if (netdev->mtu) { log_warning_netdev(netdev, "MTU configured for %s, ignoring", netdev_kind_to_string(netdev->kind)); diff --git a/src/network/networkd-netdev-veth.c b/src/network/networkd-netdev-veth.c index da09ef908f..251cf92aec 100644 --- a/src/network/networkd-netdev-veth.c +++ b/src/network/networkd-netdev-veth.c @@ -38,24 +38,22 @@ static int netdev_veth_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_m r = sd_rtnl_message_open_container(m, VETH_INFO_PEER); if (r < 0) { - log_error_netdev(netdev, - "Could not append IFLA_IPTUN_LINK attribute: %s", + log_netdev_error(netdev, + "Could not append VETH_INFO_PEER attribute: %s", strerror(-r)); return r; } if (v->ifname_peer) { r = sd_rtnl_message_append_string(m, IFLA_IFNAME, v->ifname_peer); - if (r < 0) { - log_error("Failed to add netlink interface name: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add netlink interface name: %m"); } if (v->mac_peer) { r = sd_rtnl_message_append_ether_addr(m, IFLA_ADDRESS, v->mac_peer); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_ADDRESS attribute: %s", strerror(-r)); return r; @@ -64,7 +62,7 @@ static int netdev_veth_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_m r = sd_rtnl_message_close_container(m); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_INFO_DATA attribute: %s", strerror(-r)); return r; diff --git a/src/network/networkd-netdev-vlan.c b/src/network/networkd-netdev-vlan.c index 13c4456733..665559f895 100644 --- a/src/network/networkd-netdev-vlan.c +++ b/src/network/networkd-netdev-vlan.c @@ -37,7 +37,7 @@ static int netdev_vlan_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_m if (v->id <= VLANID_MAX) { r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, v->id); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_VLAN_ID attribute: %s", strerror(-r)); return r; diff --git a/src/network/networkd-netdev-vxlan.c b/src/network/networkd-netdev-vxlan.c index 326ac547a3..d5128cb7c0 100644 --- a/src/network/networkd-netdev-vxlan.c +++ b/src/network/networkd-netdev-vxlan.c @@ -26,6 +26,7 @@ #include "sd-rtnl.h" #include "networkd-netdev-vxlan.h" #include "networkd-link.h" +#include "conf-parser.h" #include "missing.h" static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_message *m) { @@ -41,7 +42,7 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_ if (v->id <= VXLAN_VID_MAX) { r = sd_rtnl_message_append_u32(m, IFLA_VXLAN_ID, v->id); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_VXLAN_ID attribute: %s", strerror(-r)); return r; @@ -50,7 +51,7 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_ r = sd_rtnl_message_append_in_addr(m, IFLA_VXLAN_GROUP, &v->group.in); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_VXLAN_GROUP attribute: %s", strerror(-r)); return r; @@ -58,7 +59,7 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_ r = sd_rtnl_message_append_u32(m, IFLA_VXLAN_LINK, link->ifindex); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_VXLAN_LINK attribute: %s", strerror(-r)); return r; @@ -67,7 +68,7 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_ if(v->ttl) { r = sd_rtnl_message_append_u8(m, IFLA_VXLAN_TTL, v->ttl); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_VXLAN_TTL attribute: %s", strerror(-r)); return r; @@ -77,7 +78,7 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_ if(v->tos) { r = sd_rtnl_message_append_u8(m, IFLA_VXLAN_TOS, v->tos); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_VXLAN_TOS attribute: %s", strerror(-r)); return r; @@ -86,15 +87,95 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_rtnl_ r = sd_rtnl_message_append_u8(m, IFLA_VXLAN_LEARNING, v->learning); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_VXLAN_LEARNING attribute: %s", strerror(-r)); return r; } + r = sd_rtnl_message_append_u8(m, IFLA_VXLAN_RSC, v->route_short_circuit); + if (r < 0) { + log_netdev_error(netdev, + "Could not append IFLA_VXLAN_RSC attribute: %s", + strerror(-r)); + return r; + } + + r = sd_rtnl_message_append_u8(m, IFLA_VXLAN_PROXY, v->arp_proxy); + if (r < 0) { + log_netdev_error(netdev, + "Could not append IFLA_VXLAN_PROXY attribute: %s", + strerror(-r)); + return r; + } + + r = sd_rtnl_message_append_u8(m, IFLA_VXLAN_L2MISS, v->l2miss); + if (r < 0) { + log_netdev_error(netdev, + "Could not append IFLA_VXLAN_L2MISS attribute: %s", + strerror(-r)); + return r; + } + + r = sd_rtnl_message_append_u8(m, IFLA_VXLAN_L3MISS, v->l3miss); + if (r < 0) { + log_netdev_error(netdev, + "Could not append IFLA_VXLAN_L3MISS attribute: %s", + strerror(-r)); + return r; + } + + if(v->fdb_ageing) { + r = sd_rtnl_message_append_u32(m, IFLA_VXLAN_AGEING, v->fdb_ageing / USEC_PER_SEC); + if (r < 0) { + log_netdev_error(netdev, + "Could not append IFLA_VXLAN_AGEING attribute: %s", + strerror(-r)); + return r; + } + } + return r; } +int config_parse_vxlan_group_address(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + VxLan *v = userdata; + union in_addr_union *addr = data, buffer; + int r, f; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = in_addr_from_string_auto(rvalue, &f, &buffer); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "vxlan multicast group address is invalid, ignoring assignment: %s", rvalue); + return 0; + } + + if(v->family != AF_UNSPEC && v->family != f) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "vxlan multicast group incompatible, ignoring assignment: %s", rvalue); + return 0; + } + + v->family = f; + *addr = buffer; + + return 0; +} + static int netdev_vxlan_verify(NetDev *netdev, const char *filename) { VxLan *v = VXLAN(netdev); diff --git a/src/network/networkd-netdev-vxlan.h b/src/network/networkd-netdev-vxlan.h index 8c906f1075..6339af9add 100644 --- a/src/network/networkd-netdev-vxlan.h +++ b/src/network/networkd-netdev-vxlan.h @@ -33,10 +33,20 @@ struct VxLan { NetDev meta; uint64_t id; + + int family; union in_addr_union group; + unsigned tos; unsigned ttl; + + usec_t fdb_ageing; + bool learning; + bool arp_proxy; + bool route_short_circuit; + bool l2miss; + bool l3miss; }; extern const NetDevVTable vxlan_vtable; diff --git a/src/network/networkd-netdev.c b/src/network/networkd-netdev.c index fd1f51ec56..b75eab9cd8 100644 --- a/src/network/networkd-netdev.c +++ b/src/network/networkd-netdev.c @@ -65,7 +65,6 @@ static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = { DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind); DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetDevKind, "Failed to parse netdev kind"); - static void netdev_cancel_callbacks(NetDev *netdev) { _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL; netdev_join_callback *callback; @@ -137,7 +136,7 @@ void netdev_drop(NetDev *netdev) { netdev->state = NETDEV_STATE_LINGER; - log_debug_netdev(netdev, "netdev removed"); + log_netdev_debug(netdev, "netdev removed"); netdev_cancel_callbacks(netdev); @@ -185,7 +184,7 @@ static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_hand r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_SETLINK, link->ifindex); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not allocate RTM_SETLINK message: %s", strerror(-r)); return r; @@ -193,7 +192,7 @@ static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_hand r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_MASTER attribute: %s", strerror(-r)); return r; @@ -201,7 +200,7 @@ static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_hand r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not send rtnetlink message: %s", strerror(-r)); return r; @@ -209,7 +208,7 @@ static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_hand link_ref(link); - log_debug_netdev(netdev, "enslaving link '%s'", link->ifname); + log_netdev_debug(netdev, "enslaving link '%s'", link->ifname); return 0; } @@ -252,7 +251,7 @@ static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userda r = sd_rtnl_message_get_errno(m); if (r == -EEXIST) - log_debug_netdev(netdev, "netdev exists, using existing"); + log_netdev_debug(netdev, "netdev exists, using existing"); else if (r < 0) { log_warning_netdev(netdev, "netdev could not be created: %s", strerror(-r)); netdev_drop(netdev); @@ -260,7 +259,7 @@ static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userda return 1; } - log_debug_netdev(netdev, "created"); + log_netdev_debug(netdev, "created"); return 1; } @@ -289,7 +288,7 @@ int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callbac LIST_PREPEND(callbacks, netdev->callbacks, cb); - log_debug_netdev(netdev, "will enslave '%s', when reday", + log_netdev_debug(netdev, "will enslave '%s', when reday", link->ifname); } @@ -308,29 +307,29 @@ int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) { r = sd_rtnl_message_get_type(message, &type); if (r < 0) { - log_error_netdev(netdev, "Could not get rtnl message type"); + log_netdev_error(netdev, "Could not get rtnl message type"); return r; } if (type != RTM_NEWLINK) { - log_error_netdev(netdev, "Can not set ifindex from unexpected rtnl message type"); + log_netdev_error(netdev, "Can not set ifindex from unexpected rtnl message type"); return -EINVAL; } r = sd_rtnl_message_link_get_ifindex(message, &ifindex); if (r < 0) { - log_error_netdev(netdev, "Could not get ifindex: %s", strerror(-r)); + log_netdev_error(netdev, "Could not get ifindex: %s", strerror(-r)); netdev_enter_failed(netdev); return r; } else if (ifindex <= 0) { - log_error_netdev(netdev, "Got invalid ifindex: %d", ifindex); + log_netdev_error(netdev, "Got invalid ifindex: %d", ifindex); netdev_enter_failed(netdev); return r; } if (netdev->ifindex > 0) { if (netdev->ifindex != ifindex) { - log_error_netdev(netdev, "Could not set ifindex to %d, already set to %d", + log_netdev_error(netdev, "Could not set ifindex to %d, already set to %d", ifindex, netdev->ifindex); netdev_enter_failed(netdev); return -EEXIST; @@ -341,12 +340,12 @@ int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) { r = sd_rtnl_message_read_string(message, IFLA_IFNAME, &received_name); if (r < 0) { - log_error_netdev(netdev, "Could not get IFNAME"); + log_netdev_error(netdev, "Could not get IFNAME"); return r; } if (!streq(netdev->ifname, received_name)) { - log_error_netdev(netdev, "Received newlink with wrong IFNAME %s", + log_netdev_error(netdev, "Received newlink with wrong IFNAME %s", received_name); netdev_enter_failed(netdev); return r; @@ -354,19 +353,19 @@ int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) { r = sd_rtnl_message_enter_container(message, IFLA_LINKINFO); if (r < 0) { - log_error_netdev(netdev, "Could not get LINKINFO"); + log_netdev_error(netdev, "Could not get LINKINFO"); return r; } r = sd_rtnl_message_read_string(message, IFLA_INFO_KIND, &received_kind); if (r < 0) { - log_error_netdev(netdev, "Could not get KIND"); + log_netdev_error(netdev, "Could not get KIND"); return r; } r = sd_rtnl_message_exit_container(message); if (r < 0) { - log_error_netdev(netdev, "Could not exit container"); + log_netdev_error(netdev, "Could not exit container"); return r; } @@ -376,14 +375,14 @@ int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) { else { kind = netdev_kind_to_string(netdev->kind); if (!kind) { - log_error_netdev(netdev, "Could not get kind"); + log_netdev_error(netdev, "Could not get kind"); netdev_enter_failed(netdev); return -EINVAL; } } if (!streq(kind, received_kind)) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Received newlink with wrong KIND %s, " "expected %s", received_kind, kind); netdev_enter_failed(netdev); @@ -392,7 +391,7 @@ int netdev_set_ifindex(NetDev *netdev, sd_rtnl_message *message) { netdev->ifindex = ifindex; - log_debug_netdev(netdev, "netdev has index %d", netdev->ifindex); + log_netdev_debug(netdev, "netdev has index %d", netdev->ifindex); netdev_enter_ready(netdev); @@ -460,13 +459,13 @@ static int netdev_create(NetDev *netdev, Link *link, if (r < 0) return r; - log_debug_netdev(netdev, "created"); + log_netdev_debug(netdev, "created"); } else { _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL; r = sd_rtnl_message_new_link(netdev->manager->rtnl, &m, RTM_NEWLINK, 0); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not allocate RTM_NEWLINK message: %s", strerror(-r)); return r; @@ -474,7 +473,7 @@ static int netdev_create(NetDev *netdev, Link *link, r = sd_rtnl_message_append_string(m, IFLA_IFNAME, netdev->ifname); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_IFNAME, attribute: %s", strerror(-r)); return r; @@ -483,7 +482,7 @@ static int netdev_create(NetDev *netdev, Link *link, if (netdev->mac) { r = sd_rtnl_message_append_ether_addr(m, IFLA_ADDRESS, netdev->mac); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_ADDRESS attribute: %s", strerror(-r)); return r; @@ -493,7 +492,7 @@ static int netdev_create(NetDev *netdev, Link *link, if (netdev->mtu) { r = sd_rtnl_message_append_u32(m, IFLA_MTU, netdev->mtu); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_MTU attribute: %s", strerror(-r)); return r; @@ -503,8 +502,8 @@ static int netdev_create(NetDev *netdev, Link *link, if (link) { r = sd_rtnl_message_append_u32(m, IFLA_LINK, link->ifindex); if (r < 0) { - log_error_netdev(netdev, - "Colud not append IFLA_LINK attribute: %s", + log_netdev_error(netdev, + "Could not append IFLA_LINK attribute: %s", strerror(-r)); return r; } @@ -512,7 +511,7 @@ static int netdev_create(NetDev *netdev, Link *link, r = sd_rtnl_message_open_container(m, IFLA_LINKINFO); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_LINKINFO attribute: %s", strerror(-r)); return r; @@ -521,7 +520,7 @@ static int netdev_create(NetDev *netdev, Link *link, r = sd_rtnl_message_open_container_union(m, IFLA_INFO_DATA, netdev_kind_to_string(netdev->kind)); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_INFO_DATA attribute: %s", strerror(-r)); return r; @@ -535,7 +534,7 @@ static int netdev_create(NetDev *netdev, Link *link, r = sd_rtnl_message_close_container(m); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_LINKINFO attribute: %s", strerror(-r)); return r; @@ -543,7 +542,7 @@ static int netdev_create(NetDev *netdev, Link *link, r = sd_rtnl_message_close_container(m); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not append IFLA_LINKINFO attribute: %s", strerror(-r)); return r; @@ -554,7 +553,7 @@ static int netdev_create(NetDev *netdev, Link *link, r = sd_rtnl_call_async(netdev->manager->rtnl, m, callback, link, 0, NULL); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not send rtnetlink message: %s", strerror(-r)); return r; @@ -566,7 +565,7 @@ static int netdev_create(NetDev *netdev, Link *link, netdev_create_handler, netdev, 0, NULL); if (r < 0) { - log_error_netdev(netdev, + log_netdev_error(netdev, "Could not send rtnetlink message: %s", strerror(-r)); return r; @@ -577,7 +576,7 @@ static int netdev_create(NetDev *netdev, Link *link, netdev->state = NETDEV_STATE_CREATING; - log_debug_netdev(netdev, "creating"); + log_netdev_debug(netdev, "creating"); } return 0; @@ -714,7 +713,7 @@ static int netdev_load_one(Manager *manager, const char *filename) { LIST_HEAD_INIT(netdev->callbacks); - log_debug_netdev(netdev, "loaded %s", netdev_kind_to_string(netdev->kind)); + log_netdev_debug(netdev, "loaded %s", netdev_kind_to_string(netdev->kind)); switch (NETDEV_VTABLE(netdev)->create_type) { case NETDEV_CREATE_MASTER: @@ -744,10 +743,8 @@ int netdev_load(Manager *manager) { netdev_unref(netdev); r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs); - if (r < 0) { - log_error("Failed to enumerate netdev files: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to enumerate netdev files: %m"); STRV_FOREACH_BACKWARDS(f, files) { r = netdev_load_one(manager, *f); diff --git a/src/network/networkd-netdev.h b/src/network/networkd-netdev.h index e9a8a169db..c1e05c21fb 100644 --- a/src/network/networkd-netdev.h +++ b/src/network/networkd-netdev.h @@ -25,7 +25,6 @@ #include "hashmap.h" #include "list.h" #include "set.h" -#include "condition-util.h" #include "in-addr-util.h" typedef struct NetDevVTable NetDevVTable; @@ -193,12 +192,12 @@ const struct ConfigPerfItem* network_netdev_gperf_lookup(const char *key, unsign /* Macros which append INTERFACE= to the message */ -#define log_full_netdev(level, netdev, fmt, ...) log_meta_object(level, __FILE__, __LINE__, __func__, "INTERFACE=", netdev->ifname, "%-*s: " fmt, IFNAMSIZ, netdev->ifname, ##__VA_ARGS__) -#define log_debug_netdev(netdev, ...) log_full_netdev(LOG_DEBUG, netdev, ##__VA_ARGS__) +#define log_full_netdev(level, netdev, fmt, ...) log_object_internal(level, 0, __FILE__, __LINE__, __func__, "INTERFACE=", netdev->ifname, "%-*s: " fmt, IFNAMSIZ, netdev->ifname, ##__VA_ARGS__) +#define log_netdev_debug(netdev, ...) log_full_netdev(LOG_DEBUG, netdev, ##__VA_ARGS__) #define log_info_netdev(netdev, ...) log_full_netdev(LOG_INFO, netdev, ##__VA_ARGS__) #define log_notice_netdev(netdev, ...) log_full_netdev(LOG_NOTICE, netdev, ##__VA_ARGS__) #define log_warning_netdev(netdev, ...) log_full_netdev(LOG_WARNING, netdev,## __VA_ARGS__) -#define log_error_netdev(netdev, ...) log_full_netdev(LOG_ERR, netdev, ##__VA_ARGS__) +#define log_netdev_error(netdev, ...) log_full_netdev(LOG_ERR, netdev, ##__VA_ARGS__) #define log_struct_netdev(level, netdev, ...) log_struct(level, "INTERFACE=%s", netdev->ifname, __VA_ARGS__) diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index a73646187e..640a3a20b8 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -24,6 +24,8 @@ Match.Host, config_parse_net_condition, CONDITION_HOST, Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(Network, match_virt) Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(Network, match_kernel) Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(Network, match_arch) +Link.MACAddress, config_parse_hwaddr, 0, offsetof(Network, mac) +Link.MTUBytes, config_parse_iec_size, 0, offsetof(Network, mtu) Network.Description, config_parse_string, 0, offsetof(Network, description) Network.Bridge, config_parse_netdev, 0, offsetof(Network, bridge) Network.Bond, config_parse_netdev, 0, offsetof(Network, bond) @@ -47,6 +49,7 @@ Address.Broadcast, config_parse_broadcast, 0, Address.Label, config_parse_label, 0, 0 Route.Gateway, config_parse_gateway, 0, 0 Route.Destination, config_parse_destination, 0, 0 +Route.Source, config_parse_destination, 0, 0 Route.Metric, config_parse_route_priority, 0, 0 DHCP.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_dns) DHCP.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_mtu) @@ -58,6 +61,7 @@ DHCP.RequestBroadcast, config_parse_bool, 0, DHCP.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical) DHCP.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier) DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric) +Bridge.Cost, config_parse_unsigned, 0, offsetof(Network, cost) /* backwards compatibility: do not add new entries to this section */ DHCPv4.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_dns) DHCPv4.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_mtu) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index aad99236c4..ef9e0a8c35 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -90,7 +90,14 @@ static int network_load_one(Manager *manager, const char *filename) { network->llmnr = LLMNR_SUPPORT_YES; r = config_parse(NULL, filename, file, - "Match\0Network\0Address\0Route\0DHCP\0DHCPv4\0", + "Match\0" + "Link\0" + "Network\0" + "Address\0" + "Route\0" + "DHCP\0" + "DHCPv4\0" + "Bridge\0", config_item_perf_lookup, network_network_gperf_lookup, false, false, true, network); if (r < 0) @@ -131,10 +138,8 @@ int network_load(Manager *manager) { network_free(network); r = conf_files_list_strv(&files, ".network", NULL, network_dirs); - if (r < 0) { - log_error("Failed to enumerate network files: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to enumerate network files: %m"); STRV_FOREACH_BACKWARDS(f, files) { r = network_load_one(manager, *f); @@ -165,6 +170,8 @@ void network_free(Network *network) { free(network->description); free(network->dhcp_vendor_class_identifier); + free(network->mac); + strv_free(network->ntp); strv_free(network->dns); strv_free(network->domains); @@ -219,8 +226,22 @@ int network_get(Manager *manager, struct udev_device *device, udev_device_get_property_value(device, "ID_NET_DRIVER"), udev_device_get_devtype(device), ifname)) { - log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, - network->filename); + if (network->match_name) { + const char *attr; + uint8_t name_assign_type = NET_NAME_UNKNOWN; + + attr = udev_device_get_sysattr_value(device, "name_assign_type"); + if (attr) + (void)safe_atou8(attr, &name_assign_type); + + if (name_assign_type == NET_NAME_ENUM) + log_warning("%-*s: found matching network '%s', based on potentially unpredictable ifname", + IFNAMSIZ, ifname, network->filename); + else + log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename); + } else + log_debug("%-*s: found matching network '%s'", IFNAMSIZ, ifname, network->filename); + *ret = network; return 0; } diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 10d8cd902a..590dd49df9 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -113,18 +113,16 @@ int route_drop(Route *route, Link *link, r = sd_rtnl_message_new_route(link->manager->rtnl, &req, RTM_DELROUTE, route->family, route->protocol); - if (r < 0) { - log_error("Could not create RTM_DELROUTE message: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not create RTM_DELROUTE message: %m"); - if (route->family == AF_INET) - r = sd_rtnl_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in); - else if (route->family == AF_INET6) - r = sd_rtnl_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6); - if (r < 0) { - log_error("Could not append RTA_GATEWAY attribute: %s", strerror(-r)); - return r; + if (!in_addr_is_null(route->family, &route->in_addr)) { + if (route->family == AF_INET) + r = sd_rtnl_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in); + else if (route->family == AF_INET6) + r = sd_rtnl_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6); + if (r < 0) + return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m"); } if (route->dst_prefixlen) { @@ -132,16 +130,25 @@ int route_drop(Route *route, Link *link, r = sd_rtnl_message_append_in_addr(req, RTA_DST, &route->dst_addr.in); else if (route->family == AF_INET6) r = sd_rtnl_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6); - if (r < 0) { - log_error("Could not append RTA_DST attribute: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not append RTA_DST attribute: %m"); r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen); - if (r < 0) { - log_error("Could not set destination prefix length: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not set destination prefix length: %m"); + } + + if (route->src_prefixlen) { + if (route->family == AF_INET) + r = sd_rtnl_message_append_in_addr(req, RTA_SRC, &route->src_addr.in); + else if (route->family == AF_INET6) + r = sd_rtnl_message_append_in6_addr(req, RTA_SRC, &route->src_addr.in6); + if (r < 0) + return log_error_errno(r, "Could not append RTA_DST attribute: %m"); + + r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen); + if (r < 0) + return log_error_errno(r, "Could not set source prefix length: %m"); } if (!in_addr_is_null(route->family, &route->prefsrc_addr)) { @@ -149,35 +156,25 @@ int route_drop(Route *route, Link *link, r = sd_rtnl_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in); else if (route->family == AF_INET6) r = sd_rtnl_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in6); - if (r < 0) { - log_error("Could not append RTA_PREFSRC attribute: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m"); } r = sd_rtnl_message_route_set_scope(req, route->scope); - if (r < 0) { - log_error("Could not set scope: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not set scope: %m"); r = sd_rtnl_message_append_u32(req, RTA_PRIORITY, route->metrics); - if (r < 0) { - log_error("Could not append RTA_PRIORITY attribute: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m"); r = sd_rtnl_message_append_u32(req, RTA_OIF, link->ifindex); - if (r < 0) { - log_error("Could not append RTA_OIF attribute: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not append RTA_OIF attribute: %m"); r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL); - if (r < 0) { - log_error("Could not send rtnetlink message: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not send rtnetlink message: %m"); link_ref(link); @@ -198,18 +195,16 @@ int route_configure(Route *route, Link *link, r = sd_rtnl_message_new_route(link->manager->rtnl, &req, RTM_NEWROUTE, route->family, route->protocol); - if (r < 0) { - log_error("Could not create RTM_NEWROUTE message: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not create RTM_NEWROUTE message: %m"); - if (route->family == AF_INET) - r = sd_rtnl_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in); - else if (route->family == AF_INET6) - r = sd_rtnl_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6); - if (r < 0) { - log_error("Could not append RTA_GATEWAY attribute: %s", strerror(-r)); - return r; + if (!in_addr_is_null(route->family, &route->in_addr)) { + if (route->family == AF_INET) + r = sd_rtnl_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in); + else if (route->family == AF_INET6) + r = sd_rtnl_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6); + if (r < 0) + return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m"); } if (route->dst_prefixlen) { @@ -217,16 +212,25 @@ int route_configure(Route *route, Link *link, r = sd_rtnl_message_append_in_addr(req, RTA_DST, &route->dst_addr.in); else if (route->family == AF_INET6) r = sd_rtnl_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6); - if (r < 0) { - log_error("Could not append RTA_DST attribute: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not append RTA_DST attribute: %m"); r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen); - if (r < 0) { - log_error("Could not set destination prefix length: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not set destination prefix length: %m"); + } + + if (route->src_prefixlen) { + if (route->family == AF_INET) + r = sd_rtnl_message_append_in_addr(req, RTA_SRC, &route->src_addr.in); + else if (route->family == AF_INET6) + r = sd_rtnl_message_append_in6_addr(req, RTA_SRC, &route->src_addr.in6); + if (r < 0) + return log_error_errno(r, "Could not append RTA_SRC attribute: %m"); + + r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen); + if (r < 0) + return log_error_errno(r, "Could not set source prefix length: %m"); } if (!in_addr_is_null(route->family, &route->prefsrc_addr)) { @@ -234,35 +238,25 @@ int route_configure(Route *route, Link *link, r = sd_rtnl_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in); else if (route->family == AF_INET6) r = sd_rtnl_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc_addr.in6); - if (r < 0) { - log_error("Could not append RTA_PREFSRC attribute: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m"); } r = sd_rtnl_message_route_set_scope(req, route->scope); - if (r < 0) { - log_error("Could not set scope: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not set scope: %m"); r = sd_rtnl_message_append_u32(req, RTA_PRIORITY, route->metrics); - if (r < 0) { - log_error("Could not append RTA_PRIORITY attribute: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m"); r = sd_rtnl_message_append_u32(req, RTA_OIF, link->ifindex); - if (r < 0) { - log_error("Could not append RTA_OIF attribute: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not append RTA_OIF attribute: %m"); r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL); - if (r < 0) { - log_error("Could not send rtnetlink message: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not send rtnetlink message: %m"); link_ref(link); @@ -330,6 +324,7 @@ int config_parse_destination(const char *unit, _cleanup_route_free_ Route *n = NULL; const char *address, *e; union in_addr_union buffer; + unsigned char prefixlen; int r, f; assert(filename); @@ -342,7 +337,7 @@ int config_parse_destination(const char *unit, if (r < 0) return r; - /* Destination=address/prefixlen */ + /* Destination|Source=address/prefixlen */ /* address */ e = strchr(rvalue, '/'); @@ -358,31 +353,41 @@ int config_parse_destination(const char *unit, return 0; } + if (f != AF_INET && f != AF_INET6) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Unknown address family, ignoring assignment: %s", address); + return 0; + } + /* prefixlen */ if (e) { - unsigned i; - - r = safe_atou(e + 1, &i); + r = safe_atou8(e + 1, &prefixlen); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Route destination prefix length is invalid, ignoring assignment: %s", e + 1); return 0; } - - n->dst_prefixlen = (unsigned char) i; } else { - switch (n->family) { + switch (f) { case AF_INET: - n->dst_prefixlen = 32; + prefixlen = 32; break; case AF_INET6: - n->dst_prefixlen = 128; + prefixlen = 128; break; } } n->family = f; - n->dst_addr = buffer; + if (streq(lvalue, "Destination")) { + n->dst_addr = buffer; + n->dst_prefixlen = prefixlen; + } else if (streq(lvalue, "Source")) { + n->src_addr = buffer; + n->src_prefixlen = prefixlen; + } else + assert_not_reached(lvalue); + n = NULL; return 0; diff --git a/src/network/networkd-wait-online-manager.c b/src/network/networkd-wait-online-manager.c index 6e3ec6935d..3f2b96688c 100644 --- a/src/network/networkd-wait-online-manager.c +++ b/src/network/networkd-wait-online-manager.c @@ -37,7 +37,7 @@ bool manager_all_configured(Manager *m) { char **ifname; bool one_ready = false; - /* wait for all the links given on the commandline to appear */ + /* wait for all the links given on the command line to appear */ STRV_FOREACH(ifname, m->interfaces) { l = hashmap_get(m->links_by_name, *ifname); if (!l) { @@ -134,7 +134,7 @@ static int manager_process_link(sd_rtnl *rtnl, sd_rtnl_message *mm, void *userda return 0; fail: - log_warning("Failed to process RTNL link message: %s", strerror(-r)); + log_warning_errno(r, "Failed to process RTNL link message: %m"); return 0; } @@ -211,7 +211,7 @@ static int on_network_event(sd_event_source *s, int fd, uint32_t revents, void * HASHMAP_FOREACH(l, m->links, i) { r = link_update_monitor(l); if (r < 0) - log_warning("Failed to update monitor information for %i: %s", l->ifindex, strerror(-r)); + log_warning_errno(r, "Failed to update monitor information for %i: %m", l->ifindex); } if (manager_all_configured(m)) diff --git a/src/network/networkd-wait-online.c b/src/network/networkd-wait-online.c index 714343656b..32a8d85301 100644 --- a/src/network/networkd-wait-online.c +++ b/src/network/networkd-wait-online.c @@ -113,7 +113,7 @@ int main(int argc, char *argv[]) { r = manager_new(&m, arg_interfaces); if (r < 0) { - log_error("Could not create manager: %s", strerror(-r)); + log_error_errno(r, "Could not create manager: %m"); goto finish; } @@ -128,7 +128,7 @@ int main(int argc, char *argv[]) { r = sd_event_loop(m->event); if (r < 0) { - log_error("Event loop failed: %s", strerror(-r)); + log_error_errno(r, "Event loop failed: %m"); goto finish; } diff --git a/src/network/networkd.c b/src/network/networkd.c index fdb80368d4..0b386d4069 100644 --- a/src/network/networkd.c +++ b/src/network/networkd.c @@ -46,7 +46,7 @@ int main(int argc, char *argv[]) { r = get_user_creds(&user, &uid, &gid, NULL, NULL); if (r < 0) { - log_error("Cannot resolve user name %s: %s", user, strerror(-r)); + log_error_errno(r, "Cannot resolve user name %s: %m", user); goto out; } @@ -54,18 +54,15 @@ int main(int argc, char *argv[]) { * watches in. */ r = mkdir_safe_label("/run/systemd/netif", 0755, uid, gid); if (r < 0) - log_error("Could not create runtime directory: %s", - strerror(-r)); + log_error_errno(r, "Could not create runtime directory: %m"); r = mkdir_safe_label("/run/systemd/netif/links", 0755, uid, gid); if (r < 0) - log_error("Could not create runtime directory 'links': %s", - strerror(-r)); + log_error_errno(r, "Could not create runtime directory 'links': %m"); r = mkdir_safe_label("/run/systemd/netif/leases", 0755, uid, gid); if (r < 0) - log_error("Could not create runtime directory 'leases': %s", - strerror(-r)); + log_error_errno(r, "Could not create runtime directory 'leases': %m"); r = drop_privileges(uid, gid, (1ULL << CAP_NET_ADMIN) | @@ -79,37 +76,43 @@ int main(int argc, char *argv[]) { r = manager_new(&m); if (r < 0) { - log_error("Could not create manager: %s", strerror(-r)); + log_error_errno(r, "Could not create manager: %m"); goto out; } r = manager_udev_listen(m); if (r < 0) { - log_error("Could not connect to udev: %s", strerror(-r)); + log_error_errno(r, "Could not connect to udev: %m"); goto out; } r = manager_rtnl_listen(m); if (r < 0) { - log_error("Could not connect to rtnl: %s", strerror(-r)); + log_error_errno(r, "Could not connect to rtnl: %m"); goto out; } r = manager_bus_listen(m); if (r < 0) { - log_error("Could not connect to system bus: %s", strerror(-r)); + log_error_errno(r, "Could not connect to system bus: %m"); goto out; } r = manager_load_config(m); if (r < 0) { - log_error("Could not load configuration files: %s", strerror(-r)); + log_error_errno(r, "Could not load configuration files: %m"); goto out; } r = manager_rtnl_enumerate_links(m); if (r < 0) { - log_error("Could not enumerate links: %s", strerror(-r)); + log_error_errno(r, "Could not enumerate links: %m"); + goto out; + } + + r = manager_rtnl_enumerate_addresses(m); + if (r < 0) { + log_error_errno(r, "Could not enumerate links: %m"); goto out; } @@ -119,7 +122,7 @@ int main(int argc, char *argv[]) { r = sd_event_loop(m->event); if (r < 0) { - log_error("Event loop failed: %s", strerror(-r)); + log_error_errno(r, "Event loop failed: %m"); goto out; } diff --git a/src/network/networkd.h b/src/network/networkd.h index d4e79ab2f3..4cdcd73c5d 100644 --- a/src/network/networkd.h +++ b/src/network/networkd.h @@ -37,7 +37,7 @@ #include "hashmap.h" #include "list.h" #include "set.h" -#include "condition-util.h" +#include "condition.h" #include "in-addr-util.h" #define CACHE_INFO_INFINITY_LIFE_TIME 0xFFFFFFFFU @@ -106,6 +106,11 @@ struct Network { bool dhcp_server; + unsigned cost; + + struct ether_addr *mac; + unsigned mtu; + LIST_HEAD(Address, static_addresses); LIST_HEAD(Route, static_routes); @@ -145,12 +150,14 @@ struct Route { int family; unsigned char dst_prefixlen; + unsigned char src_prefixlen; unsigned char scope; uint32_t metrics; unsigned char protocol; /* RTPROT_* */ union in_addr_union in_addr; union in_addr_union dst_addr; + union in_addr_union src_addr; union in_addr_union prefsrc_addr; LIST_FIELDS(Route, routes); @@ -196,6 +203,7 @@ int manager_load_config(Manager *m); bool manager_should_reload(Manager *m); int manager_rtnl_enumerate_links(Manager *m); +int manager_rtnl_enumerate_addresses(Manager *m); int manager_rtnl_listen(Manager *m); int manager_udev_listen(Manager *m); @@ -259,6 +267,17 @@ int config_parse_tunnel_address(const char *unit, void *data, void *userdata); +int config_parse_vxlan_group_address(const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata); + /* gperf */ const struct ConfigPerfItem* network_network_gperf_lookup(const char *key, unsigned length); @@ -333,20 +352,3 @@ int address_pool_new_from_string(Manager *m, AddressPool **ret, int family, cons void address_pool_free(AddressPool *p); int address_pool_acquire(AddressPool *p, unsigned prefixlen, union in_addr_union *found); - -/* Macros which append INTERFACE= to the message */ - -#define log_full_link(level, link, fmt, ...) log_meta_object(level, __FILE__, __LINE__, __func__, "INTERFACE=", link->ifname, "%-*s: " fmt, IFNAMSIZ, link->ifname, ##__VA_ARGS__) -#define log_debug_link(link, ...) log_full_link(LOG_DEBUG, link, ##__VA_ARGS__) -#define log_info_link(link, ...) log_full_link(LOG_INFO, link, ##__VA_ARGS__) -#define log_notice_link(link, ...) log_full_link(LOG_NOTICE, link, ##__VA_ARGS__) -#define log_warning_link(link, ...) log_full_link(LOG_WARNING, link, ##__VA_ARGS__) -#define log_error_link(link, ...) log_full_link(LOG_ERR, link, ##__VA_ARGS__) - -#define log_struct_link(level, link, ...) log_struct(level, "INTERFACE=%s", link->ifname, __VA_ARGS__) - -#define ADDRESS_FMT_VAL(address) \ - (address).s_addr & 0xFF, \ - ((address).s_addr >> 8) & 0xFF, \ - ((address).s_addr >> 16) & 0xFF, \ - (address).s_addr >> 24 diff --git a/src/notify/notify.c b/src/notify/notify.c index a0f757a252..5bf901ec6a 100644 --- a/src/notify/notify.c +++ b/src/notify/notify.c @@ -193,7 +193,7 @@ int main(int argc, char* argv[]) { r = sd_pid_notify(arg_pid, false, n); if (r < 0) { - log_error("Failed to notify init system: %s", strerror(-r)); + log_error_errno(r, "Failed to notify init system: %m"); goto finish; } diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index b6d9bc631c..0466ddbff3 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -89,6 +89,8 @@ #include "copy.h" #include "base-filesystem.h" #include "barrier.h" +#include "event-util.h" +#include "cap-list.h" #ifdef HAVE_SECCOMP #include "seccomp-util.h" @@ -123,6 +125,7 @@ static bool arg_private_network = false; static bool arg_read_only = false; static bool arg_boot = false; static LinkJournal arg_link_journal = LINK_AUTO; +static bool arg_link_journal_try = false; static uint64_t arg_retain = (1ULL << CAP_CHOWN) | (1ULL << CAP_DAC_OVERRIDE) | @@ -201,8 +204,9 @@ static void help(void) { " --capability=CAP In addition to the default, retain specified\n" " capability\n" " --drop-capability=CAP Drop the specified capability from the default set\n" - " --link-journal=MODE Link up guest journal, one of no, auto, guest, host\n" - " -j Equivalent to --link-journal=host\n" + " --link-journal=MODE Link up guest journal, one of no, auto, guest, host,\n" + " try-guest, try-host\n" + " -j Equivalent to --link-journal=try-guest\n" " --read-only Mount the root directory read-only\n" " --bind=PATH[:PATH] Bind mount a file or directory from the host into\n" " the container\n" @@ -299,7 +303,7 @@ static int parse_argv(int argc, char *argv[]) { free(arg_directory); arg_directory = canonicalize_file_name(optarg); if (!arg_directory) { - log_error("Invalid root directory: %m"); + log_error_errno(errno, "Invalid root directory: %m"); return -ENOMEM; } @@ -398,7 +402,6 @@ static int parse_argv(int argc, char *argv[]) { FOREACH_WORD_SEPARATOR(word, length, optarg, ",", state) { _cleanup_free_ char *t; - cap_value_t cap; t = strndup(word, length); if (!t) @@ -410,7 +413,10 @@ static int parse_argv(int argc, char *argv[]) { else minus = (uint64_t) -1; } else { - if (cap_from_name(t, &cap) < 0) { + int cap; + + cap = capability_from_name(t); + if (cap < 0) { log_error("Failed to parse capability %s.", t); return -EINVAL; } @@ -427,6 +433,7 @@ static int parse_argv(int argc, char *argv[]) { case 'j': arg_link_journal = LINK_GUEST; + arg_link_journal_try = true; break; case ARG_LINK_JOURNAL: @@ -438,7 +445,13 @@ static int parse_argv(int argc, char *argv[]) { arg_link_journal = LINK_GUEST; else if (streq(optarg, "host")) arg_link_journal = LINK_HOST; - else { + else if (streq(optarg, "try-guest")) { + arg_link_journal = LINK_GUEST; + arg_link_journal_try = true; + } else if (streq(optarg, "try-host")) { + arg_link_journal = LINK_HOST; + arg_link_journal_try = true; + } else { log_error("Failed to parse link journal mode %s", optarg); return -EINVAL; } @@ -663,7 +676,7 @@ static int mount_all(const char *dest) { t = path_is_mount_point(where, true); if (t < 0) { - log_error("Failed to detect whether %s is a mount point: %s", where, strerror(-t)); + log_error_errno(t, "Failed to detect whether %s is a mount point: %m", where); if (r == 0) r = t; @@ -678,12 +691,12 @@ static int mount_all(const char *dest) { t = mkdir_p(where, 0755); if (t < 0) { if (mount_table[k].fatal) { - log_error("Failed to create directory %s: %s", where, strerror(-t)); + log_error_errno(t, "Failed to create directory %s: %m", where); if (r == 0) r = t; } else - log_warning("Failed to create directory %s: %s", where, strerror(-t)); + log_warning_errno(t, "Failed to create directory %s: %m", where); continue; } @@ -708,12 +721,12 @@ static int mount_all(const char *dest) { o) < 0) { if (mount_table[k].fatal) { - log_error("mount(%s) failed: %m", where); + log_error_errno(errno, "mount(%s) failed: %m", where); if (r == 0) r = -errno; } else - log_warning("mount(%s) failed: %m", where); + log_warning_errno(errno, "mount(%s) failed: %m", where); } } @@ -728,10 +741,8 @@ static int mount_binds(const char *dest, char **l, bool ro) { struct stat source_st, dest_st; int r; - if (stat(*x, &source_st) < 0) { - log_error("Failed to stat %s: %m", *x); - return -errno; - } + if (stat(*x, &source_st) < 0) + return log_error_errno(errno, "Failed to stat %s: %m", *x); where = strappend(dest, *y); if (!where) @@ -745,12 +756,10 @@ static int mount_binds(const char *dest, char **l, bool ro) { } } else if (errno == ENOENT) { r = mkdir_parents_label(where, 0755); - if (r < 0) { - log_error("Failed to bind mount %s: %s", *x, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to bind mount %s: %m", *x); } else { - log_error("Failed to bind mount %s: %m", *x); + log_error_errno(errno, "Failed to bind mount %s: %m", *x); return -errno; } @@ -758,48 +767,32 @@ static int mount_binds(const char *dest, char **l, bool ro) { * and char devices. */ if (S_ISDIR(source_st.st_mode)) { r = mkdir_label(where, 0755); - if (r < 0) { - log_error("Failed to create mount point %s: %s", where, strerror(-r)); - - return r; - } + if (r < 0 && errno != EEXIST) + return log_error_errno(r, "Failed to create mount point %s: %m", where); } else if (S_ISFIFO(source_st.st_mode)) { r = mkfifo(where, 0644); - if (r < 0 && errno != EEXIST) { - log_error("Failed to create mount point %s: %m", where); - - return -errno; - } + if (r < 0 && errno != EEXIST) + return log_error_errno(errno, "Failed to create mount point %s: %m", where); } else if (S_ISSOCK(source_st.st_mode)) { r = mknod(where, 0644 | S_IFSOCK, 0); - if (r < 0 && errno != EEXIST) { - log_error("Failed to create mount point %s: %m", where); - - return -errno; - } + if (r < 0 && errno != EEXIST) + return log_error_errno(errno, "Failed to create mount point %s: %m", where); } else if (S_ISREG(source_st.st_mode)) { r = touch(where); - if (r < 0) { - log_error("Failed to create mount point %s: %s", where, strerror(-r)); - - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to create mount point %s: %m", where); } else { log_error("Refusing to create mountpoint for file: %s", *x); return -ENOTSUP; } - if (mount(*x, where, "bind", MS_BIND, NULL) < 0) { - log_error("mount(%s) failed: %m", where); - return -errno; - } + if (mount(*x, where, "bind", MS_BIND, NULL) < 0) + return log_error_errno(errno, "mount(%s) failed: %m", where); if (ro) { r = bind_remount_recursive(where, true); - if (r < 0) { - log_error("Read-Only bind mount failed: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Read-Only bind mount failed: %m"); } } @@ -818,16 +811,11 @@ static int mount_tmpfs(const char *dest) { return log_oom(); r = mkdir_label(where, 0755); - if (r < 0) { - log_error("creating mount point for tmpfs %s failed: %s", where, strerror(-r)); - - return r; - } + if (r < 0 && r != -EEXIST) + return log_error_errno(r, "Creating mount point for tmpfs %s failed: %m", where); - if (mount("tmpfs", where, "tmpfs", MS_NODEV|MS_STRICTATIME, *o) < 0) { - log_error("tmpfs mount to %s failed: %m", where); - return -errno; - } + if (mount("tmpfs", where, "tmpfs", MS_NODEV|MS_STRICTATIME, *o) < 0) + return log_error_errno(errno, "tmpfs mount to %s failed: %m", where); } return 0; @@ -885,20 +873,20 @@ static int setup_timezone(const char *dest) { r = mkdir_parents(where, 0755); if (r < 0) { - log_error("Failed to create directory for timezone info %s in container: %s", where, strerror(-r)); + log_error_errno(r, "Failed to create directory for timezone info %s in container: %m", where); return 0; } r = unlink(where); if (r < 0 && errno != ENOENT) { - log_error("Failed to remove existing timezone info %s in container: %m", where); + log_error_errno(errno, "Failed to remove existing timezone info %s in container: %m", where); return 0; } if (symlink(what, where) < 0) { - log_error("Failed to correct timezone of container: %m"); + log_error_errno(errno, "Failed to correct timezone of container: %m"); return 0; } @@ -923,14 +911,14 @@ static int setup_resolv_conf(const char *dest) { * fails, it fails, but meh... */ r = mkdir_parents(where, 0755); if (r < 0) { - log_warning("Failed to create parent directory for resolv.conf %s: %s", where, strerror(-r)); + log_warning_errno(r, "Failed to create parent directory for resolv.conf %s: %m", where); return 0; } r = copy_file("/etc/resolv.conf", where, O_TRUNC|O_NOFOLLOW, 0644); if (r < 0) { - log_warning("Failed to copy /etc/resolv.conf to %s: %s", where, strerror(-r)); + log_warning_errno(r, "Failed to copy /etc/resolv.conf to %s: %m", where); return 0; } @@ -951,22 +939,16 @@ static int setup_volatile_state(const char *directory) { with a tmpfs, and the rest read-only. */ r = bind_remount_recursive(directory, true); - if (r < 0) { - log_error("Failed to remount %s read-only: %s", directory, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to remount %s read-only: %m", directory); p = strappenda(directory, "/var"); r = mkdir(p, 0755); - if (r < 0 && errno != EEXIST) { - log_error("Failed to create %s: %m", directory); - return -errno; - } + if (r < 0 && errno != EEXIST) + return log_error_errno(errno, "Failed to create %s: %m", directory); - if (mount("tmpfs", p, "tmpfs", MS_STRICTATIME, "mode=755") < 0) { - log_error("Failed to mount tmpfs to /var: %m"); - return -errno; - } + if (mount("tmpfs", p, "tmpfs", MS_STRICTATIME, "mode=755") < 0) + return log_error_errno(errno, "Failed to mount tmpfs to /var: %m"); return 0; } @@ -985,13 +967,11 @@ static int setup_volatile(const char *directory) { /* --volatile=yes means we mount a tmpfs to the root dir, and the original /usr to use inside it, and that read-only. */ - if (!mkdtemp(template)) { - log_error("Failed to create temporary directory: %m"); - return -errno; - } + if (!mkdtemp(template)) + return log_error_errno(errno, "Failed to create temporary directory: %m"); if (mount("tmpfs", template, "tmpfs", MS_STRICTATIME, "mode=755") < 0) { - log_error("Failed to mount tmpfs for root directory: %m"); + log_error_errno(errno, "Failed to mount tmpfs for root directory: %m"); r = -errno; goto fail; } @@ -1003,13 +983,13 @@ static int setup_volatile(const char *directory) { r = mkdir(t, 0755); if (r < 0 && errno != EEXIST) { - log_error("Failed to create %s: %m", t); + log_error_errno(errno, "Failed to create %s: %m", t); r = -errno; goto fail; } if (mount(f, t, "bind", MS_BIND|MS_REC, NULL) < 0) { - log_error("Failed to create /usr bind mount: %m"); + log_error_errno(errno, "Failed to create /usr bind mount: %m"); r = -errno; goto fail; } @@ -1018,12 +998,12 @@ static int setup_volatile(const char *directory) { r = bind_remount_recursive(t, true); if (r < 0) { - log_error("Failed to remount %s read-only: %s", t, strerror(-r)); + log_error_errno(r, "Failed to remount %s read-only: %m", t); goto fail; } if (mount(template, directory, NULL, MS_MOVE, NULL) < 0) { - log_error("Failed to move root mount: %m"); + log_error_errno(errno, "Failed to move root mount: %m"); r = -errno; goto fail; } @@ -1070,24 +1050,20 @@ static int setup_boot_id(const char *dest) { return log_oom(); r = sd_id128_randomize(&rnd); - if (r < 0) { - log_error("Failed to generate random boot id: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to generate random boot id: %m"); id128_format_as_uuid(rnd, as_uuid); r = write_string_file(from, as_uuid); - if (r < 0) { - log_error("Failed to write boot id: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to write boot id: %m"); if (mount(from, to, "bind", MS_BIND, NULL) < 0) { - log_error("Failed to bind mount boot id: %m"); + log_error_errno(errno, "Failed to bind mount boot id: %m"); r = -errno; } else if (mount(from, to, "bind", MS_BIND|MS_REMOUNT|MS_RDONLY, NULL)) - log_warning("Failed to make boot id read-only: %m"); + log_warning_errno(errno, "Failed to make boot id read-only: %m"); unlink(from); return r; @@ -1123,10 +1099,8 @@ static int copy_devnodes(const char *dest) { if (stat(from, &st) < 0) { - if (errno != ENOENT) { - log_error("Failed to stat %s: %m", from); - return -errno; - } + if (errno != ENOENT) + return log_error_errno(errno, "Failed to stat %s: %m", from); } else if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) { @@ -1136,14 +1110,12 @@ static int copy_devnodes(const char *dest) { } else { r = mkdir_parents(to, 0775); if (r < 0) { - log_error("Failed to create parent directory of %s: %s", to, strerror(-r)); + log_error_errno(r, "Failed to create parent directory of %s: %m", to); return -r; } - if (mknod(to, st.st_mode, st.st_rdev) < 0) { - log_error("mknod(%s) failed: %m", dest); - return -errno; - } + if (mknod(to, st.st_mode, st.st_rdev) < 0) + return log_error_errno(errno, "mknod(%s) failed: %m", dest); } } @@ -1157,10 +1129,8 @@ static int setup_ptmx(const char *dest) { if (!p) return log_oom(); - if (symlink("pts/ptmx", p) < 0) { - log_error("Failed to create /dev/ptmx symlink: %m"); - return -errno; - } + if (symlink("pts/ptmx", p) < 0) + return log_error_errno(errno, "Failed to create /dev/ptmx symlink: %m"); return 0; } @@ -1176,16 +1146,12 @@ static int setup_dev_console(const char *dest, const char *console) { u = umask(0000); - if (stat("/dev/null", &st) < 0) { - log_error("Failed to stat /dev/null: %m"); - return -errno; - } + if (stat("/dev/null", &st) < 0) + return log_error_errno(errno, "Failed to stat /dev/null: %m"); r = chmod_and_chown(console, 0600, 0, 0); - if (r < 0) { - log_error("Failed to correct access mode for TTY: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to correct access mode for TTY: %m"); /* We need to bind mount the right tty to /dev/console since * ptys can only exist on pts file systems. To have something @@ -1196,15 +1162,11 @@ static int setup_dev_console(const char *dest, const char *console) { * matter here, since we mount it over anyway). */ to = strappenda(dest, "/dev/console"); - if (mknod(to, (st.st_mode & ~07777) | 0600, st.st_rdev) < 0) { - log_error("mknod() for /dev/console failed: %m"); - return -errno; - } + if (mknod(to, (st.st_mode & ~07777) | 0600, st.st_rdev) < 0) + return log_error_errno(errno, "mknod() for /dev/console failed: %m"); - if (mount(console, to, "bind", MS_BIND, NULL) < 0) { - log_error("Bind mount for /dev/console failed: %m"); - return -errno; - } + if (mount(console, to, "bind", MS_BIND, NULL) < 0) + return log_error_errno(errno, "Bind mount for /dev/console failed: %m"); return 0; } @@ -1239,27 +1201,19 @@ static int setup_kmsg(const char *dest, int kmsg_socket) { asprintf(&to, "%s/proc/kmsg", dest) < 0) return log_oom(); - if (mkfifo(from, 0600) < 0) { - log_error("mkfifo() for /dev/kmsg failed: %m"); - return -errno; - } + if (mkfifo(from, 0600) < 0) + return log_error_errno(errno, "mkfifo() for /dev/kmsg failed: %m"); r = chmod_and_chown(from, 0600, 0, 0); - if (r < 0) { - log_error("Failed to correct access mode for /dev/kmsg: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to correct access mode for /dev/kmsg: %m"); - if (mount(from, to, "bind", MS_BIND, NULL) < 0) { - log_error("Bind mount for /proc/kmsg failed: %m"); - return -errno; - } + if (mount(from, to, "bind", MS_BIND, NULL) < 0) + return log_error_errno(errno, "Bind mount for /proc/kmsg failed: %m"); fd = open(from, O_RDWR|O_NDELAY|O_CLOEXEC); - if (fd < 0) { - log_error("Failed to open fifo: %m"); - return -errno; - } + if (fd < 0) + return log_error_errno(errno, "Failed to open fifo: %m"); cmsg = CMSG_FIRSTHDR(&mh); cmsg->cmsg_level = SOL_SOCKET; @@ -1274,10 +1228,8 @@ static int setup_kmsg(const char *dest, int kmsg_socket) { k = sendmsg(kmsg_socket, &mh, MSG_DONTWAIT|MSG_NOSIGNAL); safe_close(fd); - if (k < 0) { - log_error("Failed to send FIFO fd: %m"); - return -errno; - } + if (k < 0) + return log_error_errno(errno, "Failed to send FIFO fd: %m"); /* And now make the FIFO unavailable as /dev/kmsg... */ unlink(from); @@ -1308,10 +1260,8 @@ static int setup_journal(const char *directory) { r = read_one_line_file(p, &b); if (r == -ENOENT && arg_link_journal == LINK_AUTO) return 0; - else if (r < 0) { - log_error("Failed to read machine ID from %s: %s", p, strerror(-r)); - return r; - } + else if (r < 0) + return log_error_errno(r, "Failed to read machine ID from %s: %m", p); id = strstrip(b); if (isempty(id) && arg_link_journal == LINK_AUTO) @@ -1319,16 +1269,12 @@ static int setup_journal(const char *directory) { /* Verify validity */ r = sd_id128_from_string(id, &machine_id); - if (r < 0) { - log_error("Failed to parse machine ID from %s: %s", p, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to parse machine ID from %s: %m", p); r = sd_id128_get_machine(&this_id); - if (r < 0) { - log_error("Failed to retrieve machine ID: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to retrieve machine ID: %m"); if (sd_id128_equal(machine_id, this_id)) { log_full(arg_link_journal == LINK_AUTO ? LOG_WARNING : LOG_ERR, @@ -1374,14 +1320,12 @@ static int setup_journal(const char *directory) { r = mkdir_p(q, 0755); if (r < 0) - log_warning("Failed to create directory %s: %m", q); + log_warning_errno(errno, "Failed to create directory %s: %m", q); return 0; } - if (unlink(p) < 0) { - log_error("Failed to remove symlink %s: %m", p); - return -errno; - } + if (unlink(p) < 0) + return log_error_errno(errno, "Failed to remove symlink %s: %m", p); } else if (r == -EINVAL) { if (arg_link_journal == LINK_GUEST && @@ -1391,33 +1335,45 @@ static int setup_journal(const char *directory) { log_error("%s already exists and is neither a symlink nor a directory", p); return r; } else { - log_error("Failed to remove %s: %m", p); + log_error_errno(errno, "Failed to remove %s: %m", p); return -errno; } } } else if (r != -ENOENT) { - log_error("readlink(%s) failed: %m", p); + log_error_errno(errno, "readlink(%s) failed: %m", p); return r; } if (arg_link_journal == LINK_GUEST) { if (symlink(q, p) < 0) { - log_error("Failed to symlink %s to %s: %m", q, p); - return -errno; + if (arg_link_journal_try) { + log_debug_errno(errno, "Failed to symlink %s to %s, skipping journal setup: %m", q, p); + return 0; + } else { + log_error_errno(errno, "Failed to symlink %s to %s: %m", q, p); + return -errno; + } } r = mkdir_p(q, 0755); if (r < 0) - log_warning("Failed to create directory %s: %m", q); + log_warning_errno(errno, "Failed to create directory %s: %m", q); return 0; } if (arg_link_journal == LINK_HOST) { - r = mkdir_p(p, 0755); + /* don't create parents here -- if the host doesn't have + * permanent journal set up, don't force it here */ + r = mkdir(p, 0755); if (r < 0) { - log_error("Failed to create %s: %m", p); - return r; + if (arg_link_journal_try) { + log_debug_errno(errno, "Failed to create %s, skipping journal setup: %m", p); + return 0; + } else { + log_error_errno(errno, "Failed to create %s: %m", p); + return r; + } } } else if (access(p, F_OK) < 0) @@ -1428,34 +1384,12 @@ static int setup_journal(const char *directory) { r = mkdir_p(q, 0755); if (r < 0) { - log_error("Failed to create %s: %m", q); + log_error_errno(errno, "Failed to create %s: %m", q); return r; } - if (mount(p, q, "bind", MS_BIND, NULL) < 0) { - log_error("Failed to bind mount journal from host into guest: %m"); - return -errno; - } - - return 0; -} - -static int setup_kdbus(const char *dest, const char *path) { - const char *p; - - if (!path) - return 0; - - p = strappenda(dest, "/dev/kdbus"); - if (mkdir(p, 0755) < 0) { - log_error("Failed to create kdbus path: %m"); - return -errno; - } - - if (mount(path, p, "bind", MS_BIND, NULL) < 0) { - log_error("Failed to mount kdbus domain path: %m"); - return -errno; - } + if (mount(p, q, "bind", MS_BIND, NULL) < 0) + return log_error_errno(errno, "Failed to bind mount journal from host into guest: %m"); return 0; } @@ -1473,10 +1407,8 @@ static int register_machine(pid_t pid, int local_ifindex) { return 0; r = sd_bus_default_system(&bus); - if (r < 0) { - log_error("Failed to open system bus: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to open system bus: %m"); if (arg_keep_unit) { r = sd_bus_call_method( @@ -1505,10 +1437,8 @@ static int register_machine(pid_t pid, int local_ifindex) { "/org/freedesktop/machine1", "org.freedesktop.machine1.Manager", "CreateMachineWithNetwork"); - if (r < 0) { - log_error("Failed to create message: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to create message: %m"); r = sd_bus_message_append( m, @@ -1520,32 +1450,24 @@ static int register_machine(pid_t pid, int local_ifindex) { (uint32_t) pid, strempty(arg_directory), local_ifindex > 0 ? 1 : 0, local_ifindex); - if (r < 0) { - log_error("Failed to append message arguments: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to append message arguments: %m"); r = sd_bus_message_open_container(m, 'a', "(sv)"); - if (r < 0) { - log_error("Failed to open container: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to open container: %m"); if (!isempty(arg_slice)) { r = sd_bus_message_append(m, "(sv)", "Slice", "s", arg_slice); - if (r < 0) { - log_error("Failed to append slice: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to append slice: %m"); } r = sd_bus_message_append(m, "(sv)", "DevicePolicy", "s", "strict"); - if (r < 0) { - log_error("Failed to add device policy: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add device policy: %m"); - r = sd_bus_message_append(m, "(sv)", "DeviceAllow", "a(ss)", 11, + r = sd_bus_message_append(m, "(sv)", "DeviceAllow", "a(ss)", 9, /* Allow the container to * access and create the API * device nodes, so that @@ -1565,28 +1487,13 @@ static int register_machine(pid_t pid, int local_ifindex) { * container to ever create * these device nodes. */ "/dev/pts/ptmx", "rw", - "char-pts", "rw", - /* Allow the container - * access to all kdbus - * devices. Again, the - * container cannot create - * these nodes, only use - * them. We use a pretty - * open match here, so that - * the kernel API can still - * change. */ - "char-kdbus", "rw", - "char-kdbus/*", "rw"); - if (r < 0) { - log_error("Failed to add device whitelist: %s", strerror(-r)); - return r; - } + "char-pts", "rw"); + if (r < 0) + return log_error_errno(r, "Failed to add device whitelist: %m"); r = sd_bus_message_close_container(m); - if (r < 0) { - log_error("Failed to close container: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to close container: %m"); r = sd_bus_call(bus, m, 0, &error, NULL); } @@ -1610,10 +1517,8 @@ static int terminate_machine(pid_t pid) { return 0; r = sd_bus_default_system(&bus); - if (r < 0) { - log_error("Failed to open system bus: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to open system bus: %m"); r = sd_bus_call_method( bus, @@ -1664,10 +1569,8 @@ static int reset_audit_loginuid(void) { r = read_one_line_file("/proc/self/loginuid", &p); if (r == -ENOENT) return 0; - if (r < 0) { - log_error("Failed to read /proc/self/loginuid: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to read /proc/self/loginuid: %m"); /* Already reset? */ if (streq(p, "4294967295")) @@ -1689,16 +1592,19 @@ static int reset_audit_loginuid(void) { #define HOST_HASH_KEY SD_ID128_MAKE(1a,37,6f,c7,46,ec,45,0b,ad,a3,d5,31,06,60,5d,b1) #define CONTAINER_HASH_KEY SD_ID128_MAKE(c3,c4,f9,19,b5,57,b2,1c,e6,cf,14,27,03,9c,ee,a2) +#define MACVLAN_HASH_KEY SD_ID128_MAKE(00,13,6d,bc,66,83,44,81,bb,0c,f9,51,1f,24,a6,6f) -static int generate_mac(struct ether_addr *mac, sd_id128_t hash_key) { - int r; - +static int generate_mac(struct ether_addr *mac, sd_id128_t hash_key, uint64_t idx) { uint8_t result[8]; size_t l, sz; - uint8_t *v; + uint8_t *v, *i; + int r; l = strlen(arg_machine); sz = sizeof(sd_id128_t) + l; + if (idx > 0) + sz += sizeof(idx); + v = alloca(sz); /* fetch some persistent data unique to the host */ @@ -1708,7 +1614,11 @@ static int generate_mac(struct ether_addr *mac, sd_id128_t hash_key) { /* combine with some data unique (on this host) to this * container instance */ - memcpy(v + sizeof(sd_id128_t), arg_machine, l); + i = mempcpy(v + sizeof(sd_id128_t), arg_machine, l); + if (idx > 0) { + idx = htole64(idx); + memcpy(i, &idx, sizeof(idx)); + } /* Let's hash the host machine ID plus the container name. We * use a fixed, but originally randomly created hash key here. */ @@ -1741,107 +1651,73 @@ static int setup_veth(pid_t pid, char iface_name[IFNAMSIZ], int *ifi) { snprintf(iface_name, IFNAMSIZ - 1, "%s-%s", arg_network_bridge ? "vb" : "ve", arg_machine); - r = generate_mac(&mac_container, CONTAINER_HASH_KEY); - if (r < 0) { - log_error("Failed to generate predictable MAC address for container side"); - return r; - } + r = generate_mac(&mac_container, CONTAINER_HASH_KEY, 0); + if (r < 0) + return log_error_errno(r, "Failed to generate predictable MAC address for container side: %m"); - r = generate_mac(&mac_host, HOST_HASH_KEY); - if (r < 0) { - log_error("Failed to generate predictable MAC address for host side"); - return r; - } + r = generate_mac(&mac_host, HOST_HASH_KEY, 0); + if (r < 0) + return log_error_errno(r, "Failed to generate predictable MAC address for host side: %m"); r = sd_rtnl_open(&rtnl, 0); - if (r < 0) { - log_error("Failed to connect to netlink: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0); - if (r < 0) { - log_error("Failed to allocate netlink message: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to allocate netlink message: %m"); r = sd_rtnl_message_append_string(m, IFLA_IFNAME, iface_name); - if (r < 0) { - log_error("Failed to add netlink interface name: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add netlink interface name: %m"); r = sd_rtnl_message_append_ether_addr(m, IFLA_ADDRESS, &mac_host); - if (r < 0) { - log_error("Failed to add netlink MAC address: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add netlink MAC address: %m"); r = sd_rtnl_message_open_container(m, IFLA_LINKINFO); - if (r < 0) { - log_error("Failed to open netlink container: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to open netlink container: %m"); r = sd_rtnl_message_open_container_union(m, IFLA_INFO_DATA, "veth"); - if (r < 0) { - log_error("Failed to open netlink container: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to open netlink container: %m"); r = sd_rtnl_message_open_container(m, VETH_INFO_PEER); - if (r < 0) { - log_error("Failed to open netlink container: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to open netlink container: %m"); r = sd_rtnl_message_append_string(m, IFLA_IFNAME, "host0"); - if (r < 0) { - log_error("Failed to add netlink interface name: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add netlink interface name: %m"); r = sd_rtnl_message_append_ether_addr(m, IFLA_ADDRESS, &mac_container); - if (r < 0) { - log_error("Failed to add netlink MAC address: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add netlink MAC address: %m"); r = sd_rtnl_message_append_u32(m, IFLA_NET_NS_PID, pid); - if (r < 0) { - log_error("Failed to add netlink namespace field: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add netlink namespace field: %m"); r = sd_rtnl_message_close_container(m); - if (r < 0) { - log_error("Failed to close netlink container: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to close netlink container: %m"); r = sd_rtnl_message_close_container(m); - if (r < 0) { - log_error("Failed to close netlink container: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to close netlink container: %m"); r = sd_rtnl_message_close_container(m); - if (r < 0) { - log_error("Failed to close netlink container: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to close netlink container: %m"); r = sd_rtnl_call(rtnl, m, 0, NULL); - if (r < 0) { - log_error("Failed to add new veth interfaces: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add new veth interfaces: %m"); i = (int) if_nametoindex(iface_name); - if (i <= 0) { - log_error("Failed to resolve interface %s: %m", iface_name); - return -errno; - } + if (i <= 0) + return log_error_errno(errno, "Failed to resolve interface %s: %m", iface_name); *ifi = i; @@ -1863,48 +1739,34 @@ static int setup_bridge(const char veth_name[], int *ifi) { return 0; bridge = (int) if_nametoindex(arg_network_bridge); - if (bridge <= 0) { - log_error("Failed to resolve interface %s: %m", arg_network_bridge); - return -errno; - } + if (bridge <= 0) + return log_error_errno(errno, "Failed to resolve interface %s: %m", arg_network_bridge); *ifi = bridge; r = sd_rtnl_open(&rtnl, 0); - if (r < 0) { - log_error("Failed to connect to netlink: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); r = sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, 0); - if (r < 0) { - log_error("Failed to allocate netlink message: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to allocate netlink message: %m"); r = sd_rtnl_message_link_set_flags(m, IFF_UP, IFF_UP); - if (r < 0) { - log_error("Failed to set IFF_UP flag: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to set IFF_UP flag: %m"); r = sd_rtnl_message_append_string(m, IFLA_IFNAME, veth_name); - if (r < 0) { - log_error("Failed to add netlink interface name field: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add netlink interface name field: %m"); r = sd_rtnl_message_append_u32(m, IFLA_MASTER, bridge); - if (r < 0) { - log_error("Failed to add netlink master field: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add netlink master field: %m"); r = sd_rtnl_call(rtnl, m, 0, NULL); - if (r < 0) { - log_error("Failed to add veth interface to bridge: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add veth interface to bridge: %m"); return 0; } @@ -1915,17 +1777,13 @@ static int parse_interface(struct udev *udev, const char *name) { int ifi; ifi = (int) if_nametoindex(name); - if (ifi <= 0) { - log_error("Failed to resolve interface %s: %m", name); - return -errno; - } + if (ifi <= 0) + return log_error_errno(errno, "Failed to resolve interface %s: %m", name); sprintf(ifi_str, "n%i", ifi); d = udev_device_new_from_device_id(udev, ifi_str); - if (!d) { - log_error("Failed to get udev device for interface %s: %m", name); - return -errno; - } + if (!d) + return log_error_errno(errno, "Failed to get udev device for interface %s: %m", name); if (udev_device_get_is_initialized(d) <= 0) { log_error("Network interface %s is not initialized yet.", name); @@ -1948,10 +1806,8 @@ static int move_network_interfaces(pid_t pid) { return 0; r = sd_rtnl_open(&rtnl, 0); - if (r < 0) { - log_error("Failed to connect to netlink: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); udev = udev_new(); if (!udev) { @@ -1968,22 +1824,16 @@ static int move_network_interfaces(pid_t pid) { return ifi; r = sd_rtnl_message_new_link(rtnl, &m, RTM_SETLINK, ifi); - if (r < 0) { - log_error("Failed to allocate netlink message: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to allocate netlink message: %m"); r = sd_rtnl_message_append_u32(m, IFLA_NET_NS_PID, pid); - if (r < 0) { - log_error("Failed to append namespace PID to netlink message: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to append namespace PID to netlink message: %m"); r = sd_rtnl_call(rtnl, m, 0, NULL); - if (r < 0) { - log_error("Failed to move interface %s to namespace: %s", *i, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to move interface %s to namespace: %m", *i); } return 0; @@ -1992,6 +1842,7 @@ static int move_network_interfaces(pid_t pid) { static int setup_macvlan(pid_t pid) { _cleanup_udev_unref_ struct udev *udev = NULL; _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL; + unsigned idx = 0; char **i; int r; @@ -2002,10 +1853,8 @@ static int setup_macvlan(pid_t pid) { return 0; r = sd_rtnl_open(&rtnl, 0); - if (r < 0) { - log_error("Failed to connect to netlink: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to connect to netlink: %m"); udev = udev_new(); if (!udev) { @@ -2016,23 +1865,24 @@ static int setup_macvlan(pid_t pid) { STRV_FOREACH(i, arg_network_macvlan) { _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL; _cleanup_free_ char *n = NULL; + struct ether_addr mac; int ifi; ifi = parse_interface(udev, *i); if (ifi < 0) return ifi; + r = generate_mac(&mac, MACVLAN_HASH_KEY, idx++); + if (r < 0) + return log_error_errno(r, "Failed to create MACVLAN MAC address: %m"); + r = sd_rtnl_message_new_link(rtnl, &m, RTM_NEWLINK, 0); - if (r < 0) { - log_error("Failed to allocate netlink message: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to allocate netlink message: %m"); r = sd_rtnl_message_append_u32(m, IFLA_LINK, ifi); - if (r < 0) { - log_error("Failed to add netlink interface index: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add netlink interface index: %m"); n = strappend("mv-", *i); if (!n) @@ -2041,52 +1891,40 @@ static int setup_macvlan(pid_t pid) { strshorten(n, IFNAMSIZ-1); r = sd_rtnl_message_append_string(m, IFLA_IFNAME, n); - if (r < 0) { - log_error("Failed to add netlink interface name: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add netlink interface name: %m"); + + r = sd_rtnl_message_append_ether_addr(m, IFLA_ADDRESS, &mac); + if (r < 0) + return log_error_errno(r, "Failed to add netlink MAC address: %m"); r = sd_rtnl_message_append_u32(m, IFLA_NET_NS_PID, pid); - if (r < 0) { - log_error("Failed to add netlink namespace field: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add netlink namespace field: %m"); r = sd_rtnl_message_open_container(m, IFLA_LINKINFO); - if (r < 0) { - log_error("Failed to open netlink container: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to open netlink container: %m"); r = sd_rtnl_message_open_container_union(m, IFLA_INFO_DATA, "macvlan"); - if (r < 0) { - log_error("Failed to open netlink container: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to open netlink container: %m"); r = sd_rtnl_message_append_u32(m, IFLA_MACVLAN_MODE, MACVLAN_MODE_BRIDGE); - if (r < 0) { - log_error("Failed to append macvlan mode: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to append macvlan mode: %m"); r = sd_rtnl_message_close_container(m); - if (r < 0) { - log_error("Failed to close netlink container: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to close netlink container: %m"); r = sd_rtnl_message_close_container(m); - if (r < 0) { - log_error("Failed to close netlink container: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to close netlink container: %m"); r = sd_rtnl_call(rtnl, m, 0, NULL); - if (r < 0) { - log_error("Failed to add new macvlan interfaces: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add new macvlan interfaces: %m"); } return 0; @@ -2117,7 +1955,7 @@ static int setup_seccomp(void) { r = seccomp_add_secondary_archs(seccomp); if (r < 0) { - log_error("Failed to add secondary archs to seccomp filter: %s", strerror(-r)); + log_error_errno(r, "Failed to add secondary archs to seccomp filter: %m"); goto finish; } @@ -2126,7 +1964,7 @@ static int setup_seccomp(void) { if (r == -EFAULT) continue; /* unknown syscall */ if (r < 0) { - log_error("Failed to block syscall: %s", strerror(-r)); + log_error_errno(r, "Failed to block syscall: %m"); goto finish; } } @@ -2149,19 +1987,19 @@ static int setup_seccomp(void) { SCMP_A0(SCMP_CMP_EQ, AF_NETLINK), SCMP_A2(SCMP_CMP_EQ, NETLINK_AUDIT)); if (r < 0) { - log_error("Failed to add audit seccomp rule: %s", strerror(-r)); + log_error_errno(r, "Failed to add audit seccomp rule: %m"); goto finish; } r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0); if (r < 0) { - log_error("Failed to unset NO_NEW_PRIVS: %s", strerror(-r)); + log_error_errno(r, "Failed to unset NO_NEW_PRIVS: %m"); goto finish; } r = seccomp_load(seccomp); if (r < 0) - log_error("Failed to install seccomp audit filter: %s", strerror(-r)); + log_error_errno(r, "Failed to install seccomp audit filter: %m"); finish: seccomp_release(seccomp); @@ -2185,15 +2023,11 @@ static int setup_image(char **device_path, int *loop_nr) { assert(loop_nr); fd = open(arg_image, O_CLOEXEC|(arg_read_only ? O_RDONLY : O_RDWR)|O_NONBLOCK|O_NOCTTY); - if (fd < 0) { - log_error("Failed to open %s: %m", arg_image); - return -errno; - } + if (fd < 0) + return log_error_errno(errno, "Failed to open %s: %m", arg_image); - if (fstat(fd, &st) < 0) { - log_error("Failed to stat %s: %m", arg_image); - return -errno; - } + if (fstat(fd, &st) < 0) + return log_error_errno(errno, "Failed to stat %s: %m", arg_image); if (S_ISBLK(st.st_mode)) { char *p; @@ -2213,43 +2047,33 @@ static int setup_image(char **device_path, int *loop_nr) { } if (!S_ISREG(st.st_mode)) { - log_error("%s is not a regular file or block device: %m", arg_image); + log_error_errno(errno, "%s is not a regular file or block device: %m", arg_image); return -EINVAL; } control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); - if (control < 0) { - log_error("Failed to open /dev/loop-control: %m"); - return -errno; - } + if (control < 0) + return log_error_errno(errno, "Failed to open /dev/loop-control: %m"); nr = ioctl(control, LOOP_CTL_GET_FREE); - if (nr < 0) { - log_error("Failed to allocate loop device: %m"); - return -errno; - } + if (nr < 0) + return log_error_errno(errno, "Failed to allocate loop device: %m"); if (asprintf(&loopdev, "/dev/loop%i", nr) < 0) return log_oom(); loop = open(loopdev, O_CLOEXEC|(arg_read_only ? O_RDONLY : O_RDWR)|O_NONBLOCK|O_NOCTTY); - if (loop < 0) { - log_error("Failed to open loop device %s: %m", loopdev); - return -errno; - } + if (loop < 0) + return log_error_errno(errno, "Failed to open loop device %s: %m", loopdev); - if (ioctl(loop, LOOP_SET_FD, fd) < 0) { - log_error("Failed to set loopback file descriptor on %s: %m", loopdev); - return -errno; - } + if (ioctl(loop, LOOP_SET_FD, fd) < 0) + return log_error_errno(errno, "Failed to set loopback file descriptor on %s: %m", loopdev); if (arg_read_only) info.lo_flags |= LO_FLAGS_READ_ONLY; - if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0) { - log_error("Failed to set loopback settings on %s: %m", loopdev); - return -errno; - } + if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0) + return log_error_errno(errno, "Failed to set loopback settings on %s: %m", loopdev); *device_path = loopdev; loopdev = NULL; @@ -2270,7 +2094,14 @@ static int dissect_image( bool *secondary) { #ifdef HAVE_BLKID - int home_nr = -1, root_nr = -1, secondary_root_nr = -1, srv_nr = -1; + int home_nr = -1, srv_nr = -1; +#ifdef GPT_ROOT_NATIVE + int root_nr = -1; +#endif +#ifdef GPT_ROOT_SECONDARY + int secondary_root_nr = -1; +#endif + _cleanup_free_ char *home = NULL, *root = NULL, *secondary_root = NULL, *srv = NULL; _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL; _cleanup_udev_device_unref_ struct udev_device *d = NULL; @@ -2299,7 +2130,7 @@ static int dissect_image( if (errno == 0) return log_oom(); - log_error("Failed to set device on blkid probe: %m"); + log_error_errno(errno, "Failed to set device on blkid probe: %m"); return -errno; } @@ -2315,7 +2146,7 @@ static int dissect_image( } else if (r != 0) { if (errno == 0) errno = EIO; - log_error("Failed to probe: %m"); + log_error_errno(errno, "Failed to probe: %m"); return -errno; } @@ -2340,10 +2171,8 @@ static int dissect_image( if (!udev) return log_oom(); - if (fstat(fd, &st) < 0) { - log_error("Failed to stat block device: %m"); - return -errno; - } + if (fstat(fd, &st) < 0) + return log_error_errno(errno, "Failed to stat block device: %m"); d = udev_device_new_from_devnum(udev, 'b', st.st_rdev); if (!d) @@ -2358,10 +2187,8 @@ static int dissect_image( return log_oom(); r = udev_enumerate_scan_devices(e); - if (r < 0) { - log_error("Failed to scan for partition devices of %s: %s", arg_image, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to scan for partition devices of %s: %m", arg_image); first = udev_enumerate_get_list_entry(e); udev_list_entry_foreach(item, first) { @@ -2379,7 +2206,7 @@ static int dissect_image( if (!errno) errno = ENOMEM; - log_error("Failed to get partition device of %s: %m", arg_image); + log_error_errno(errno, "Failed to get partition device of %s: %m", arg_image); return -errno; } @@ -2534,7 +2361,7 @@ static int mount_device(const char *what, const char *where, const char *directo if (!b) { if (errno == 0) return log_oom(); - log_error("Failed to allocate prober for %s: %m", what); + log_error_errno(errno, "Failed to allocate prober for %s: %m", what); return -errno; } @@ -2549,7 +2376,7 @@ static int mount_device(const char *what, const char *where, const char *directo } else if (r != 0) { if (errno == 0) errno = EIO; - log_error("Failed to probe %s: %m", what); + log_error_errno(errno, "Failed to probe %s: %m", what); return -errno; } @@ -2566,10 +2393,8 @@ static int mount_device(const char *what, const char *where, const char *directo return -ENOTSUP; } - if (mount(what, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), NULL) < 0) { - log_error("Failed to mount %s: %m", what); - return -errno; - } + if (mount(what, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), NULL) < 0) + return log_error_errno(errno, "Failed to mount %s: %m", what); return 0; #else @@ -2589,26 +2414,20 @@ static int mount_devices( if (root_device) { r = mount_device(root_device, arg_directory, NULL, root_device_rw); - if (r < 0) { - log_error("Failed to mount root directory: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to mount root directory: %m"); } if (home_device) { r = mount_device(home_device, arg_directory, "/home", home_device_rw); - if (r < 0) { - log_error("Failed to mount home directory: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to mount home directory: %m"); } if (srv_device) { r = mount_device(srv_device, arg_directory, "/srv", srv_device_rw); - if (r < 0) { - log_error("Failed to mount server data directory: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to mount server data directory: %m"); } return 0; @@ -2624,19 +2443,19 @@ static void loop_remove(int nr, int *image_fd) { if (image_fd && *image_fd >= 0) { r = ioctl(*image_fd, LOOP_CLR_FD); if (r < 0) - log_warning("Failed to close loop image: %m"); + log_warning_errno(errno, "Failed to close loop image: %m"); *image_fd = safe_close(*image_fd); } control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); if (control < 0) { - log_warning("Failed to open /dev/loop-control: %m"); + log_warning_errno(errno, "Failed to open /dev/loop-control: %m"); return; } r = ioctl(control, LOOP_CTL_REMOVE, nr); if (r < 0) - log_warning("Failed to remove loop %d: %m", nr); + log_warning_errno(errno, "Failed to remove loop %d: %m", nr); } static int spawn_getent(const char *database, const char *key, pid_t *rpid) { @@ -2647,16 +2466,13 @@ static int spawn_getent(const char *database, const char *key, pid_t *rpid) { assert(key); assert(rpid); - if (pipe2(pipe_fds, O_CLOEXEC) < 0) { - log_error("Failed to allocate pipe: %m"); - return -errno; - } + if (pipe2(pipe_fds, O_CLOEXEC) < 0) + return log_error_errno(errno, "Failed to allocate pipe: %m"); pid = fork(); - if (pid < 0) { - log_error("Failed to fork getent child: %m"); - return -errno; - } else if (pid == 0) { + if (pid < 0) + return log_error_errno(errno, "Failed to fork getent child: %m"); + else if (pid == 0) { int nullfd; char *empty_env = NULL; @@ -2715,20 +2531,14 @@ static int change_uid_gid(char **_home) { if (!arg_user || streq(arg_user, "root") || streq(arg_user, "0")) { /* Reset everything fully to 0, just in case */ - if (setgroups(0, NULL) < 0) { - log_error("setgroups() failed: %m"); - return -errno; - } + if (setgroups(0, NULL) < 0) + return log_error_errno(errno, "setgroups() failed: %m"); - if (setresgid(0, 0, 0) < 0) { - log_error("setregid() failed: %m"); - return -errno; - } + if (setresgid(0, 0, 0) < 0) + return log_error_errno(errno, "setregid() failed: %m"); - if (setresuid(0, 0, 0) < 0) { - log_error("setreuid() failed: %m"); - return -errno; - } + if (setresuid(0, 0, 0) < 0) + return log_error_errno(errno, "setreuid() failed: %m"); *_home = NULL; return 0; @@ -2751,13 +2561,13 @@ static int change_uid_gid(char **_home) { return -ESRCH; } - log_error("Failed to read from getent: %m"); + log_error_errno(errno, "Failed to read from getent: %m"); return -errno; } truncate_nl(line); - wait_for_terminate_and_warn("getent passwd", pid); + wait_for_terminate_and_warn("getent passwd", pid, true); x = strchr(line, ':'); if (!x) { @@ -2835,13 +2645,13 @@ static int change_uid_gid(char **_home) { return -ESRCH; } - log_error("Failed to read from getent: %m"); + log_error_errno(errno, "Failed to read from getent: %m"); return -errno; } truncate_nl(line); - wait_for_terminate_and_warn("getent initgroups", pid); + wait_for_terminate_and_warn("getent initgroups", pid, true); /* Skip over the username and subsequent separator whitespace */ x = line; @@ -2865,35 +2675,25 @@ static int change_uid_gid(char **_home) { } r = mkdir_parents(home, 0775); - if (r < 0) { - log_error("Failed to make home root directory: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to make home root directory: %m"); r = mkdir_safe(home, 0755, uid, gid); - if (r < 0 && r != -EEXIST) { - log_error("Failed to make home directory: %s", strerror(-r)); - return r; - } + if (r < 0 && r != -EEXIST) + return log_error_errno(r, "Failed to make home directory: %m"); fchown(STDIN_FILENO, uid, gid); fchown(STDOUT_FILENO, uid, gid); fchown(STDERR_FILENO, uid, gid); - if (setgroups(n_uids, uids) < 0) { - log_error("Failed to set auxiliary groups: %m"); - return -errno; - } + if (setgroups(n_uids, uids) < 0) + return log_error_errno(errno, "Failed to set auxiliary groups: %m"); - if (setresgid(gid, gid, gid) < 0) { - log_error("setregid() failed: %m"); - return -errno; - } + if (setresgid(gid, gid, gid) < 0) + return log_error_errno(errno, "setregid() failed: %m"); - if (setresuid(uid, uid, uid) < 0) { - log_error("setreuid() failed: %m"); - return -errno; - } + if (setresuid(uid, uid, uid) < 0) + return log_error_errno(errno, "setreuid() failed: %m"); if (_home) { *_home = home; @@ -2911,8 +2711,8 @@ static int change_uid_gid(char **_home) { * container argument. * > 0 : The program executed in the container terminated with an * error. The exit code of the program executed in the - * container is returned. No change is made to the container - * argument. + * container is returned. The container argument has been set + * to CONTAINER_TERMINATED. * 0 : The container is being rebooted, has been shut down or exited * successfully. The container argument has been set to either * CONTAINER_TERMINATED or CONTAINER_REBOOTED. @@ -2921,61 +2721,48 @@ static int change_uid_gid(char **_home) { * error is indicated by a non-zero value. */ static int wait_for_container(pid_t pid, ContainerStatus *container) { - int r; siginfo_t status; + int r; r = wait_for_terminate(pid, &status); - if (r < 0) { - log_warning("Failed to wait for container: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_warning_errno(r, "Failed to wait for container: %m"); switch (status.si_code) { + case CLD_EXITED: - r = status.si_status; - if (r == 0) { - if (!arg_quiet) - log_debug("Container %s exited successfully.", - arg_machine); + if (status.si_status == 0) { + log_full(arg_quiet ? LOG_DEBUG : LOG_INFO, "Container %s exited successfully.", arg_machine); - *container = CONTAINER_TERMINATED; - } else { - log_error("Container %s failed with error code %i.", - arg_machine, status.si_status); - } - break; + } else + log_full(arg_quiet ? LOG_DEBUG : LOG_INFO, "Container %s failed with error code %i.", arg_machine, status.si_status); + + *container = CONTAINER_TERMINATED; + return status.si_status; case CLD_KILLED: if (status.si_status == SIGINT) { - if (!arg_quiet) - log_info("Container %s has been shut down.", - arg_machine); + log_full(arg_quiet ? LOG_DEBUG : LOG_INFO, "Container %s has been shut down.", arg_machine); *container = CONTAINER_TERMINATED; - r = 0; - break; + return 0; + } else if (status.si_status == SIGHUP) { - if (!arg_quiet) - log_info("Container %s is being rebooted.", - arg_machine); + log_full(arg_quiet ? LOG_DEBUG : LOG_INFO, "Container %s is being rebooted.", arg_machine); *container = CONTAINER_REBOOTED; - r = 0; - break; + return 0; } + /* CLD_KILLED fallthrough */ case CLD_DUMPED: - log_error("Container %s terminated by signal %s.", - arg_machine, signal_to_string(status.si_status)); - r = -1; - break; + log_error("Container %s terminated by signal %s.", arg_machine, signal_to_string(status.si_status)); + return -EIO; default: - log_error("Container %s failed due to unknown reason.", - arg_machine); - r = -1; - break; + log_error("Container %s failed due to unknown reason.", arg_machine); + return -EIO; } return r; @@ -2983,11 +2770,27 @@ static int wait_for_container(pid_t pid, ContainerStatus *container) { static void nop_handler(int sig) {} +static int on_orderly_shutdown(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) { + pid_t pid; + + pid = PTR_TO_UINT32(userdata); + if (pid > 0) { + if (kill(pid, SIGRTMIN+3) >= 0) { + log_info("Trying to halt container. Send SIGTERM again to trigger immediate termination."); + sd_event_source_set_userdata(s, NULL); + return 0; + } + } + + sd_event_exit(sd_event_source_get_event(s), 0); + return 0; +} + int main(int argc, char *argv[]) { - _cleanup_free_ char *kdbus_domain = NULL, *device_path = NULL, *root_device = NULL, *home_device = NULL, *srv_device = NULL; + _cleanup_free_ char *device_path = NULL, *root_device = NULL, *home_device = NULL, *srv_device = NULL; bool root_device_rw = true, home_device_rw = true, srv_device_rw = true; - _cleanup_close_ int master = -1, kdbus_fd = -1, image_fd = -1; + _cleanup_close_ int master = -1, image_fd = -1; _cleanup_close_pair_ int kmsg_socket_pair[2] = { -1, -1 }; _cleanup_fdset_free_ FDSet *fds = NULL; int r = EXIT_FAILURE, k, n_fd_passed, loop_nr = -1; @@ -3054,7 +2857,7 @@ int main(int argc, char *argv[]) { if (n_fd_passed > 0) { k = fdset_new_listen_fds(&fds, false); if (k < 0) { - log_error("Failed to collect file descriptors: %s", strerror(-k)); + log_error_errno(k, "Failed to collect file descriptors: %m"); goto finish; } } @@ -3087,7 +2890,7 @@ int main(int argc, char *argv[]) { char template[] = "/tmp/nspawn-root-XXXXXX"; if (!mkdtemp(template)) { - log_error("Failed to create temporary directory: %m"); + log_error_errno(errno, "Failed to create temporary directory: %m"); r = -errno; goto finish; } @@ -3115,13 +2918,13 @@ int main(int argc, char *argv[]) { master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY); if (master < 0) { - log_error("Failed to acquire pseudo tty: %m"); + log_error_errno(errno, "Failed to acquire pseudo tty: %m"); goto finish; } console = ptsname(master); if (!console) { - log_error("Failed to determine tty name: %m"); + log_error_errno(errno, "Failed to determine tty name: %m"); goto finish; } @@ -3130,32 +2933,12 @@ int main(int argc, char *argv[]) { arg_machine, arg_image ? arg_image : arg_directory); if (unlockpt(master) < 0) { - log_error("Failed to unlock tty: %m"); + log_error_errno(errno, "Failed to unlock tty: %m"); goto finish; } - if (access("/dev/kdbus/control", F_OK) >= 0) { - - if (arg_share_system) { - kdbus_domain = strdup("/dev/kdbus"); - if (!kdbus_domain) { - log_oom(); - goto finish; - } - } else { - const char *ns; - - ns = strappenda("machine-", arg_machine); - kdbus_fd = bus_kernel_create_domain(ns, &kdbus_domain); - if (r < 0) - log_debug("Failed to create kdbus domain: %s", strerror(-r)); - else - log_debug("Successfully created kdbus domain as %s", kdbus_domain); - } - } - if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0, kmsg_socket_pair) < 0) { - log_error("Failed to create kmsg socket pair: %m"); + log_error_errno(errno, "Failed to create kmsg socket pair: %m"); goto finish; } @@ -3164,11 +2947,12 @@ int main(int argc, char *argv[]) { "STATUS=Container running."); assert_se(sigemptyset(&mask) == 0); - assert_se(sigemptyset(&mask_chld) == 0); - sigaddset(&mask_chld, SIGCHLD); sigset_add_many(&mask, SIGCHLD, SIGWINCH, SIGTERM, SIGINT, -1); assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0); + assert_se(sigemptyset(&mask_chld) == 0); + assert_se(sigaddset(&mask_chld, SIGCHLD) == 0); + for (;;) { ContainerStatus container_status; _cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL; @@ -3179,7 +2963,7 @@ int main(int argc, char *argv[]) { r = barrier_create(&barrier); if (r < 0) { - log_error("Cannot initialize IPC barrier: %s", strerror(-r)); + log_error_errno(r, "Cannot initialize IPC barrier: %m"); goto finish; } @@ -3188,13 +2972,13 @@ int main(int argc, char *argv[]) { * give it a chance to call wait() and terminate. */ r = sigprocmask(SIG_UNBLOCK, &mask_chld, NULL); if (r < 0) { - log_error("Failed to change the signal mask: %m"); + log_error_errno(errno, "Failed to change the signal mask: %m"); goto finish; } r = sigaction(SIGCHLD, &sa, NULL); if (r < 0) { - log_error("Failed to install SIGCHLD handler: %m"); + log_error_errno(errno, "Failed to install SIGCHLD handler: %m"); goto finish; } @@ -3203,9 +2987,9 @@ int main(int argc, char *argv[]) { (arg_private_network ? CLONE_NEWNET : 0), NULL); if (pid < 0) { if (errno == EINVAL) - log_error("clone() failed, do you have namespace support enabled in your kernel? (You need UTS, IPC, PID and NET namespacing built in): %m"); + log_error_errno(errno, "clone() failed, do you have namespace support enabled in your kernel? (You need UTS, IPC, PID and NET namespacing built in): %m"); else - log_error("clone() failed: %m"); + log_error_errno(errno, "clone() failed: %m"); r = pid; goto finish; @@ -3253,18 +3037,18 @@ int main(int argc, char *argv[]) { k = -EINVAL; } - log_error("Failed to open console: %s", strerror(-k)); + log_error_errno(k, "Failed to open console: %m"); _exit(EXIT_FAILURE); } if (dup2(STDIN_FILENO, STDOUT_FILENO) != STDOUT_FILENO || dup2(STDIN_FILENO, STDERR_FILENO) != STDERR_FILENO) { - log_error("Failed to duplicate console: %m"); + log_error_errno(errno, "Failed to duplicate console: %m"); _exit(EXIT_FAILURE); } if (setsid() < 0) { - log_error("setsid() failed: %m"); + log_error_errno(errno, "setsid() failed: %m"); _exit(EXIT_FAILURE); } @@ -3272,7 +3056,7 @@ int main(int argc, char *argv[]) { _exit(EXIT_FAILURE); if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0) { - log_error("PR_SET_PDEATHSIG failed: %m"); + log_error_errno(errno, "PR_SET_PDEATHSIG failed: %m"); _exit(EXIT_FAILURE); } @@ -3280,7 +3064,7 @@ int main(int argc, char *argv[]) { * receive mounts from the real root, but don't * propagate mounts to the real root. */ if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) { - log_error("MS_SLAVE|MS_REC failed: %m"); + log_error_errno(errno, "MS_SLAVE|MS_REC failed: %m"); _exit(EXIT_FAILURE); } @@ -3292,7 +3076,7 @@ int main(int argc, char *argv[]) { /* Turn directory into bind mount */ if (mount(arg_directory, arg_directory, "bind", MS_BIND|MS_REC, NULL) < 0) { - log_error("Failed to make bind mount: %m"); + log_error_errno(errno, "Failed to make bind mount: %m"); _exit(EXIT_FAILURE); } @@ -3310,7 +3094,7 @@ int main(int argc, char *argv[]) { if (arg_read_only) { k = bind_remount_recursive(arg_directory, true); if (k < 0) { - log_error("Failed to make tree read-only: %s", strerror(-k)); + log_error_errno(k, "Failed to make tree read-only: %m"); _exit(EXIT_FAILURE); } } @@ -3358,31 +3142,28 @@ int main(int argc, char *argv[]) { if (mount_tmpfs(arg_directory) < 0) _exit(EXIT_FAILURE); - if (setup_kdbus(arg_directory, kdbus_domain) < 0) - _exit(EXIT_FAILURE); - /* Tell the parent that we are ready, and that * it can cgroupify us to that we lack access * to certain devices and resources. */ - barrier_place(&barrier); + (void)barrier_place(&barrier); if (chdir(arg_directory) < 0) { - log_error("chdir(%s) failed: %m", arg_directory); + log_error_errno(errno, "chdir(%s) failed: %m", arg_directory); _exit(EXIT_FAILURE); } if (mount(arg_directory, "/", NULL, MS_MOVE, NULL) < 0) { - log_error("mount(MS_MOVE) failed: %m"); + log_error_errno(errno, "mount(MS_MOVE) failed: %m"); _exit(EXIT_FAILURE); } if (chroot(".") < 0) { - log_error("chroot() failed: %m"); + log_error_errno(errno, "chroot() failed: %m"); _exit(EXIT_FAILURE); } if (chdir("/") < 0) { - log_error("chdir() failed: %m"); + log_error_errno(errno, "chdir() failed: %m"); _exit(EXIT_FAILURE); } @@ -3392,7 +3173,7 @@ int main(int argc, char *argv[]) { loopback_setup(); if (drop_capabilities() < 0) { - log_error("drop_capabilities() failed: %m"); + log_error_errno(errno, "drop_capabilities() failed: %m"); _exit(EXIT_FAILURE); } @@ -3434,12 +3215,12 @@ int main(int argc, char *argv[]) { if (arg_personality != 0xffffffffLU) { if (personality(arg_personality) < 0) { - log_error("personality() failed: %m"); + log_error_errno(errno, "personality() failed: %m"); _exit(EXIT_FAILURE); } } else if (secondary) { if (personality(PER_LINUX32) < 0) { - log_error("personality() failed: %m"); + log_error_errno(errno, "personality() failed: %m"); _exit(EXIT_FAILURE); } } @@ -3447,7 +3228,7 @@ int main(int argc, char *argv[]) { #ifdef HAVE_SELINUX if (arg_selinux_context) if (setexeccon((security_context_t) arg_selinux_context) < 0) { - log_error("setexeccon(\"%s\") failed: %m", arg_selinux_context); + log_error_errno(errno, "setexeccon(\"%s\") failed: %m", arg_selinux_context); _exit(EXIT_FAILURE); } #endif @@ -3495,7 +3276,7 @@ int main(int argc, char *argv[]) { execle("/bin/sh", "-sh", NULL, env_use); } - log_error("execv() failed: %m"); + log_error_errno(errno, "execv() failed: %m"); _exit(EXIT_FAILURE); } @@ -3505,6 +3286,8 @@ int main(int argc, char *argv[]) { /* wait for child-setup to be done */ if (barrier_place_and_sync(&barrier)) { + _cleanup_event_unref_ sd_event *event = NULL; + _cleanup_(pty_forward_freep) PTYForward *forward = NULL; int ifi = 0; r = move_network_interfaces(pid); @@ -3541,14 +3324,39 @@ int main(int argc, char *argv[]) { /* Notify the child that the parent is ready with all * its setup, and that the child can now hand over * control to the code to run inside the container. */ - barrier_place(&barrier); + (void)barrier_place(&barrier); - k = process_pty(master, &mask, arg_boot ? pid : 0, SIGRTMIN+3); - if (k < 0) { - r = EXIT_FAILURE; - break; + r = sd_event_new(&event); + if (r < 0) { + log_error_errno(r, "Failed to get default event source: %m"); + goto finish; } + if (arg_boot) { + /* Try to kill the init system on SIGINT or SIGTERM */ + sd_event_add_signal(event, NULL, SIGINT, on_orderly_shutdown, UINT32_TO_PTR(pid)); + sd_event_add_signal(event, NULL, SIGTERM, on_orderly_shutdown, UINT32_TO_PTR(pid)); + } else { + /* Immediately exit */ + sd_event_add_signal(event, NULL, SIGINT, NULL, NULL); + sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); + } + + /* simply exit on sigchld */ + sd_event_add_signal(event, NULL, SIGCHLD, NULL, NULL); + + r = pty_forward_new(event, master, &forward); + if (r < 0) { + log_error_errno(r, "Failed to create PTY forwarder: %m"); + goto finish; + } + + r = sd_event_loop(event); + if (r < 0) + return log_error_errno(r, "Failed to run event loop: %m"); + + forward = pty_forward_free(forward); + if (!arg_quiet) putc('\n', stdout); diff --git a/src/nss-myhostname/nss-myhostname.c b/src/nss-myhostname/nss-myhostname.c index 86e7be2aa1..aa92cc96e4 100644 --- a/src/nss-myhostname/nss-myhostname.c +++ b/src/nss-myhostname/nss-myhostname.c @@ -77,6 +77,18 @@ enum nss_status _nss_myhostname_gethostbyname4_r( canonical = "localhost"; local_address_ipv4 = htonl(INADDR_LOOPBACK); + + } else if (streq(name, "gateway")) { + + n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses); + if (n_addresses <= 0) { + *errnop = ENOENT; + *h_errnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; + } + + canonical = "gateway"; + } else { hn = gethostname_malloc(); if (!hn) { @@ -92,7 +104,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r( return NSS_STATUS_NOTFOUND; } - n_addresses = local_addresses(NULL, 0, &addresses); + n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses); if (n_addresses < 0) n_addresses = 0; @@ -314,7 +326,7 @@ enum nss_status _nss_myhostname_gethostbyname3_r( _cleanup_free_ struct local_address *addresses = NULL; const char *canonical, *additional = NULL; _cleanup_free_ char *hn = NULL; - uint32_t local_address_ipv4; + uint32_t local_address_ipv4 = 0; int n_addresses = 0; assert(name); @@ -335,6 +347,18 @@ enum nss_status _nss_myhostname_gethostbyname3_r( if (is_localhost(name)) { canonical = "localhost"; local_address_ipv4 = htonl(INADDR_LOOPBACK); + + } else if (streq(name, "gateway")) { + + n_addresses = local_gateways(NULL, 0, af, &addresses); + if (n_addresses <= 0) { + *errnop = ENOENT; + *h_errnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; + } + + canonical = "gateway"; + } else { hn = gethostname_malloc(); if (!hn) { @@ -349,7 +373,7 @@ enum nss_status _nss_myhostname_gethostbyname3_r( return NSS_STATUS_NOTFOUND; } - n_addresses = local_addresses(NULL, 0, &addresses); + n_addresses = local_addresses(NULL, 0, af, &addresses); if (n_addresses < 0) n_addresses = 0; @@ -425,16 +449,42 @@ enum nss_status _nss_myhostname_gethostbyaddr2_r( } - n_addresses = local_addresses(NULL, 0, &addresses); - if (n_addresses < 0) - n_addresses = 0; + n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses); + if (n_addresses > 0) { + for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) { + if (af != a->family) + continue; - for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) { - if (af != a->family) - continue; + if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0) { - if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0) - goto found; + hn = gethostname_malloc(); + if (!hn) { + *errnop = ENOMEM; + *h_errnop = NO_RECOVERY; + return NSS_STATUS_TRYAGAIN; + } + + canonical = hn; + goto found; + } + } + } + + free(addresses); + addresses = NULL; + + n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses); + if (n_addresses > 0) { + for (a = addresses, n = 0; (int) n < n_addresses; n++, a++) { + if (af != a->family) + continue; + + if (memcmp(addr, &a->address, FAMILY_ADDRESS_SIZE(af)) == 0) { + + canonical = "gateway"; + goto found; + } + } } *errnop = ENOENT; @@ -443,16 +493,6 @@ enum nss_status _nss_myhostname_gethostbyaddr2_r( return NSS_STATUS_NOTFOUND; found: - if (!canonical) { - hn = gethostname_malloc(); - if (!hn) { - *errnop = ENOMEM; - *h_errnop = NO_RECOVERY; - return NSS_STATUS_TRYAGAIN; - } - - canonical = hn; - } return fill_in_hostent( canonical, additional, diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c index 6a029a331b..3f32ed0650 100644 --- a/src/nss-resolve/nss-resolve.c +++ b/src/nss-resolve/nss-resolve.c @@ -33,7 +33,7 @@ #include "sd-bus.h" #include "bus-util.h" -#include "bus-errors.h" +#include "bus-common-errors.h" #include "macro.h" #include "nss-util.h" #include "util.h" diff --git a/src/path/path.c b/src/path/path.c index 37f2571fab..2f0148f074 100644 --- a/src/path/path.c +++ b/src/path/path.c @@ -88,7 +88,7 @@ static int list_homes(void) { if (q == -ENXIO) continue; if (q < 0) { - log_error("Failed to query %s: %s", path_table[i], strerror(-r)); + log_error_errno(r, "Failed to query %s: %m", path_table[i]); r = q; continue; } @@ -108,10 +108,8 @@ static int print_home(const char *n) { _cleanup_free_ char *p = NULL; r = sd_path_home(i, arg_suffix, &p); - if (r < 0) { - log_error("Failed to query %s: %s", n, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to query %s: %m", n); printf("%s\n", p); return 0; diff --git a/src/quotacheck/quotacheck.c b/src/quotacheck/quotacheck.c index ed95b48c63..9ae3abd990 100644 --- a/src/quotacheck/quotacheck.c +++ b/src/quotacheck/quotacheck.c @@ -74,6 +74,7 @@ int main(int argc, char *argv[]) { }; pid_t pid; + int r; if (argc > 1) { log_error("This program takes no arguments."); @@ -86,7 +87,10 @@ int main(int argc, char *argv[]) { umask(0022); - parse_proc_cmdline(parse_proc_cmdline_item); + r = parse_proc_cmdline(parse_proc_cmdline_item); + if (r < 0) + log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); + test_files(); if (!arg_force) { @@ -99,7 +103,7 @@ int main(int argc, char *argv[]) { pid = fork(); if (pid < 0) { - log_error("fork(): %m"); + log_error_errno(errno, "fork(): %m"); return EXIT_FAILURE; } else if (pid == 0) { /* Child */ @@ -107,5 +111,7 @@ int main(int argc, char *argv[]) { _exit(1); /* Operational error */ } - return wait_for_terminate_and_warn("quotacheck", pid) >= 0 ? EXIT_SUCCESS : EXIT_FAILURE; + r = wait_for_terminate_and_warn("quotacheck", pid, true); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/random-seed/random-seed.c b/src/random-seed/random-seed.c index af79ecf2a9..06c1239601 100644 --- a/src/random-seed/random-seed.c +++ b/src/random-seed/random-seed.c @@ -72,7 +72,7 @@ int main(int argc, char *argv[]) { r = mkdir_parents_label(RANDOM_SEED, 0755); if (r < 0) { - log_error("Failed to create directory " RANDOM_SEED_DIR ": %s", strerror(-r)); + log_error_errno(r, "Failed to create directory " RANDOM_SEED_DIR ": %m"); goto finish; } @@ -86,7 +86,7 @@ int main(int argc, char *argv[]) { if (seed_fd < 0) { seed_fd = open(RANDOM_SEED, O_RDONLY|O_CLOEXEC|O_NOCTTY); if (seed_fd < 0) { - log_error("Failed to open " RANDOM_SEED ": %m"); + log_error_errno(errno, "Failed to open " RANDOM_SEED ": %m"); r = -errno; goto finish; } @@ -96,7 +96,7 @@ int main(int argc, char *argv[]) { if (random_fd < 0) { random_fd = open("/dev/urandom", O_WRONLY|O_CLOEXEC|O_NOCTTY, 0600); if (random_fd < 0) { - log_error("Failed to open /dev/urandom: %m"); + log_error_errno(errno, "Failed to open /dev/urandom: %m"); r = -errno; goto finish; } @@ -106,33 +106,30 @@ int main(int argc, char *argv[]) { if (k <= 0) { if (r != 0) - log_error("Failed to read seed from " RANDOM_SEED ": %m"); + log_error_errno(errno, "Failed to read seed from " RANDOM_SEED ": %m"); r = k == 0 ? -EIO : (int) k; } else { lseek(seed_fd, 0, SEEK_SET); - k = loop_write(random_fd, buf, (size_t) k, false); - if (k <= 0) { - log_error("Failed to write seed to /dev/urandom: %s", r < 0 ? strerror(-r) : "short write"); - - r = k == 0 ? -EIO : (int) k; - } + r = loop_write(random_fd, buf, (size_t) k, false); + if (r < 0) + log_error_errno(r, "Failed to write seed to /dev/urandom: %m"); } } else if (streq(argv[1], "save")) { seed_fd = open(RANDOM_SEED, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600); if (seed_fd < 0) { - log_error("Failed to open " RANDOM_SEED ": %m"); + log_error_errno(errno, "Failed to open " RANDOM_SEED ": %m"); r = -errno; goto finish; } random_fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); if (random_fd < 0) { - log_error("Failed to open /dev/urandom: %m"); + log_error_errno(errno, "Failed to open /dev/urandom: %m"); r = -errno; goto finish; } @@ -155,10 +152,8 @@ int main(int argc, char *argv[]) { r = k == 0 ? -EIO : (int) k; } else { r = loop_write(seed_fd, buf, (size_t) k, false); - if (r <= 0) { - log_error("Failed to write new random seed file: %s", r < 0 ? strerror(-r) : "short write"); - r = r == 0 ? -EIO : r; - } + if (r < 0) + log_error_errno(r, "Failed to write new random seed file: %m"); } finish: diff --git a/src/rc-local-generator/rc-local-generator.c b/src/rc-local-generator/rc-local-generator.c index 01ff4702a7..d4e6ba4bf9 100644 --- a/src/rc-local-generator/rc-local-generator.c +++ b/src/rc-local-generator/rc-local-generator.c @@ -60,7 +60,7 @@ static int add_symlink(const char *service, const char *where) { if (errno == EEXIST) return 0; - log_error("Failed to create symlink %s: %m", to); + log_error_errno(errno, "Failed to create symlink %s: %m", to); return -errno; } diff --git a/src/remount-fs/remount-fs.c b/src/remount-fs/remount-fs.c index cd7cfe7a47..57b47021e4 100644 --- a/src/remount-fs/remount-fs.c +++ b/src/remount-fs/remount-fs.c @@ -60,7 +60,7 @@ int main(int argc, char *argv[]) { if (errno == ENOENT) return EXIT_SUCCESS; - log_error("Failed to open /etc/fstab: %m"); + log_error_errno(errno, "Failed to open /etc/fstab: %m"); return EXIT_FAILURE; } @@ -87,7 +87,7 @@ int main(int argc, char *argv[]) { pid = fork(); if (pid < 0) { - log_error("Failed to fork: %m"); + log_error_errno(errno, "Failed to fork: %m"); ret = EXIT_FAILURE; continue; } @@ -104,7 +104,7 @@ int main(int argc, char *argv[]) { execv("/bin/mount", (char **) arguments); - log_error("Failed to execute /bin/mount: %m"); + log_error_errno(errno, "Failed to execute /bin/mount: %m"); _exit(EXIT_FAILURE); } @@ -120,7 +120,7 @@ int main(int argc, char *argv[]) { k = hashmap_put(pids, UINT_TO_PTR(pid), s); if (k < 0) { - log_error("Failed to add PID to set: %s", strerror(-k)); + log_error_errno(k, "Failed to add PID to set: %m"); ret = EXIT_FAILURE; continue; } @@ -135,7 +135,7 @@ int main(int argc, char *argv[]) { if (errno == EINTR) continue; - log_error("waitid() failed: %m"); + log_error_errno(errno, "waitid() failed: %m"); ret = EXIT_FAILURE; break; } diff --git a/src/reply-password/reply-password.c b/src/reply-password/reply-password.c index 73c2d1bbdf..54683b6f4a 100644 --- a/src/reply-password/reply-password.c +++ b/src/reply-password/reply-password.c @@ -51,10 +51,8 @@ static int send_on_socket(int fd, const char *socket_name, const void *packet, s strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path)); - if (sendto(fd, packet, size, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)) < 0) { - log_error("Failed to send: %m"); - return -errno; - } + if (sendto(fd, packet, size, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)) < 0) + return log_error_errno(errno, "Failed to send: %m"); return 0; } @@ -77,7 +75,7 @@ int main(int argc, char *argv[]) { packet[0] = '+'; if (!fgets(packet+1, sizeof(packet)-1, stdin)) { - log_error("Failed to read password: %m"); + log_error_errno(errno, "Failed to read password: %m"); goto finish; } @@ -93,7 +91,7 @@ int main(int argc, char *argv[]) { fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (fd < 0) { - log_error("socket() failed: %m"); + log_error_errno(errno, "socket() failed: %m"); goto finish; } diff --git a/src/resolve-host/resolve-host.c b/src/resolve-host/resolve-host.c index 49049d2a25..43ecf81ef6 100644 --- a/src/resolve-host/resolve-host.c +++ b/src/resolve-host/resolve-host.c @@ -26,7 +26,7 @@ #include "sd-bus.h" #include "bus-util.h" #include "bus-error.h" -#include "bus-errors.h" +#include "bus-common-errors.h" #include "in-addr-util.h" #include "af-list.h" #include "build.h" @@ -155,7 +155,7 @@ static int resolve_host(sd_bus *bus, const char *name) { r = in_addr_to_string(family, a, &pretty); if (r < 0) { - log_error("%s: failed to print address: %s", name, strerror(-r)); + log_error_errno(r, "%s: failed to print address: %m", name); continue; } @@ -513,10 +513,8 @@ static int parse_argv(int argc, char *argv[]) { case 'i': arg_ifindex = if_nametoindex(optarg); - if (arg_ifindex <= 0) { - log_error("Unknown interfaces %s: %m", optarg); - return -errno; - } + if (arg_ifindex <= 0) + return log_error_errno(errno, "Unknown interfaces %s: %m", optarg); break; case 't': @@ -614,7 +612,7 @@ int main(int argc, char **argv) { r = sd_bus_open_system(&bus); if (r < 0) { - log_error("sd_bus_open_system: %s", strerror(-r)); + log_error_errno(r, "sd_bus_open_system: %m"); goto finish; } diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index 0029023bcc..8161b5321f 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -19,7 +19,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "bus-errors.h" +#include "bus-common-errors.h" #include "bus-util.h" #include "resolved-dns-domain.h" @@ -251,7 +251,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *q) { finish: if (r < 0) { - log_error("Failed to send hostname reply: %s", strerror(-r)); + log_error_errno(r, "Failed to send hostname reply: %m"); sd_bus_reply_method_errno(q->request, -r, NULL); } @@ -419,7 +419,7 @@ static void bus_method_resolve_address_complete(DnsQuery *q) { finish: if (r < 0) { - log_error("Failed to send address reply: %s", strerror(-r)); + log_error_errno(r, "Failed to send address reply: %m"); sd_bus_reply_method_errno(q->request, -r, NULL); } @@ -591,7 +591,7 @@ static void bus_method_resolve_record_complete(DnsQuery *q) { finish: if (r < 0) { - log_error("Failed to send record reply: %s", strerror(-r)); + log_error_errno(r, "Failed to send record reply: %m"); sd_bus_reply_method_errno(q->request, -r, NULL); } @@ -690,7 +690,7 @@ static int match_prepare_for_sleep(sd_bus *bus, sd_bus_message *message, void *u r = sd_bus_message_read(message, "b", &b); if (r < 0) { - log_debug("Failed to parse PrepareForSleep signal: %s", strerror(-r)); + log_debug_errno(r, "Failed to parse PrepareForSleep signal: %m"); return 0; } @@ -717,34 +717,26 @@ int manager_connect_bus(Manager *m) { * boot. Let's try in 5s again. As soon as we have * kdbus we can stop doing this... */ - log_debug("Failed to connect to bus, trying again in 5s: %s", strerror(-r)); + log_debug_errno(r, "Failed to connect to bus, trying again in 5s: %m"); r = sd_event_add_time(m->event, &m->bus_retry_event_source, CLOCK_MONOTONIC, now(CLOCK_MONOTONIC) + 5*USEC_PER_SEC, 0, on_bus_retry, m); - if (r < 0) { - log_error("Failed to install bus reconnect time event: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to install bus reconnect time event: %m"); return 0; } r = sd_bus_add_object_vtable(m->bus, NULL, "/org/freedesktop/resolve1", "org.freedesktop.resolve1.Manager", resolve_vtable, m); - if (r < 0) { - log_error("Failed to register object: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to register object: %m"); r = sd_bus_request_name(m->bus, "org.freedesktop.resolve1", 0); - if (r < 0) { - log_error("Failed to register name: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to register name: %m"); r = sd_bus_attach_event(m->bus, m->event, 0); - if (r < 0) { - log_error("Failed to attach bus to event loop: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to attach bus to event loop: %m"); r = sd_bus_add_match(m->bus, &m->prepare_for_sleep_slot, "type='signal'," @@ -755,7 +747,7 @@ int manager_connect_bus(Manager *m) { match_prepare_for_sleep, m); if (r < 0) - log_error("Failed to add match for PrepareForSleep: %s", strerror(-r)); + log_error_errno(r, "Failed to add match for PrepareForSleep: %m"); return 0; } diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c index 63e87f8df5..7af63b0a82 100644 --- a/src/resolve/resolved-conf.c +++ b/src/resolve/resolved-conf.c @@ -120,14 +120,12 @@ int config_parse_support( void *data, void *userdata) { - Manager *m = userdata; Support support, *v = data; int r; assert(filename); assert(lvalue); assert(rvalue); - assert(m); support = support_from_string(rvalue); if (support < 0) { @@ -147,8 +145,9 @@ int config_parse_support( int manager_parse_config_file(Manager *m) { assert(m); - return config_parse(NULL, "/etc/systemd/resolved.conf", NULL, - "Resolve\0", - config_item_perf_lookup, resolved_gperf_lookup, - false, false, true, m); + return config_parse_many("/etc/systemd/resolved.conf", + CONF_DIRS_NULSTR("systemd/resolved.conf"), + "Resolve\0", + config_item_perf_lookup, resolved_gperf_lookup, + false, m); } diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index 7375f77481..cf5b6189c5 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -22,6 +22,7 @@ #include "utf8.h" #include "util.h" #include "strv.h" +#include "unaligned.h" #include "resolved-dns-domain.h" #include "resolved-dns-packet.h" @@ -311,8 +312,7 @@ int dns_packet_append_uint16(DnsPacket *p, uint16_t v, size_t *start) { if (r < 0) return r; - ((uint8_t*) d)[0] = (uint8_t) (v >> 8); - ((uint8_t*) d)[1] = (uint8_t) v; + unaligned_write_be16(d, v); return 0; } @@ -327,10 +327,7 @@ int dns_packet_append_uint32(DnsPacket *p, uint32_t v, size_t *start) { if (r < 0) return r; - ((uint8_t*) d)[0] = (uint8_t) (v >> 24); - ((uint8_t*) d)[1] = (uint8_t) (v >> 16); - ((uint8_t*) d)[2] = (uint8_t) (v >> 8); - ((uint8_t*) d)[3] = (uint8_t) v; + unaligned_write_be32(d, v); return 0; } @@ -550,10 +547,19 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, size_t *star case DNS_TYPE_TXT: { char **s; - STRV_FOREACH(s, rr->txt.strings) { - r = dns_packet_append_string(p, *s, NULL); + if (strv_isempty(rr->txt.strings)) { + /* RFC 6763, section 6.1 suggests to generate + * single empty string for an empty array. */ + + r = dns_packet_append_string(p, "", NULL); if (r < 0) goto fail; + } else { + STRV_FOREACH(s, rr->txt.strings) { + r = dns_packet_append_string(p, *s, NULL); + if (r < 0) + goto fail; + } } r = 0; @@ -793,8 +799,8 @@ int dns_packet_read_uint16(DnsPacket *p, uint16_t *ret, size_t *start) { if (r < 0) return r; - *ret = (((uint16_t) ((uint8_t*) d)[0]) << 8) | - ((uint16_t) ((uint8_t*) d)[1]); + *ret = unaligned_read_be16(d); + return 0; } @@ -808,10 +814,7 @@ int dns_packet_read_uint32(DnsPacket *p, uint32_t *ret, size_t *start) { if (r < 0) return r; - *ret = (((uint32_t) ((uint8_t*) d)[0]) << 24) | - (((uint32_t) ((uint8_t*) d)[1]) << 16) | - (((uint32_t) ((uint8_t*) d)[2]) << 8) | - ((uint32_t) ((uint8_t*) d)[3]); + *ret = unaligned_read_be32(d); return 0; } @@ -866,7 +869,7 @@ fail: int dns_packet_read_name(DnsPacket *p, char **_ret, bool allow_compression, size_t *start) { - size_t saved_rindex, after_rindex = 0; + size_t saved_rindex, after_rindex = 0, jump_barrier; _cleanup_free_ char *ret = NULL; size_t n = 0, allocated = 0; bool first = true; @@ -876,6 +879,7 @@ int dns_packet_read_name(DnsPacket *p, char **_ret, assert(_ret); saved_rindex = p->rindex; + jump_barrier = p->rindex; for (;;) { uint8_t c, d; @@ -922,7 +926,7 @@ int dns_packet_read_name(DnsPacket *p, char **_ret, goto fail; ptr = (uint16_t) (c & ~0xc0) << 8 | (uint16_t) d; - if (ptr < DNS_PACKET_HEADER_SIZE || ptr >= saved_rindex) { + if (ptr < DNS_PACKET_HEADER_SIZE || ptr >= jump_barrier) { r = -EBADMSG; goto fail; } @@ -930,9 +934,13 @@ int dns_packet_read_name(DnsPacket *p, char **_ret, if (after_rindex == 0) after_rindex = p->rindex; + /* Jumps are limited to a "prior occurence" (RFC-1035 4.1.4) */ + jump_barrier = ptr; p->rindex = ptr; - } else + } else { + r = -EBADMSG; goto fail; + } } if (!GREEDY_REALLOC(ret, allocated, n + 1)) { @@ -1112,22 +1120,31 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, size_t *start) { break; case DNS_TYPE_SPF: /* exactly the same as TXT */ - case DNS_TYPE_TXT: { - char *s; + case DNS_TYPE_TXT: + if (rdlength <= 0) { + /* RFC 6763, section 6.1 suggests to treat + * empty TXT RRs as equivalent to a TXT record + * with a single empty string. */ - while (p->rindex < offset + rdlength) { - r = dns_packet_read_string(p, &s, NULL); + r = strv_extend(&rr->txt.strings, ""); if (r < 0) goto fail; + } else { + while (p->rindex < offset + rdlength) { + char *s; - r = strv_consume(&rr->txt.strings, s); - if (r < 0) - goto fail; + r = dns_packet_read_string(p, &s, NULL); + if (r < 0) + goto fail; + + r = strv_consume(&rr->txt.strings, s); + if (r < 0) + goto fail; + } } r = 0; break; - } case DNS_TYPE_A: r = dns_packet_read_blob(p, &rr->a.in_addr, sizeof(struct in_addr), NULL); diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index fd5ecf413d..78d9e4a412 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -370,14 +370,8 @@ int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecor strcaseeq(a->hinfo.os, b->hinfo.os); case DNS_TYPE_SPF: /* exactly the same as TXT */ - case DNS_TYPE_TXT: { - int i; - - for (i = 0; a->txt.strings[i] || b->txt.strings[i]; i++) - if (!streq_ptr(a->txt.strings[i], b->txt.strings[i])) - return false; - return true; - } + case DNS_TYPE_TXT: + return strv_equal(a->txt.strings, b->txt.strings); case DNS_TYPE_A: return memcmp(&a->a.in_addr, &b->a.in_addr, sizeof(struct in_addr)) == 0; diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index 1664b1328e..a43359f8c5 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -334,7 +334,8 @@ DnsScopeMatch dns_scope_good_domain(DnsScope *s, int ifindex, uint64_t flags, co if (s->protocol == DNS_PROTOCOL_LLMNR) { if (dns_name_endswith(domain, "in-addr.arpa") > 0 || dns_name_endswith(domain, "ip6.arpa") > 0 || - dns_name_single_label(domain) > 0) + (dns_name_single_label(domain) > 0 && + dns_name_equal(domain, "gateway") <= 0)) /* don't resolve "gateway" with LLMNR, let nss-myhostname handle this */ return DNS_SCOPE_MAYBE; return DNS_SCOPE_NO; @@ -386,7 +387,7 @@ int dns_scope_llmnr_membership(DnsScope *s, bool b) { * one. This is necessary on some devices, such as * veth. */ if (b) - setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn)); + (void)setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn)); if (setsockopt(fd, IPPROTO_IP, b ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreqn, sizeof(mreqn)) < 0) return -errno; @@ -402,7 +403,7 @@ int dns_scope_llmnr_membership(DnsScope *s, bool b) { return fd; if (b) - setsockopt(fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); + (void)setsockopt(fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); if (setsockopt(fd, IPPROTO_IPV6, b ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) return -errno; @@ -539,7 +540,7 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) { r = dns_packet_extract(p); if (r < 0) { - log_debug("Failed to extract resources from incoming packet: %s", strerror(-r)); + log_debug_errno(r, "Failed to extract resources from incoming packet: %m"); return; } @@ -551,7 +552,7 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) { r = dns_zone_lookup(&s->zone, p->question, &answer, &soa, &tentative); if (r < 0) { - log_debug("Failed to lookup key: %s", strerror(-r)); + log_debug_errno(r, "Failed to lookup key: %m"); return; } if (r == 0) @@ -562,7 +563,7 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) { r = dns_scope_make_reply_packet(s, DNS_PACKET_ID(p), DNS_RCODE_SUCCESS, p->question, answer, soa, tentative, &reply); if (r < 0) { - log_debug("Failed to build reply packet: %s", strerror(-r)); + log_debug_errno(r, "Failed to build reply packet: %m"); return; } @@ -581,7 +582,7 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) { return; } if (fd < 0) { - log_debug("Failed to get reply socket: %s", strerror(-fd)); + log_debug_errno(fd, "Failed to get reply socket: %m"); return; } @@ -594,7 +595,7 @@ void dns_scope_process_query(DnsScope *s, DnsStream *stream, DnsPacket *p) { } if (r < 0) { - log_debug("Failed to send reply packet: %s", strerror(-r)); + log_debug_errno(r, "Failed to send reply packet: %m"); return; } } @@ -688,13 +689,13 @@ static int on_conflict_dispatch(sd_event_source *es, usec_t usec, void *userdata r = dns_scope_make_conflict_packet(scope, rr, &p); if (r < 0) { - log_error("Failed to make conflict packet: %s", strerror(-r)); + log_error_errno(r, "Failed to make conflict packet: %m"); return 0; } r = dns_scope_emit(scope, p); if (r < 0) - log_debug("Failed to send conflict packet: %s", strerror(-r)); + log_debug_errno(r, "Failed to send conflict packet: %m"); } return 0; @@ -721,10 +722,8 @@ int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr) { r = ordered_hashmap_put(scope->conflict_queue, rr->key, rr); if (r == -EEXIST || r == 0) return 0; - if (r < 0) { - log_debug("Failed to queue conflicting RR: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_debug_errno(r, "Failed to queue conflicting RR: %m"); dns_resource_record_ref(rr); @@ -740,10 +739,8 @@ int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr) { now(clock_boottime_or_monotonic()) + jitter, LLMNR_JITTER_INTERVAL_USEC, on_conflict_dispatch, scope); - if (r < 0) { - log_debug("Failed to add conflict dispatch event: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_debug_errno(r, "Failed to add conflict dispatch event: %m"); return 0; } @@ -772,7 +769,7 @@ void dns_scope_check_conflicts(DnsScope *scope, DnsPacket *p) { r = dns_packet_extract(p); if (r < 0) { - log_debug("Failed to extract packet: %s", strerror(-r)); + log_debug_errno(r, "Failed to extract packet: %m"); return; } diff --git a/src/resolve/resolved-dns-stream.c b/src/resolve/resolved-dns-stream.c index 3690679ec6..4c0b557bad 100644 --- a/src/resolve/resolved-dns-stream.c +++ b/src/resolve/resolved-dns-stream.c @@ -172,11 +172,11 @@ static int dns_stream_identify(DnsStream *s) { if (s->local.sa.sa_family == AF_INET) { r = setsockopt(s->fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex, sizeof(ifindex)); if (r < 0) - log_debug("Failed to invoke IP_UNICAST_IF: %m"); + log_debug_errno(errno, "Failed to invoke IP_UNICAST_IF: %m"); } else if (s->local.sa.sa_family == AF_INET6) { r = setsockopt(s->fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex, sizeof(ifindex)); if (r < 0) - log_debug("Failed to invoke IPV6_UNICAST_IF: %m"); + log_debug_errno(errno, "Failed to invoke IPV6_UNICAST_IF: %m"); } } diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c index 8098ff5e70..a4c9b7d7af 100644 --- a/src/resolve/resolved-dns-zone.c +++ b/src/resolve/resolved-dns-zone.c @@ -570,7 +570,7 @@ static int dns_zone_item_verify(DnsZoneItem *i) { i->state = DNS_ZONE_ITEM_VERIFYING; r = dns_zone_item_probe_start(i); if (r < 0) { - log_error("Failed to start probing for verifying RR: %s", strerror(-r)); + log_error_errno(r, "Failed to start probing for verifying RR: %m"); i->state = DNS_ZONE_ITEM_ESTABLISHED; return r; } diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index 4def672214..f94e4bb6f0 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -88,7 +88,7 @@ static void link_allocate_scopes(Link *l) { if (!l->unicast_scope) { r = dns_scope_new(l->manager, &l->unicast_scope, l, DNS_PROTOCOL_DNS, AF_UNSPEC); if (r < 0) - log_warning("Failed to allocate DNS scope: %s", strerror(-r)); + log_warning_errno(r, "Failed to allocate DNS scope: %m"); } } else l->unicast_scope = dns_scope_free(l->unicast_scope); @@ -99,7 +99,7 @@ static void link_allocate_scopes(Link *l) { if (!l->llmnr_ipv4_scope) { r = dns_scope_new(l->manager, &l->llmnr_ipv4_scope, l, DNS_PROTOCOL_LLMNR, AF_INET); if (r < 0) - log_warning("Failed to allocate LLMNR IPv4 scope: %s", strerror(-r)); + log_warning_errno(r, "Failed to allocate LLMNR IPv4 scope: %m"); } } else l->llmnr_ipv4_scope = dns_scope_free(l->llmnr_ipv4_scope); @@ -111,7 +111,7 @@ static void link_allocate_scopes(Link *l) { if (!l->llmnr_ipv6_scope) { r = dns_scope_new(l->manager, &l->llmnr_ipv6_scope, l, DNS_PROTOCOL_LLMNR, AF_INET6); if (r < 0) - log_warning("Failed to allocate LLMNR IPv6 scope: %s", strerror(-r)); + log_warning_errno(r, "Failed to allocate LLMNR IPv6 scope: %m"); } } else l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope); @@ -439,11 +439,11 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) { r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_address_rr, true); if (r < 0) - log_warning("Failed to add A record to LLMNR zone: %s", strerror(-r)); + log_warning_errno(r, "Failed to add A record to LLMNR zone: %m"); r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_ptr_rr, false); if (r < 0) - log_warning("Failed to add IPv6 PTR record to LLMNR zone: %s", strerror(-r)); + log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m"); } else { if (a->llmnr_address_rr) { if (a->link->llmnr_ipv4_scope) @@ -496,11 +496,11 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) { r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_address_rr, true); if (r < 0) - log_warning("Failed to add AAAA record to LLMNR zone: %s", strerror(-r)); + log_warning_errno(r, "Failed to add AAAA record to LLMNR zone: %m"); r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_ptr_rr, false); if (r < 0) - log_warning("Failed to add IPv6 PTR record to LLMNR zone: %s", strerror(-r)); + log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m"); } else { if (a->llmnr_address_rr) { if (a->link->llmnr_ipv6_scope) @@ -519,7 +519,7 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) { return; fail: - log_debug("Failed to update address RRs: %s", strerror(-r)); + log_debug_errno(r, "Failed to update address RRs: %m"); } int link_address_update_rtnl(LinkAddress *a, sd_rtnl_message *m) { diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index c4a5b08773..0594479787 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -100,7 +100,7 @@ static int manager_process_link(sd_rtnl *rtnl, sd_rtnl_message *mm, void *userda return 0; fail: - log_warning("Failed to process RTNL link message: %s", strerror(-r)); + log_warning_errno(r, "Failed to process RTNL link message: %m"); return 0; } @@ -185,7 +185,7 @@ static int manager_process_address(sd_rtnl *rtnl, sd_rtnl_message *mm, void *use return 0; fail: - log_warning("Failed to process RTNL address message: %s", strerror(-r)); + log_warning_errno(r, "Failed to process RTNL address message: %m"); return 0; } @@ -278,12 +278,12 @@ static int on_network_event(sd_event_source *s, int fd, uint32_t revents, void * HASHMAP_FOREACH(l, m->links, i) { r = link_update_monitor(l); if (r < 0) - log_warning("Failed to update monitor information for %i: %s", l->ifindex, strerror(-r)); + log_warning_errno(r, "Failed to update monitor information for %i: %m", l->ifindex); } r = manager_write_resolv_conf(m); if (r < 0) - log_warning("Could not update resolv.conf: %s", strerror(-r)); + log_warning_errno(r, "Could not update resolv.conf: %m"); return 0; } @@ -370,7 +370,7 @@ static int manager_watch_hostname(Manager *m) { m->hostname_fd = open("/proc/sys/kernel/hostname", O_RDONLY|O_CLOEXEC|O_NDELAY|O_NOCTTY); if (m->hostname_fd < 0) { - log_warning("Failed to watch hostname: %m"); + log_warning_errno(errno, "Failed to watch hostname: %m"); return 0; } @@ -379,10 +379,8 @@ static int manager_watch_hostname(Manager *m) { if (r == -EPERM) /* kernels prior to 3.2 don't support polling this file. Ignore the failure. */ m->hostname_fd = safe_close(m->hostname_fd); - else { - log_error("Failed to add hostname event source: %s", strerror(-r)); - return r; - } + else + return log_error_errno(r, "Failed to add hostname event source: %m"); } r = determine_hostname(&m->hostname); @@ -593,7 +591,7 @@ int manager_read_resolv_conf(Manager *m) { r = stat("/etc/resolv.conf", &st); if (r < 0) { if (errno != ENOENT) - log_warning("Failed to open /etc/resolv.conf: %m"); + log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m"); r = -errno; goto clear; } @@ -616,13 +614,13 @@ int manager_read_resolv_conf(Manager *m) { f = fopen("/etc/resolv.conf", "re"); if (!f) { if (errno != ENOENT) - log_warning("Failed to open /etc/resolv.conf: %m"); + log_warning_errno(errno, "Failed to open /etc/resolv.conf: %m"); r = -errno; goto clear; } if (fstat(fileno(f), &st) < 0) { - log_error("Failed to stat open file: %m"); + log_error_errno(errno, "Failed to stat open file: %m"); r = -errno; goto clear; } @@ -688,7 +686,7 @@ static void write_resolv_conf_server(DnsServer *s, FILE *f, unsigned *count) { r = in_addr_to_string(s->family, &s->address, &t); if (r < 0) { - log_warning("Invalid DNS address. Ignoring: %s", strerror(-r)); + log_warning_errno(r, "Invalid DNS address. Ignoring: %m"); return; } diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c index 7d258c9470..c0ab947c0e 100644 --- a/src/resolve/resolved.c +++ b/src/resolve/resolved.c @@ -49,20 +49,20 @@ int main(int argc, char *argv[]) { r = mac_selinux_init(NULL); if (r < 0) { - log_error("SELinux setup failed: %s", strerror(-r)); + log_error_errno(r, "SELinux setup failed: %m"); goto finish; } r = get_user_creds(&user, &uid, &gid, NULL, NULL); if (r < 0) { - log_error("Cannot resolve user name %s: %s", user, strerror(-r)); + log_error_errno(r, "Cannot resolve user name %s: %m", user); goto finish; } /* Always create the directory where resolv.conf will live */ r = mkdir_safe_label("/run/systemd/resolve", 0755, uid, gid); if (r < 0) { - log_error("Could not create runtime directory: %s", strerror(-r)); + log_error_errno(r, "Could not create runtime directory: %m"); goto finish; } @@ -74,17 +74,17 @@ int main(int argc, char *argv[]) { r = manager_new(&m); if (r < 0) { - log_error("Could not create manager: %s", strerror(-r)); + log_error_errno(r, "Could not create manager: %m"); goto finish; } r = manager_parse_config_file(m); if (r < 0) - log_warning("Failed to parse configuration file: %s", strerror(-r)); + log_warning_errno(r, "Failed to parse configuration file: %m"); r = manager_start(m); if (r < 0) { - log_error("Failed to start manager: %s", strerror(-r)); + log_error_errno(r, "Failed to start manager: %m"); goto finish; } @@ -92,7 +92,7 @@ int main(int argc, char *argv[]) { * symlink */ r = manager_write_resolv_conf(m); if (r < 0) - log_warning("Could not create resolv.conf: %s", strerror(-r)); + log_warning_errno(r, "Could not create resolv.conf: %m"); sd_notify(false, "READY=1\n" @@ -100,7 +100,7 @@ int main(int argc, char *argv[]) { r = sd_event_loop(m->event); if (r < 0) { - log_error("Event loop failed: %s", strerror(-r)); + log_error_errno(r, "Event loop failed: %m"); goto finish; } @@ -108,7 +108,7 @@ int main(int argc, char *argv[]) { finish: sd_notify(false, - "STOPPIN=1\n" + "STOPPING=1\n" "STATUS=Shutting down..."); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; diff --git a/src/resolve/resolved.conf.in b/src/resolve/resolved.conf.in index c8263d67f4..e5a19ee474 100644 --- a/src/resolve/resolved.conf.in +++ b/src/resolve/resolved.conf.in @@ -5,6 +5,9 @@ # the Free Software Foundation; either version 2.1 of the License, or # (at your option) any later version. # +# You can override the directives in this file by creating files in +# /etc/systemd/resolved.conf.d/*.conf. +# # See resolved.conf(5) for details [Resolve] diff --git a/src/rfkill/rfkill.c b/src/rfkill/rfkill.c index c7c659292a..5a90c778fb 100644 --- a/src/rfkill/rfkill.c +++ b/src/rfkill/rfkill.c @@ -28,8 +28,8 @@ int main(int argc, char *argv[]) { _cleanup_udev_unref_ struct udev *udev = NULL; _cleanup_udev_device_unref_ struct udev_device *device = NULL; - _cleanup_free_ char *saved = NULL, *escaped_name = NULL, *escaped_path_id = NULL; - const char *name, *path_id; + _cleanup_free_ char *saved = NULL, *escaped_type = NULL, *escaped_path_id = NULL; + const char *name, *type, *path_id; int r; if (argc != 3) { @@ -45,7 +45,7 @@ int main(int argc, char *argv[]) { r = mkdir_p("/var/lib/systemd/rfkill", 0755); if (r < 0) { - log_error("Failed to create rfkill directory: %s", strerror(-r)); + log_error_errno(r, "Failed to create rfkill directory: %m"); return EXIT_FAILURE; } @@ -55,25 +55,28 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - errno = 0; device = udev_device_new_from_subsystem_sysname(udev, "rfkill", argv[2]); if (!device) { - if (errno != 0) - log_error("Failed to get rfkill device '%s': %m", argv[2]); - else - log_oom(); - - return EXIT_FAILURE; + log_debug_errno(errno, "Failed to get rfkill device '%s', ignoring: %m", argv[2]); + return EXIT_SUCCESS; } name = udev_device_get_sysattr_value(device, "name"); if (!name) { - log_error("rfkill device has no name?"); - return EXIT_FAILURE; + log_error("rfkill device has no name? Ignoring device."); + return EXIT_SUCCESS; + } + + log_debug("Operating on rfkill device '%s'.", name); + + type = udev_device_get_sysattr_value(device, "type"); + if (!type) { + log_error("rfkill device has no type? Ignoring device."); + return EXIT_SUCCESS; } - escaped_name = cescape(name); - if (!escaped_name) { + escaped_type = cescape(type); + if (!escaped_type) { log_oom(); return EXIT_FAILURE; } @@ -86,9 +89,9 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - saved = strjoin("/var/lib/systemd/rfkill/", escaped_path_id, ":", escaped_name, NULL); + saved = strjoin("/var/lib/systemd/rfkill/", escaped_path_id, ":", escaped_type, NULL); } else - saved = strjoin("/var/lib/systemd/rfkill/", escaped_name, NULL); + saved = strjoin("/var/lib/systemd/rfkill/", escaped_type, NULL); if (!saved) { log_oom(); @@ -102,19 +105,17 @@ int main(int argc, char *argv[]) { return EXIT_SUCCESS; r = read_one_line_file(saved, &value); + if (r == -ENOENT) + return EXIT_SUCCESS; if (r < 0) { - - if (r == -ENOENT) - return EXIT_SUCCESS; - - log_error("Failed to read %s: %s", saved, strerror(-r)); + log_error_errno(r, "Failed to read %s: %m", saved); return EXIT_FAILURE; } r = udev_device_set_sysattr_value(device, "soft", value); if (r < 0) { - log_error("Failed to write system attribute: %s", strerror(-r)); - return EXIT_FAILURE; + log_debug_errno(r, "Failed to write 'soft' attribute on rfkill device, ignoring: %m"); + return EXIT_SUCCESS; } } else if (streq(argv[1], "save")) { @@ -122,13 +123,13 @@ int main(int argc, char *argv[]) { value = udev_device_get_sysattr_value(device, "soft"); if (!value) { - log_error("Failed to read system attribute: %s", strerror(-r)); - return EXIT_FAILURE; + log_debug_errno(r, "Failed to read system attribute, ignoring device: %m"); + return EXIT_SUCCESS; } r = write_string_file(saved, value); if (r < 0) { - log_error("Failed to write %s: %s", saved, strerror(-r)); + log_error_errno(r, "Failed to write %s: %m", saved); return EXIT_FAILURE; } diff --git a/src/run/run.c b/src/run/run.c index e3b62939c7..7a80223918 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -30,6 +30,7 @@ #include "env-util.h" #include "path-util.h" #include "bus-error.h" +#include "calendarspec.h" static bool arg_scope = false; static bool arg_remain_after_exit = false; @@ -47,30 +48,51 @@ static int arg_nice = 0; static bool arg_nice_set = false; static char **arg_environment = NULL; static char **arg_property = NULL; +static usec_t arg_on_active = 0; +static usec_t arg_on_boot = 0; +static usec_t arg_on_startup = 0; +static usec_t arg_on_unit_active = 0; +static usec_t arg_on_unit_inactive = 0; +static char *arg_on_calendar = NULL; +static char **arg_timer_property = NULL; static void help(void) { - printf("%s [OPTIONS...] COMMAND [ARGS...]\n\n" - "Run the specified command in a transient scope or service unit.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --user Run as user unit\n" - " -H --host=[USER@]HOST Operate on remote host\n" - " -M --machine=CONTAINER Operate on local container\n" - " --scope Run this as scope rather than service\n" - " --unit=UNIT Run under the specified unit name\n" - " -p --property=NAME=VALUE Set unit property\n" - " --description=TEXT Description for unit\n" - " --slice=SLICE Run in the specified slice\n" - " -r --remain-after-exit Leave service around until explicitly stopped\n" - " --send-sighup Send SIGHUP when terminating\n" - " --service-type=TYPE Service type\n" - " --uid=USER Run as system user\n" - " --gid=GROUP Run as system group\n" - " --nice=NICE Nice level\n" - " --setenv=NAME=VALUE Set environment\n", + printf("%s [OPTIONS...] {COMMAND} [ARGS...]\n\n" + "Run the specified command in a transient scope or service or timer\n" + "unit. If timer option is specified and unit is exist which is\n" + "specified with --unit option then command can be ommited.\n\n" + " -h --help Show this help\n" + " --version Show package version\n" + " --user Run as user unit\n" + " -H --host=[USER@]HOST Operate on remote host\n" + " -M --machine=CONTAINER Operate on local container\n" + " --scope Run this as scope rather than service\n" + " --unit=UNIT Run under the specified unit name\n" + " -p --property=NAME=VALUE Set unit property\n" + " --description=TEXT Description for unit\n" + " --slice=SLICE Run in the specified slice\n" + " -r --remain-after-exit Leave service around until explicitly stopped\n" + " --send-sighup Send SIGHUP when terminating\n" + " --service-type=TYPE Service type\n" + " --uid=USER Run as system user\n" + " --gid=GROUP Run as system group\n" + " --nice=NICE Nice level\n" + " --setenv=NAME=VALUE Set environment\n\n" + "Timer options:\n\n" + " --on-active=SEC Run after seconds\n" + " --on-boot=SEC Run after seconds from machine was booted up\n" + " --on-startup=SEC Run after seconds from systemd was first started\n" + " --on-unit-active=SEC Run after seconds from the last activation\n" + " --on-unit-inactive=SEC Run after seconds from the last deactivation\n" + " --on-calendar=SPEC Realtime timer\n" + " --timer-property=NAME=VALUE Set timer unit property\n", program_invocation_short_name); } +static bool with_timer(void) { + return arg_on_active || arg_on_boot || arg_on_startup || arg_on_unit_active || arg_on_unit_inactive || arg_on_calendar; +} + static int parse_argv(int argc, char *argv[]) { enum { @@ -86,32 +108,47 @@ static int parse_argv(int argc, char *argv[]) { ARG_EXEC_GROUP, ARG_SERVICE_TYPE, ARG_NICE, - ARG_SETENV + ARG_SETENV, + ARG_ON_ACTIVE, + ARG_ON_BOOT, + ARG_ON_STARTUP, + ARG_ON_UNIT_ACTIVE, + ARG_ON_UNIT_INACTIVE, + ARG_ON_CALENDAR, + ARG_TIMER_PROPERTY }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "user", no_argument, NULL, ARG_USER }, - { "system", no_argument, NULL, ARG_SYSTEM }, - { "scope", no_argument, NULL, ARG_SCOPE }, - { "unit", required_argument, NULL, ARG_UNIT }, - { "description", required_argument, NULL, ARG_DESCRIPTION }, - { "slice", required_argument, NULL, ARG_SLICE }, - { "remain-after-exit", no_argument, NULL, 'r' }, - { "send-sighup", no_argument, NULL, ARG_SEND_SIGHUP }, - { "host", required_argument, NULL, 'H' }, - { "machine", required_argument, NULL, 'M' }, - { "service-type", required_argument, NULL, ARG_SERVICE_TYPE }, - { "uid", required_argument, NULL, ARG_EXEC_USER }, - { "gid", required_argument, NULL, ARG_EXEC_GROUP }, - { "nice", required_argument, NULL, ARG_NICE }, - { "setenv", required_argument, NULL, ARG_SETENV }, - { "property", required_argument, NULL, 'p' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "user", no_argument, NULL, ARG_USER }, + { "system", no_argument, NULL, ARG_SYSTEM }, + { "scope", no_argument, NULL, ARG_SCOPE }, + { "unit", required_argument, NULL, ARG_UNIT }, + { "description", required_argument, NULL, ARG_DESCRIPTION }, + { "slice", required_argument, NULL, ARG_SLICE }, + { "remain-after-exit", no_argument, NULL, 'r' }, + { "send-sighup", no_argument, NULL, ARG_SEND_SIGHUP }, + { "host", required_argument, NULL, 'H' }, + { "machine", required_argument, NULL, 'M' }, + { "service-type", required_argument, NULL, ARG_SERVICE_TYPE }, + { "uid", required_argument, NULL, ARG_EXEC_USER }, + { "gid", required_argument, NULL, ARG_EXEC_GROUP }, + { "nice", required_argument, NULL, ARG_NICE }, + { "setenv", required_argument, NULL, ARG_SETENV }, + { "property", required_argument, NULL, 'p' }, + { "on-active", required_argument, NULL, ARG_ON_ACTIVE }, + { "on-boot", required_argument, NULL, ARG_ON_BOOT }, + { "on-startup", required_argument, NULL, ARG_ON_STARTUP }, + { "on-unit-active", required_argument, NULL, ARG_ON_UNIT_ACTIVE }, + { "on-unit-inactive", required_argument, NULL, ARG_ON_UNIT_INACTIVE }, + { "on-calendar", required_argument, NULL, ARG_ON_CALENDAR }, + { "timer-property", required_argument, NULL, ARG_TIMER_PROPERTY }, {}, }; int r, c; + CalendarSpec *spec = NULL; assert(argc >= 0); assert(argv); @@ -207,6 +244,74 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_ON_ACTIVE: + + r = parse_sec(optarg, &arg_on_active); + if (r < 0) { + log_error("Failed to parse timer value: %s", optarg); + return r; + } + + break; + + case ARG_ON_BOOT: + + r = parse_sec(optarg, &arg_on_boot); + if (r < 0) { + log_error("Failed to parse timer value: %s", optarg); + return r; + } + + break; + + case ARG_ON_STARTUP: + + r = parse_sec(optarg, &arg_on_startup); + if (r < 0) { + log_error("Failed to parse timer value: %s", optarg); + return r; + } + + break; + + case ARG_ON_UNIT_ACTIVE: + + r = parse_sec(optarg, &arg_on_unit_active); + if (r < 0) { + log_error("Failed to parse timer value: %s", optarg); + return r; + } + + break; + + case ARG_ON_UNIT_INACTIVE: + + r = parse_sec(optarg, &arg_on_unit_inactive); + if (r < 0) { + log_error("Failed to parse timer value: %s", optarg); + return r; + } + + break; + + case ARG_ON_CALENDAR: + + r = calendar_spec_from_string(optarg, &spec); + if (r < 0) { + log_error("Invalid calendar spec: %s", optarg); + return r; + } + free(spec); + arg_on_calendar = optarg; + break; + + case ARG_TIMER_PROPERTY: + + if (strv_extend(&arg_timer_property, optarg) < 0) + return log_oom(); + + break; + case '?': return -EINVAL; @@ -214,7 +319,7 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached("Unhandled option"); } - if (optind >= argc) { + if ((optind >= argc) && (!arg_unit || !with_timer())) { log_error("Command line to execute required."); return -EINVAL; } @@ -234,44 +339,34 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } + if (arg_scope && with_timer()) { + log_error("Timer options are not supported in --scope mode."); + return -EINVAL; + } + + if (arg_timer_property && !with_timer()) { + log_error("--timer-property= has no effect without any other timer options."); + return -EINVAL; + } + return 1; } -static int message_start_transient_unit_new(sd_bus *bus, const char *name, sd_bus_message **ret) { - _cleanup_bus_message_unref_ sd_bus_message *m = NULL; +static int transient_unit_set_properties(sd_bus_message *m, UnitType t) { char **i; int r; - assert(bus); - assert(name); - assert(ret); - - r = sd_bus_message_new_method_call( - bus, - &m, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "StartTransientUnit"); - if (r < 0) - return r; - - r = sd_bus_message_append(m, "ss", name, "fail"); - if (r < 0) - return r; - - r = sd_bus_message_open_container(m, 'a', "(sv)"); - if (r < 0) - return r; - - STRV_FOREACH(i, arg_property) { + STRV_FOREACH(i, t == UNIT_TIMER ? arg_timer_property : arg_property) { r = sd_bus_message_open_container(m, 'r', "sv"); if (r < 0) return r; r = bus_append_unit_property_assignment(m, *i); - if (r < 0) - return r; + if (r < 0) { + r = sd_bus_message_append(m, "sv", 0); + if (r < 0) + return r; + } r = sd_bus_message_close_container(m); if (r < 0) @@ -294,152 +389,324 @@ static int message_start_transient_unit_new(sd_bus *bus, const char *name, sd_bu return r; } - if (arg_send_sighup) { + if (arg_send_sighup && t != UNIT_TIMER) { r = sd_bus_message_append(m, "(sv)", "SendSIGHUP", "b", arg_send_sighup); if (r < 0) return r; } - *ret = m; - m = NULL; - return 0; } -static int message_start_transient_unit_send(sd_bus *bus, sd_bus_message *m, sd_bus_error *error, sd_bus_message **reply) { +static int transient_service_set_properties(sd_bus_message *m, char **argv) { int r; - assert(bus); assert(m); - r = sd_bus_message_close_container(m); + r = transient_unit_set_properties(m, UNIT_SERVICE); if (r < 0) return r; - r = sd_bus_message_append(m, "a(sa(sv))", 0); - if (r < 0) - return r; - - return sd_bus_call(bus, m, 0, error, reply); -} - -static int start_transient_service( - sd_bus *bus, - char **argv, - sd_bus_error *error) { - - _cleanup_bus_message_unref_ sd_bus_message *m = NULL; - _cleanup_free_ char *name = NULL; - int r; - - if (arg_unit) { - name = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service"); - if (!name) - return log_oom(); - } else if (asprintf(&name, "run-"PID_FMT".service", getpid()) < 0) - return log_oom(); - - r = message_start_transient_unit_new(bus, name, &m); - if (r < 0) - return bus_log_create_error(r); - if (arg_remain_after_exit) { r = sd_bus_message_append(m, "(sv)", "RemainAfterExit", "b", arg_remain_after_exit); if (r < 0) - return bus_log_create_error(r); + return r; } if (arg_service_type) { r = sd_bus_message_append(m, "(sv)", "Type", "s", arg_service_type); if (r < 0) - return bus_log_create_error(r); + return r; } if (arg_exec_user) { r = sd_bus_message_append(m, "(sv)", "User", "s", arg_exec_user); if (r < 0) - return bus_log_create_error(r); + return r; } if (arg_exec_group) { r = sd_bus_message_append(m, "(sv)", "Group", "s", arg_exec_group); if (r < 0) - return bus_log_create_error(r); + return r; } if (arg_nice_set) { r = sd_bus_message_append(m, "(sv)", "Nice", "i", arg_nice); if (r < 0) - return bus_log_create_error(r); + return r; } if (!strv_isempty(arg_environment)) { r = sd_bus_message_open_container(m, 'r', "sv"); if (r < 0) - return bus_log_create_error(r); + return r; r = sd_bus_message_append(m, "s", "Environment"); if (r < 0) - return bus_log_create_error(r); + return r; r = sd_bus_message_open_container(m, 'v', "as"); if (r < 0) - return bus_log_create_error(r); + return r; r = sd_bus_message_append_strv(m, arg_environment); if (r < 0) - return bus_log_create_error(r); + return r; r = sd_bus_message_close_container(m); if (r < 0) - return bus_log_create_error(r); + return r; r = sd_bus_message_close_container(m); if (r < 0) - return bus_log_create_error(r); + return r; + } + + /* Exec container */ + { + r = sd_bus_message_open_container(m, 'r', "sv"); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "s", "ExecStart"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(m, 'v', "a(sasb)"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(m, 'a', "(sasb)"); + if (r < 0) + return r; + + r = sd_bus_message_open_container(m, 'r', "sasb"); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "s", argv[0]); + if (r < 0) + return r; + + r = sd_bus_message_append_strv(m, argv); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "b", false); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + } + + return 0; +} + +static int transient_timer_set_properties(sd_bus_message *m) { + int r; + + assert(m); + + r = transient_unit_set_properties(m, UNIT_TIMER); + if (r < 0) + return r; + + if (arg_on_active) { + r = sd_bus_message_append(m, "(sv)", "OnActiveSec", "t", arg_on_active); + if (r < 0) + return r; + } + + if (arg_on_boot) { + r = sd_bus_message_append(m, "(sv)", "OnBootSec", "t", arg_on_boot); + if (r < 0) + return r; + } + + if (arg_on_startup) { + r = sd_bus_message_append(m, "(sv)", "OnStartupSec", "t", arg_on_startup); + if (r < 0) + return r; + } + + if (arg_on_unit_active) { + r = sd_bus_message_append(m, "(sv)", "OnUnitActiveSec", "t", arg_on_unit_active); + if (r < 0) + return r; + } + + if (arg_on_unit_inactive) { + r = sd_bus_message_append(m, "(sv)", "OnUnitInactiveSec", "t", arg_on_unit_inactive); + if (r < 0) + return r; } - r = sd_bus_message_open_container(m, 'r', "sv"); + if (arg_on_calendar) { + r = sd_bus_message_append(m, "(sv)", "OnCalendar", "s", arg_on_calendar); + if (r < 0) + return r; + } + + return 0; +} + +static int transient_scope_set_properties(sd_bus_message *m) { + int r; + + assert(m); + + r = transient_unit_set_properties(m, UNIT_SCOPE); + if (r < 0) + return r; + + r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid()); + if (r < 0) + return r; + + return 0; +} + +static int start_transient_service( + sd_bus *bus, + char **argv, + sd_bus_error *error) { + + _cleanup_bus_message_unref_ sd_bus_message *m = NULL; + _cleanup_free_ char *service = NULL; + int r; + + assert(bus); + assert(argv); + + if (arg_unit) { + service = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service"); + if (!service) + return log_oom(); + } else if (asprintf(&service, "run-"PID_FMT".service", getpid()) < 0) + return log_oom(); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartTransientUnit"); if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_append(m, "s", "ExecStart"); + /* name and mode */ + r = sd_bus_message_append(m, "ss", service, "fail"); if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_open_container(m, 'v', "a(sasb)"); + /* properties */ + r = sd_bus_message_open_container(m, 'a', "(sv)"); if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_open_container(m, 'a', "(sasb)"); + r = transient_service_set_properties(m, argv); if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_open_container(m, 'r', "sasb"); + r = sd_bus_message_close_container(m); if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_append(m, "s", argv[0]); + /* aux */ + r = sd_bus_message_append(m, "a(sa(sv))", 0); if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_append_strv(m, argv); + /* send dbus */ + r = sd_bus_call(bus, m, 0, error, NULL); if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_append(m, "b", false); + log_info("Running as unit %s.", service); + + return 0; +} + +static int start_transient_timer( + sd_bus *bus, + char **argv, + sd_bus_error *error) { + + _cleanup_bus_message_unref_ sd_bus_message *m = NULL; + _cleanup_free_ char *timer = NULL, *service = NULL; + int r; + + assert(bus); + assert(argv); + + if (arg_unit) { + switch(unit_name_to_type(arg_unit)) { + case UNIT_SERVICE: + service = strdup(arg_unit); + timer = unit_name_change_suffix(service, ".timer"); + if (!timer) + return log_oom(); + break; + + case UNIT_TIMER: + timer = strdup(arg_unit); + service = unit_name_change_suffix(timer, ".service"); + if (!service) + return log_oom(); + break; + + default: + service = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".service"); + if (!service) + return log_oom(); + + timer = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".timer"); + if (!timer) + return log_oom(); + + break; + } + } else if ((asprintf(&service, "run-"PID_FMT".service", getpid()) < 0) || + (asprintf(&timer, "run-"PID_FMT".timer", getpid()) < 0)) + return log_oom(); + + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartTransientUnit"); if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_close_container(m); + /* name and mode */ + r = sd_bus_message_append(m, "ss", timer, "fail"); if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_close_container(m); + /* properties */ + r = sd_bus_message_open_container(m, 'a', "(sv)"); if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_close_container(m); + r = transient_timer_set_properties(m); if (r < 0) return bus_log_create_error(r); @@ -447,11 +714,52 @@ static int start_transient_service( if (r < 0) return bus_log_create_error(r); - r = message_start_transient_unit_send(bus, m, error, NULL); + if (argv[0]) { + r = sd_bus_message_open_container(m, 'a', "(sa(sv))"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'r', "sa(sv)"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "s", service); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "(sv)"); + if (r < 0) + return bus_log_create_error(r); + + r = transient_service_set_properties(m, argv); + 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); + 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); + } else { + r = sd_bus_message_append(m, "a(sa(sv))", 0); + if (r < 0) + return bus_log_create_error(r); + } + + /* send dbus */ + r = sd_bus_call(bus, m, 0, error, NULL); if (r < 0) return bus_log_create_error(r); - log_info("Running as unit %s.", name); + log_info("Running as unit %s.", timer); + if (argv[0]) + log_info("Will run as unit %s.", service); return 0; } @@ -462,51 +770,72 @@ static int start_transient_scope( sd_bus_error *error) { _cleanup_bus_message_unref_ sd_bus_message *m = NULL; - _cleanup_free_ char *name = NULL; + _cleanup_free_ char *scope = NULL; _cleanup_strv_free_ char **env = NULL, **user_env = NULL; int r; assert(bus); + assert(argv); if (arg_unit) { - name = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".scope"); - if (!name) + scope = unit_name_mangle_with_suffix(arg_unit, MANGLE_NOGLOB, ".scope"); + if (!scope) return log_oom(); - } else if (asprintf(&name, "run-"PID_FMT".scope", getpid()) < 0) + } else if (asprintf(&scope, "run-"PID_FMT".scope", getpid()) < 0) return log_oom(); - r = message_start_transient_unit_new(bus, name, &m); + r = sd_bus_message_new_method_call( + bus, + &m, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "StartTransientUnit"); if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, (uint32_t) getpid()); + /* name and mode */ + r = sd_bus_message_append(m, "ss", scope, "fail"); + if (r < 0) + return bus_log_create_error(r); + + /* properties */ + r = sd_bus_message_open_container(m, 'a', "(sv)"); + if (r < 0) + return bus_log_create_error(r); + + r = transient_scope_set_properties(m); + 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); + + /* aux */ + r = sd_bus_message_append(m, "a(sa(sv))", 0); if (r < 0) return bus_log_create_error(r); - r = message_start_transient_unit_send(bus, m, error, NULL); + /* send dbus */ + r = sd_bus_call(bus, m, 0, error, NULL); if (r < 0) return bus_log_create_error(r); if (arg_nice_set) { - if (setpriority(PRIO_PROCESS, 0, arg_nice) < 0) { - log_error("Failed to set nice level: %m"); - return -errno; - } + if (setpriority(PRIO_PROCESS, 0, arg_nice) < 0) + return log_error_errno(errno, "Failed to set nice level: %m"); } if (arg_exec_group) { gid_t gid; r = get_group_creds(&arg_exec_group, &gid); - if (r < 0) { - log_error("Failed to resolve group %s: %s", arg_exec_group, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to resolve group %s: %m", arg_exec_group); - if (setresgid(gid, gid, gid) < 0) { - log_error("Failed to change GID to " GID_FMT ": %m", gid); - return -errno; - } + if (setresgid(gid, gid, gid) < 0) + return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid); } if (arg_exec_user) { @@ -515,10 +844,8 @@ static int start_transient_scope( gid_t gid; r = get_user_creds(&arg_exec_user, &uid, &gid, &home, &shell); - if (r < 0) { - log_error("Failed to resolve user %s: %s", arg_exec_user, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to resolve user %s: %m", arg_exec_user); r = strv_extendf(&user_env, "HOME=%s", home); if (r < 0) @@ -537,26 +864,22 @@ static int start_transient_scope( return log_oom(); if (!arg_exec_group) { - if (setresgid(gid, gid, gid) < 0) { - log_error("Failed to change GID to " GID_FMT ": %m", gid); - return -errno; - } + if (setresgid(gid, gid, gid) < 0) + return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid); } - if (setresuid(uid, uid, uid) < 0) { - log_error("Failed to change UID to " UID_FMT ": %m", uid); - return -errno; - } + if (setresuid(uid, uid, uid) < 0) + return log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", uid); } env = strv_env_merge(3, environ, user_env, arg_environment); if (!env) return log_oom(); - log_info("Running as unit %s.", name); + log_info("Running as unit %s.", scope); execvpe(argv[0], argv, env); - log_error("Failed to execute: %m"); + log_error_errno(errno, "Failed to execute: %m"); return -errno; } @@ -573,12 +896,16 @@ int main(int argc, char* argv[]) { if (r <= 0) goto finish; - r = find_binary(argv[optind], &command); - if (r < 0) { - log_error("Failed to find executable %s: %s", argv[optind], strerror(-r)); - goto finish; + if (argc > optind) { + r = find_binary(argv[optind], arg_transport == BUS_TRANSPORT_LOCAL, &command); + if (r < 0) { + log_error_errno(r, "Failed to find executable %s%s: %m", + argv[optind], + arg_transport == BUS_TRANSPORT_LOCAL ? "" : " on local system"); + goto finish; + } + argv[optind] = command; } - argv[optind] = command; if (!arg_description) { description = strv_join(argv + optind, " "); @@ -587,23 +914,36 @@ int main(int argc, char* argv[]) { goto finish; } + if (arg_unit && isempty(description)) { + free(description); + description = strdup(arg_unit); + + if (!description) { + r = log_oom(); + goto finish; + } + } + arg_description = description; } r = bus_open_transport_systemd(arg_transport, arg_host, arg_user, &bus); if (r < 0) { - log_error("Failed to create bus connection: %s", strerror(-r)); + log_error_errno(r, "Failed to create bus connection: %m"); goto finish; } if (arg_scope) r = start_transient_scope(bus, argv + optind, &error); + else if (with_timer()) + r = start_transient_timer(bus, argv + optind, &error); else r = start_transient_service(bus, argv + optind, &error); finish: strv_free(arg_environment); strv_free(arg_property); + strv_free(arg_timer_property); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/shared/architecture.c b/src/shared/architecture.c index dc45f3589d..34c5a53fa9 100644 --- a/src/shared/architecture.c +++ b/src/shared/architecture.c @@ -23,7 +23,7 @@ #include "architecture.h" -Architecture uname_architecture(void) { +int uname_architecture(void) { /* Return a sanitized enum identifying the architecture we are * running on. This is based on uname(), and the user may @@ -41,7 +41,7 @@ Architecture uname_architecture(void) { static const struct { const char *machine; - Architecture arch; + int arch; } arch_map[] = { #if defined(__x86_64__) || defined(__i386__) { "x86_64", ARCHITECTURE_X86_64 }, @@ -121,7 +121,7 @@ Architecture uname_architecture(void) { #endif }; - static Architecture cached = _ARCHITECTURE_INVALID; + static int cached = _ARCHITECTURE_INVALID; struct utsname u; unsigned i; @@ -168,4 +168,4 @@ static const char *const architecture_table[_ARCHITECTURE_MAX] = { [ARCHITECTURE_CRIS] = "cris", }; -DEFINE_STRING_TABLE_LOOKUP(architecture, Architecture); +DEFINE_STRING_TABLE_LOOKUP(architecture, int); diff --git a/src/shared/architecture.h b/src/shared/architecture.h index 353d49bedf..cb82418a5e 100644 --- a/src/shared/architecture.h +++ b/src/shared/architecture.h @@ -30,7 +30,7 @@ * focus on general family, and distuignish word width and * endianness. */ -typedef enum Architecture { +enum { ARCHITECTURE_X86 = 0, ARCHITECTURE_X86_64, ARCHITECTURE_PPC, @@ -60,9 +60,9 @@ typedef enum Architecture { ARCHITECTURE_CRIS, _ARCHITECTURE_MAX, _ARCHITECTURE_INVALID = -1 -} Architecture; +}; -Architecture uname_architecture(void); +int uname_architecture(void); /* * LIB_ARCH_TUPLE should resolve to the local library path @@ -133,7 +133,7 @@ Architecture uname_architecture(void); # else # define native_architecture() ARCHITECTURE_MIPS_LE # define LIB_ARCH_TUPLE "mipsel-linux-gnu" -#endif +# endif #elif defined(__alpha__) # define native_architecture() ARCHITECTURE_ALPHA # define LIB_ARCH_TUPLE "alpha-linux-gnu" @@ -185,8 +185,8 @@ Architecture uname_architecture(void); # define native_architecture() ARCHITECTURE_CRIS # error "Missing LIB_ARCH_TUPLE for CRIS" #else -#error "Please register your architecture here!" +# error "Please register your architecture here!" #endif -const char *architecture_to_string(Architecture a) _const_; -Architecture architecture_from_string(const char *s) _pure_; +const char *architecture_to_string(int a) _const_; +int architecture_from_string(const char *s) _pure_; diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c index 94a27f9010..d6589a67f6 100644 --- a/src/shared/ask-password-api.c +++ b/src/shared/ask-password-api.c @@ -258,10 +258,8 @@ static int create_socket(char **name) { assert(name); fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); - if (fd < 0) { - log_error("socket() failed: %m"); - return -errno; - } + if (fd < 0) + return log_error_errno(errno, "socket() failed: %m"); snprintf(sa.un.sun_path, sizeof(sa.un.sun_path)-1, "/run/systemd/ask-password/sck.%" PRIx64, random_u64()); @@ -271,13 +269,13 @@ static int create_socket(char **name) { if (r < 0) { r = -errno; - log_error("bind(%s) failed: %m", sa.un.sun_path); + log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); goto fail; } if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) { r = -errno; - log_error("SO_PASSCRED failed: %m"); + log_error_errno(errno, "SO_PASSCRED failed: %m"); goto fail; } @@ -330,7 +328,7 @@ int ask_password_agent( fd = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC); if (fd < 0) { - log_error("Failed to create password file: %m"); + log_error_errno(errno, "Failed to create password file: %m"); r = -errno; goto finish; } @@ -339,7 +337,7 @@ int ask_password_agent( f = fdopen(fd, "w"); if (!f) { - log_error("Failed to allocate FILE: %m"); + log_error_errno(errno, "Failed to allocate FILE: %m"); r = -errno; goto finish; } @@ -348,7 +346,7 @@ int ask_password_agent( signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); if (signal_fd < 0) { - log_error("signalfd(): %m"); + log_error_errno(errno, "signalfd(): %m"); r = -errno; goto finish; } @@ -384,7 +382,7 @@ int ask_password_agent( fflush(f); if (ferror(f)) { - log_error("Failed to write query file: %m"); + log_error_errno(errno, "Failed to write query file: %m"); r = -errno; goto finish; } @@ -396,7 +394,7 @@ int ask_password_agent( final[sizeof(final)-9] = 'k'; if (rename(temp, final) < 0) { - log_error("Failed to rename query file: %m"); + log_error_errno(errno, "Failed to rename query file: %m"); r = -errno; goto finish; } @@ -433,7 +431,7 @@ int ask_password_agent( if (errno == EINTR) continue; - log_error("poll() failed: %m"); + log_error_errno(errno, "poll() failed: %m"); r = -errno; goto finish; } @@ -472,7 +470,7 @@ int ask_password_agent( errno == EINTR) continue; - log_error("recvmsg() failed: %m"); + log_error_errno(errno, "recvmsg() failed: %m"); r = -errno; goto finish; } diff --git a/src/shared/audit.c b/src/shared/audit.c index f101050825..4701c0a8de 100644 --- a/src/shared/audit.c +++ b/src/shared/audit.c @@ -80,3 +80,21 @@ int audit_loginuid_from_pid(pid_t pid, uid_t *uid) { *uid = (uid_t) u; return 0; } + +bool use_audit(void) { + static int cached_use = -1; + + if (cached_use < 0) { + int fd; + + fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_AUDIT); + if (fd < 0) + cached_use = errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT; + else { + cached_use = true; + safe_close(fd); + } + } + + return cached_use; +} diff --git a/src/shared/audit.h b/src/shared/audit.h index 0effc0baa0..b4aecffb30 100644 --- a/src/shared/audit.h +++ b/src/shared/audit.h @@ -27,3 +27,5 @@ int audit_session_from_pid(pid_t pid, uint32_t *id); int audit_loginuid_from_pid(pid_t pid, uid_t *uid); + +bool use_audit(void); diff --git a/src/shared/barrier.h b/src/shared/barrier.h index c55e311344..d4ad2a419b 100644 --- a/src/shared/barrier.h +++ b/src/shared/barrier.h @@ -91,6 +91,6 @@ static inline bool barrier_is_aborted(Barrier *b) { } static inline bool barrier_place_and_sync(Barrier *b) { - barrier_place(b); + (void)barrier_place(b); return barrier_sync(b); } diff --git a/src/shared/base-filesystem.c b/src/shared/base-filesystem.c index 4c65a495d3..73907c6354 100644 --- a/src/shared/base-filesystem.c +++ b/src/shared/base-filesystem.c @@ -58,10 +58,8 @@ int base_filesystem_create(const char *root) { int r; fd = open(root, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); - if (fd < 0) { - log_error("Failed to open root file system: %m"); - return -errno; - } + if (fd < 0) + return log_error_errno(errno, "Failed to open root file system: %m"); for (i = 0; i < ELEMENTSOF(table); i ++) { if (faccessat(fd, table[i].dir, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) @@ -95,19 +93,15 @@ int base_filesystem_create(const char *root) { continue; r = symlinkat(target, fd, table[i].dir); - if (r < 0 && errno != EEXIST) { - log_error("Failed to create symlink at %s/%s: %m", root, table[i].dir); - return -errno; - } + if (r < 0 && errno != EEXIST) + return log_error_errno(errno, "Failed to create symlink at %s/%s: %m", root, table[i].dir); continue; } RUN_WITH_UMASK(0000) r = mkdirat(fd, table[i].dir, table[i].mode); - if (r < 0 && errno != EEXIST) { - log_error("Failed to create directory at %s/%s: %m", root, table[i].dir); - return -errno; - } + if (r < 0 && errno != EEXIST) + return log_error_errno(errno, "Failed to create directory at %s/%s: %m", root, table[i].dir); } return 0; diff --git a/src/shared/calendarspec.c b/src/shared/calendarspec.c index 7efcf7b371..19ae8a3233 100644 --- a/src/shared/calendarspec.c +++ b/src/shared/calendarspec.c @@ -24,6 +24,8 @@ #include "calendarspec.h" +#define BITS_WEEKDAYS 127 + static void free_chain(CalendarComponent *c) { CalendarComponent *n; @@ -120,7 +122,7 @@ static void fix_year(CalendarComponent *c) { int calendar_spec_normalize(CalendarSpec *c) { assert(c); - if (c->weekdays_bits <= 0 || c->weekdays_bits >= 127) + if (c->weekdays_bits <= 0 || c->weekdays_bits >= BITS_WEEKDAYS) c->weekdays_bits = -1; fix_year(c->year); @@ -154,7 +156,7 @@ _pure_ static bool chain_valid(CalendarComponent *c, int from, int to) { _pure_ bool calendar_spec_valid(CalendarSpec *c) { assert(c); - if (c->weekdays_bits > 127) + if (c->weekdays_bits > BITS_WEEKDAYS) return false; if (!chain_valid(c->year, 1970, 2199)) @@ -194,7 +196,7 @@ static void format_weekdays(FILE *f, const CalendarSpec *c) { assert(f); assert(c); - assert(c->weekdays_bits > 0 && c->weekdays_bits <= 127); + assert(c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS); for (x = 0, l = -1; x < (int) ELEMENTSOF(days); x++) { @@ -259,7 +261,7 @@ int calendar_spec_to_string(const CalendarSpec *c, char **p) { if (!f) return -ENOMEM; - if (c->weekdays_bits > 0 && c->weekdays_bits <= 127) { + if (c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS) { format_weekdays(f, c); fputc(' ', f); } @@ -880,7 +882,7 @@ static bool matches_weekday(int weekdays_bits, const struct tm *tm) { struct tm t; int k; - if (weekdays_bits < 0 || weekdays_bits >= 127) + if (weekdays_bits < 0 || weekdays_bits >= BITS_WEEKDAYS) return true; t = *tm; diff --git a/src/shared/cap-list.c b/src/shared/cap-list.c new file mode 100644 index 0000000000..56d1488f48 --- /dev/null +++ b/src/shared/cap-list.c @@ -0,0 +1,62 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2014 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 <linux/capability.h> +#include <string.h> + +#include "util.h" +#include "cap-list.h" +#include "missing.h" + +static const struct capability_name* lookup_capability(register const char *str, register unsigned int len); + +#include "cap-to-name.h" +#include "cap-from-name.h" + +const char *capability_to_name(int id) { + + if (id < 0) + return NULL; + + if (id >= (int) ELEMENTSOF(capability_names)) + return NULL; + + return capability_names[id]; +} + +int capability_from_name(const char *name) { + const struct capability_name *sc; + int r, i; + + assert(name); + + /* Try to parse numeric capability */ + r = safe_atoi(name, &i); + if (r >= 0 && i >= 0) + return i; + + /* Try to parse string capability */ + sc = lookup_capability(name, strlen(name)); + if (!sc) + return -EINVAL; + + return sc->id; +} diff --git a/src/core/condition.h b/src/shared/cap-list.h index 6dd77bb658..c699e466a7 100644 --- a/src/core/condition.h +++ b/src/shared/cap-list.h @@ -5,7 +5,7 @@ /*** This file is part of systemd. - Copyright 2010 Lennart Poettering + Copyright 2014 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 @@ -21,6 +21,5 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "condition-util.h" - -bool condition_test_list(const char *unit, Condition *c); +const char *capability_to_name(int id); +int capability_from_name(const char *name); diff --git a/src/shared/capability.c b/src/shared/capability.c index d2b901337f..65d7e038a7 100644 --- a/src/shared/capability.c +++ b/src/shared/capability.c @@ -227,37 +227,25 @@ int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) { * binary has the capability configured in the file system, * which we want to avoid. */ - if (setresgid(gid, gid, gid) < 0) { - log_error("Failed change group ID: %m"); - return -errno; - } + if (setresgid(gid, gid, gid) < 0) + return log_error_errno(errno, "Failed to change group ID: %m"); - if (setgroups(0, NULL) < 0) { - log_error("Failed to drop auxiliary groups list: %m"); - return -errno; - } + if (setgroups(0, NULL) < 0) + return log_error_errno(errno, "Failed to drop auxiliary groups list: %m"); - if (prctl(PR_SET_KEEPCAPS, 1) < 0) { - log_error("Failed to enable keep capabilities flag: %m"); - return -errno; - } + if (prctl(PR_SET_KEEPCAPS, 1) < 0) + return log_error_errno(errno, "Failed to enable keep capabilities flag: %m"); r = setresuid(uid, uid, uid); - if (r < 0) { - log_error("Failed change user ID: %m"); - return -errno; - } + if (r < 0) + return log_error_errno(errno, "Failed to change user ID: %m"); - if (prctl(PR_SET_KEEPCAPS, 0) < 0) { - log_error("Failed to disable keep capabilities flag: %m"); - return -errno; - } + if (prctl(PR_SET_KEEPCAPS, 0) < 0) + return log_error_errno(errno, "Failed to disable keep capabilities flag: %m"); r = capability_bounding_set_drop(~keep_capabilities, true); - if (r < 0) { - log_error("Failed to drop capabilities: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to drop capabilities: %m"); d = cap_init(); if (!d) @@ -273,15 +261,31 @@ int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) { if (cap_set_flag(d, CAP_EFFECTIVE, j, bits, CAP_SET) < 0 || cap_set_flag(d, CAP_PERMITTED, j, bits, CAP_SET) < 0) { - log_error("Failed to enable capabilities bits: %m"); + log_error_errno(errno, "Failed to enable capabilities bits: %m"); return -errno; } } - if (cap_set_proc(d) < 0) { - log_error("Failed to increase capabilities: %m"); + if (cap_set_proc(d) < 0) + return log_error_errno(errno, "Failed to increase capabilities: %m"); + + return 0; +} + +int drop_capability(cap_value_t cv) { + _cleanup_cap_free_ cap_t tmp_cap = NULL; + + tmp_cap = cap_get_proc(); + if (!tmp_cap) + return -errno; + + if ((cap_set_flag(tmp_cap, CAP_INHERITABLE, 1, &cv, CAP_CLEAR) < 0) || + (cap_set_flag(tmp_cap, CAP_PERMITTED, 1, &cv, CAP_CLEAR) < 0) || + (cap_set_flag(tmp_cap, CAP_EFFECTIVE, 1, &cv, CAP_CLEAR) < 0)) + return -errno; + + if (cap_set_proc(tmp_cap) < 0) return -errno; - } return 0; } diff --git a/src/shared/capability.h b/src/shared/capability.h index 3e6d9995f5..6f2f6f997d 100644 --- a/src/shared/capability.h +++ b/src/shared/capability.h @@ -34,6 +34,8 @@ int capability_bounding_set_drop_usermode(uint64_t drop); int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilites); +int drop_capability(cap_value_t cv); + DEFINE_TRIVIAL_CLEANUP_FUNC(cap_t, cap_free); #define _cleanup_cap_free_ _cleanup_(cap_freep) diff --git a/src/shared/cgroup-util.c b/src/shared/cgroup-util.c index da8e885226..1bcba01887 100644 --- a/src/shared/cgroup-util.c +++ b/src/shared/cgroup-util.c @@ -195,7 +195,7 @@ int cg_kill(const char *controller, const char *path, int sig, bool sigcont, boo if (ret >= 0 && errno != ESRCH) ret = -errno; } else { - if (sigcont) + if (sigcont && sig != SIGKILL) kill(pid, SIGCONT); if (ret == 0) @@ -682,7 +682,7 @@ int cg_set_group_access( assert(path); - if (mode != (mode_t) -1) + if (mode != MODE_INVALID) mode &= 0777; r = cg_get_path(controller, path, NULL, &fs); @@ -704,10 +704,10 @@ int cg_set_task_access( assert(path); - if (mode == (mode_t) -1 && uid == (uid_t) -1 && gid == (gid_t) -1) + if (mode == MODE_INVALID && uid == UID_INVALID && gid == GID_INVALID) return 0; - if (mode != (mode_t) -1) + if (mode != MODE_INVALID) mode &= 0666; r = cg_get_path(controller, path, "cgroup.procs", &fs); @@ -1624,7 +1624,7 @@ int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask ma return 0; } -int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t pid) { +int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) { CGroupControllerMask bit = 1; const char *n; int r; @@ -1634,8 +1634,18 @@ int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t return r; NULSTR_FOREACH(n, mask_names) { - if (supported & bit) + + if (supported & bit) { + const char *p = NULL; + + if (path_callback) + p = path_callback(bit, userdata); + + if (!p) + p = path; + cg_attach_fallback(n, path, pid); + } bit <<= 1; } @@ -1643,7 +1653,7 @@ int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t return 0; } -int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids) { +int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids, cg_migrate_callback_t path_callback, void *userdata) { Iterator i; void *pidp; int r = 0; @@ -1652,7 +1662,7 @@ int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, pid_t pid = PTR_TO_LONG(pidp); int q; - q = cg_attach_everywhere(supported, path, pid); + q = cg_attach_everywhere(supported, path, pid, path_callback, userdata); if (q < 0) r = q; } diff --git a/src/shared/cgroup-util.h b/src/shared/cgroup-util.h index aca4e44c46..5e1e445c33 100644 --- a/src/shared/cgroup-util.h +++ b/src/shared/cgroup-util.h @@ -34,7 +34,8 @@ typedef enum CGroupControllerMask { CGROUP_CPUACCT = 2, CGROUP_BLKIO = 4, CGROUP_MEMORY = 8, - CGROUP_DEVICE = 16 + CGROUP_DEVICE = 16, + _CGROUP_CONTROLLER_MASK_ALL = 31 } CGroupControllerMask; /* @@ -125,8 +126,8 @@ int cg_slice_to_path(const char *unit, char **ret); typedef const char* (*cg_migrate_callback_t)(CGroupControllerMask mask, void *userdata); int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask mask, const char *path); -int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t pid); -int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids); +int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t pid, cg_migrate_callback_t callback, void *userdata); +int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids, cg_migrate_callback_t callback, void *userdata); int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, const char *to, cg_migrate_callback_t callback, void *userdata); int cg_trim_everywhere(CGroupControllerMask supported, const char *path, bool delete_root); diff --git a/src/shared/clean-ipc.c b/src/shared/clean-ipc.c index cb1722614e..39ab645133 100644 --- a/src/shared/clean-ipc.c +++ b/src/shared/clean-ipc.c @@ -44,7 +44,7 @@ static int clean_sysvipc_shm(uid_t delete_uid) { if (errno == ENOENT) return 0; - log_warning("Failed to open /proc/sysvipc/shm: %m"); + log_warning_errno(errno, "Failed to open /proc/sysvipc/shm: %m"); return -errno; } @@ -78,7 +78,7 @@ static int clean_sysvipc_shm(uid_t delete_uid) { if (errno == EIDRM || errno == EINVAL) continue; - log_warning("Failed to remove SysV shared memory segment %i: %m", shmid); + log_warning_errno(errno, "Failed to remove SysV shared memory segment %i: %m", shmid); ret = -errno; } } @@ -86,7 +86,7 @@ static int clean_sysvipc_shm(uid_t delete_uid) { return ret; fail: - log_warning("Failed to read /proc/sysvipc/shm: %m"); + log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m"); return -errno; } @@ -101,7 +101,7 @@ static int clean_sysvipc_sem(uid_t delete_uid) { if (errno == ENOENT) return 0; - log_warning("Failed to open /proc/sysvipc/sem: %m"); + log_warning_errno(errno, "Failed to open /proc/sysvipc/sem: %m"); return -errno; } @@ -130,7 +130,7 @@ static int clean_sysvipc_sem(uid_t delete_uid) { if (errno == EIDRM || errno == EINVAL) continue; - log_warning("Failed to remove SysV semaphores object %i: %m", semid); + log_warning_errno(errno, "Failed to remove SysV semaphores object %i: %m", semid); ret = -errno; } } @@ -138,7 +138,7 @@ static int clean_sysvipc_sem(uid_t delete_uid) { return ret; fail: - log_warning("Failed to read /proc/sysvipc/sem: %m"); + log_warning_errno(errno, "Failed to read /proc/sysvipc/sem: %m"); return -errno; } @@ -153,7 +153,7 @@ static int clean_sysvipc_msg(uid_t delete_uid) { if (errno == ENOENT) return 0; - log_warning("Failed to open /proc/sysvipc/msg: %m"); + log_warning_errno(errno, "Failed to open /proc/sysvipc/msg: %m"); return -errno; } @@ -183,7 +183,7 @@ static int clean_sysvipc_msg(uid_t delete_uid) { if (errno == EIDRM || errno == EINVAL) continue; - log_warning("Failed to remove SysV message queue %i: %m", msgid); + log_warning_errno(errno, "Failed to remove SysV message queue %i: %m", msgid); ret = -errno; } } @@ -191,7 +191,7 @@ static int clean_sysvipc_msg(uid_t delete_uid) { return ret; fail: - log_warning("Failed to read /proc/sysvipc/msg: %m"); + log_warning_errno(errno, "Failed to read /proc/sysvipc/msg: %m"); return -errno; } @@ -211,7 +211,7 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) { if (errno == ENOENT) continue; - log_warning("Failed to stat() POSIX shared memory segment %s: %m", de->d_name); + log_warning_errno(errno, "Failed to stat() POSIX shared memory segment %s: %m", de->d_name); ret = -errno; continue; } @@ -225,7 +225,7 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) { kid = xopendirat(dirfd(dir), de->d_name, O_NOFOLLOW|O_NOATIME); if (!kid) { if (errno != ENOENT) { - log_warning("Failed to enter shared memory directory %s: %m", de->d_name); + log_warning_errno(errno, "Failed to enter shared memory directory %s: %m", de->d_name); ret = -errno; } } else { @@ -239,7 +239,7 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) { if (errno == ENOENT) continue; - log_warning("Failed to remove POSIX shared memory directory %s: %m", de->d_name); + log_warning_errno(errno, "Failed to remove POSIX shared memory directory %s: %m", de->d_name); ret = -errno; } } else { @@ -249,7 +249,7 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) { if (errno == ENOENT) continue; - log_warning("Failed to remove POSIX shared memory segment %s: %m", de->d_name); + log_warning_errno(errno, "Failed to remove POSIX shared memory segment %s: %m", de->d_name); ret = -errno; } } @@ -258,7 +258,7 @@ static int clean_posix_shm_internal(DIR *dir, uid_t uid) { return ret; fail: - log_warning("Failed to read /dev/shm: %m"); + log_warning_errno(errno, "Failed to read /dev/shm: %m"); return -errno; } @@ -270,7 +270,7 @@ static int clean_posix_shm(uid_t uid) { if (errno == ENOENT) return 0; - log_warning("Failed to open /dev/shm: %m"); + log_warning_errno(errno, "Failed to open /dev/shm: %m"); return -errno; } @@ -287,7 +287,7 @@ static int clean_posix_mq(uid_t uid) { if (errno == ENOENT) return 0; - log_warning("Failed to open /dev/mqueue: %m"); + log_warning_errno(errno, "Failed to open /dev/mqueue: %m"); return -errno; } @@ -302,7 +302,7 @@ static int clean_posix_mq(uid_t uid) { if (errno == ENOENT) continue; - log_warning("Failed to stat() MQ segment %s: %m", de->d_name); + log_warning_errno(errno, "Failed to stat() MQ segment %s: %m", de->d_name); ret = -errno; continue; } @@ -317,7 +317,7 @@ static int clean_posix_mq(uid_t uid) { if (errno == ENOENT) continue; - log_warning("Failed to unlink POSIX message queue %s: %m", fn); + log_warning_errno(errno, "Failed to unlink POSIX message queue %s: %m", fn); ret = -errno; } } @@ -325,7 +325,7 @@ static int clean_posix_mq(uid_t uid) { return ret; fail: - log_warning("Failed to read /dev/mqueue: %m"); + log_warning_errno(errno, "Failed to read /dev/mqueue: %m"); return -errno; } diff --git a/src/shared/condition-util.c b/src/shared/condition-util.c deleted file mode 100644 index ff4a8ecd15..0000000000 --- a/src/shared/condition-util.c +++ /dev/null @@ -1,267 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2010 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 <stdlib.h> -#include <errno.h> -#include <string.h> -#include <unistd.h> -#include <sys/statvfs.h> -#include <fnmatch.h> - -#include "systemd/sd-id128.h" -#include "util.h" -#include "condition-util.h" -#include "virt.h" -#include "path-util.h" -#include "fileio.h" -#include "unit.h" -#include "architecture.h" - -Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) { - Condition *c; - - assert(type < _CONDITION_TYPE_MAX); - - c = new0(Condition, 1); - if (!c) - return NULL; - - c->type = type; - c->trigger = trigger; - c->negate = negate; - - if (parameter) { - c->parameter = strdup(parameter); - if (!c->parameter) { - free(c); - return NULL; - } - } - - return c; -} - -void condition_free(Condition *c) { - assert(c); - - free(c->parameter); - free(c); -} - -void condition_free_list(Condition *first) { - Condition *c, *n; - - LIST_FOREACH_SAFE(conditions, c, n, first) - condition_free(c); -} - -bool condition_test_kernel_command_line(Condition *c) { - char *line, *word = NULL; - const char *w, *state; - bool equal; - int r; - size_t l, pl; - bool found = false; - - assert(c); - assert(c->parameter); - assert(c->type == CONDITION_KERNEL_COMMAND_LINE); - - r = proc_cmdline(&line); - if (r < 0) - log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r)); - if (r <= 0) - return c->negate; - - equal = !!strchr(c->parameter, '='); - pl = strlen(c->parameter); - - FOREACH_WORD_QUOTED(w, l, line, state) { - - free(word); - word = strndup(w, l); - if (!word) - break; - - if (equal) { - if (streq(word, c->parameter)) { - found = true; - break; - } - } else { - if (startswith(word, c->parameter) && (word[pl] == '=' || word[pl] == 0)) { - found = true; - break; - } - } - - } - if (!isempty(state)) - log_warning("Trailing garbage and the end of kernel commandline, ignoring."); - - free(word); - free(line); - - return found == !c->negate; -} - -bool condition_test_virtualization(Condition *c) { - int b, v; - const char *id; - - assert(c); - assert(c->parameter); - assert(c->type == CONDITION_VIRTUALIZATION); - - v = detect_virtualization(&id); - if (v < 0) { - log_warning("Failed to detect virtualization, ignoring: %s", strerror(-v)); - return c->negate; - } - - /* First, compare with yes/no */ - b = parse_boolean(c->parameter); - - if (v > 0 && b > 0) - return !c->negate; - - if (v == 0 && b == 0) - return !c->negate; - - /* Then, compare categorization */ - if (v == VIRTUALIZATION_VM && streq(c->parameter, "vm")) - return !c->negate; - - if (v == VIRTUALIZATION_CONTAINER && streq(c->parameter, "container")) - return !c->negate; - - /* Finally compare id */ - return (v > 0 && streq(c->parameter, id)) == !c->negate; -} - -bool condition_test_architecture(Condition *c) { - Architecture a, b; - - assert(c); - assert(c->parameter); - assert(c->type == CONDITION_ARCHITECTURE); - - a = uname_architecture(); - if (a < 0) - return c->negate; - - if (streq(c->parameter, "native")) - b = native_architecture(); - else - b = architecture_from_string(c->parameter); - - if (b < 0) - return c->negate; - - return (a == b) == !c->negate; -} - -bool condition_test_host(Condition *c) { - _cleanup_free_ char *h = NULL; - sd_id128_t x, y; - int r; - - assert(c); - assert(c->parameter); - assert(c->type == CONDITION_HOST); - - if (sd_id128_from_string(c->parameter, &x) >= 0) { - - r = sd_id128_get_machine(&y); - if (r < 0) - return c->negate; - - return sd_id128_equal(x, y) == !c->negate; - } - - h = gethostname_malloc(); - if (!h) - return c->negate; - - return (fnmatch(c->parameter, h, FNM_CASEFOLD) == 0) == !c->negate; -} - -bool condition_test_ac_power(Condition *c) { - int r; - - assert(c); - assert(c->parameter); - assert(c->type == CONDITION_AC_POWER); - - r = parse_boolean(c->parameter); - if (r < 0) - return !c->negate; - - return ((on_ac_power() != 0) == !!r) == !c->negate; -} - -void condition_dump(Condition *c, FILE *f, const char *prefix) { - assert(c); - assert(f); - - if (!prefix) - prefix = ""; - - fprintf(f, - "%s\t%s: %s%s%s %s\n", - prefix, - condition_type_to_string(c->type), - c->trigger ? "|" : "", - c->negate ? "!" : "", - c->parameter, - c->state < 0 ? "failed" : c->state > 0 ? "succeeded" : "untested"); -} - -void condition_dump_list(Condition *first, FILE *f, const char *prefix) { - Condition *c; - - LIST_FOREACH(conditions, c, first) - condition_dump(c, f, prefix); -} - -static const char* const condition_type_table[_CONDITION_TYPE_MAX] = { - [CONDITION_PATH_EXISTS] = "ConditionPathExists", - [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob", - [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory", - [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink", - [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint", - [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite", - [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty", - [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty", - [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable", - [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine", - [CONDITION_VIRTUALIZATION] = "ConditionVirtualization", - [CONDITION_SECURITY] = "ConditionSecurity", - [CONDITION_CAPABILITY] = "ConditionCapability", - [CONDITION_HOST] = "ConditionHost", - [CONDITION_AC_POWER] = "ConditionACPower", - [CONDITION_ARCHITECTURE] = "ConditionArchitecture", - [CONDITION_NEEDS_UPDATE] = "ConditionNeedsUpdate", - [CONDITION_FIRST_BOOT] = "ConditionFirstBoot", - [CONDITION_NULL] = "ConditionNull" -}; - -DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType); diff --git a/src/shared/condition.c b/src/shared/condition.c new file mode 100644 index 0000000000..dcbf9a7e86 --- /dev/null +++ b/src/shared/condition.c @@ -0,0 +1,527 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 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 <stdlib.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <sys/statvfs.h> +#include <fnmatch.h> + +#include "sd-id128.h" +#include "util.h" +#include "virt.h" +#include "path-util.h" +#include "fileio.h" +#include "unit.h" +#include "architecture.h" +#include "smack-util.h" +#include "apparmor-util.h" +#include "ima-util.h" +#include "selinux-util.h" +#include "audit.h" +#include "condition.h" +#include "cap-list.h" + +Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) { + Condition *c; + int r; + + assert(type >= 0); + assert(type < _CONDITION_TYPE_MAX); + assert(!parameter == (type == CONDITION_NULL)); + + c = new0(Condition, 1); + if (!c) + return NULL; + + c->type = type; + c->trigger = trigger; + c->negate = negate; + + r = free_and_strdup(&c->parameter, parameter); + if (r < 0) { + free(c); + return NULL; + } + + return c; +} + +void condition_free(Condition *c) { + assert(c); + + free(c->parameter); + free(c); +} + +void condition_free_list(Condition *first) { + Condition *c, *n; + + LIST_FOREACH_SAFE(conditions, c, n, first) + condition_free(c); +} + +static int condition_test_kernel_command_line(Condition *c) { + _cleanup_free_ char *line = NULL; + const char *p; + bool equal; + int r; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_KERNEL_COMMAND_LINE); + + r = proc_cmdline(&line); + if (r < 0) + return r; + + equal = !!strchr(c->parameter, '='); + p = line; + + for (;;) { + _cleanup_free_ char *word = NULL; + bool found; + + r = unquote_first_word(&p, &word, true); + if (r < 0) + return r; + if (r == 0) + break; + + if (equal) + found = streq(word, c->parameter); + else { + const char *f; + + f = startswith(word, c->parameter); + found = f && (*f == '=' || *f == 0); + } + + if (found) + return true; + } + + return false; +} + +static int condition_test_virtualization(Condition *c) { + int b, v; + const char *id; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_VIRTUALIZATION); + + v = detect_virtualization(&id); + if (v < 0) + return v; + + /* First, compare with yes/no */ + b = parse_boolean(c->parameter); + + if (v > 0 && b > 0) + return true; + + if (v == 0 && b == 0) + return true; + + /* Then, compare categorization */ + if (v == VIRTUALIZATION_VM && streq(c->parameter, "vm")) + return true; + + if (v == VIRTUALIZATION_CONTAINER && streq(c->parameter, "container")) + return true; + + /* Finally compare id */ + return v > 0 && streq(c->parameter, id); +} + +static int condition_test_architecture(Condition *c) { + int a, b; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_ARCHITECTURE); + + a = uname_architecture(); + if (a < 0) + return a; + + if (streq(c->parameter, "native")) + b = native_architecture(); + else + b = architecture_from_string(c->parameter); + if (b < 0) + return b; + + return a == b; +} + +static int condition_test_host(Condition *c) { + _cleanup_free_ char *h = NULL; + sd_id128_t x, y; + int r; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_HOST); + + if (sd_id128_from_string(c->parameter, &x) >= 0) { + + r = sd_id128_get_machine(&y); + if (r < 0) + return r; + + return sd_id128_equal(x, y); + } + + h = gethostname_malloc(); + if (!h) + return -ENOMEM; + + return fnmatch(c->parameter, h, FNM_CASEFOLD) == 0; +} + +static int condition_test_ac_power(Condition *c) { + int r; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_AC_POWER); + + r = parse_boolean(c->parameter); + if (r < 0) + return r; + + return (on_ac_power() != 0) == !!r; +} + +static int condition_test_security(Condition *c) { + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_SECURITY); + + if (streq(c->parameter, "selinux")) + return mac_selinux_use(); + if (streq(c->parameter, "smack")) + return mac_smack_use(); + if (streq(c->parameter, "apparmor")) + return mac_apparmor_use(); + if (streq(c->parameter, "audit")) + return use_audit(); + if (streq(c->parameter, "ima")) + return use_ima(); + + return false; +} + +static int condition_test_capability(Condition *c) { + _cleanup_fclose_ FILE *f = NULL; + int value; + char line[LINE_MAX]; + unsigned long long capabilities = -1; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_CAPABILITY); + + /* If it's an invalid capability, we don't have it */ + value = capability_from_name(c->parameter); + if (value < 0) + return -EINVAL; + + /* If it's a valid capability we default to assume + * that we have it */ + + f = fopen("/proc/self/status", "re"); + if (!f) + return -errno; + + while (fgets(line, sizeof(line), f)) { + truncate_nl(line); + + if (startswith(line, "CapBnd:")) { + (void) sscanf(line+7, "%llx", &capabilities); + break; + } + } + + return !!(capabilities & (1ULL << value)); +} + +static int condition_test_needs_update(Condition *c) { + const char *p; + struct stat usr, other; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_NEEDS_UPDATE); + + /* If the file system is read-only we shouldn't suggest an update */ + if (path_is_read_only_fs(c->parameter) > 0) + return false; + + /* Any other failure means we should allow the condition to be true, + * so that we rather invoke too many update tools then too + * few. */ + + if (!path_is_absolute(c->parameter)) + return true; + + p = strappenda(c->parameter, "/.updated"); + if (lstat(p, &other) < 0) + return true; + + if (lstat("/usr/", &usr) < 0) + return true; + + return usr.st_mtim.tv_sec > other.st_mtim.tv_sec || + (usr.st_mtim.tv_sec == other.st_mtim.tv_sec && usr.st_mtim.tv_nsec > other.st_mtim.tv_nsec); +} + +static int condition_test_first_boot(Condition *c) { + int r; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_FIRST_BOOT); + + r = parse_boolean(c->parameter); + if (r < 0) + return r; + + return (access("/run/systemd/first-boot", F_OK) >= 0) == !!r; +} + +static int condition_test_path_exists(Condition *c) { + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_PATH_EXISTS); + + return access(c->parameter, F_OK) >= 0; +} + +static int condition_test_path_exists_glob(Condition *c) { + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_PATH_EXISTS_GLOB); + + return glob_exists(c->parameter) > 0; +} + +static int condition_test_path_is_directory(Condition *c) { + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_PATH_IS_DIRECTORY); + + return is_dir(c->parameter, true) > 0; +} + +static int condition_test_path_is_symbolic_link(Condition *c) { + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_PATH_IS_SYMBOLIC_LINK); + + return is_symlink(c->parameter) > 0; +} + +static int condition_test_path_is_mount_point(Condition *c) { + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_PATH_IS_MOUNT_POINT); + + return path_is_mount_point(c->parameter, true) > 0; +} + +static int condition_test_path_is_read_write(Condition *c) { + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_PATH_IS_READ_WRITE); + + return path_is_read_only_fs(c->parameter) <= 0; +} + +static int condition_test_directory_not_empty(Condition *c) { + int r; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_DIRECTORY_NOT_EMPTY); + + r = dir_is_empty(c->parameter); + return r <= 0 && r != -ENOENT; +} + +static int condition_test_file_not_empty(Condition *c) { + struct stat st; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_FILE_NOT_EMPTY); + + return (stat(c->parameter, &st) >= 0 && + S_ISREG(st.st_mode) && + st.st_size > 0); +} + +static int condition_test_file_is_executable(Condition *c) { + struct stat st; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_FILE_IS_EXECUTABLE); + + return (stat(c->parameter, &st) >= 0 && + S_ISREG(st.st_mode) && + (st.st_mode & 0111)); +} + +static int condition_test_null(Condition *c) { + assert(c); + assert(c->type == CONDITION_NULL); + + /* Note that during parsing we already evaluate the string and + * store it in c->negate */ + return true; +} + +int condition_test(Condition *c) { + + static int (*const condition_tests[_CONDITION_TYPE_MAX])(Condition *c) = { + [CONDITION_PATH_EXISTS] = condition_test_path_exists, + [CONDITION_PATH_EXISTS_GLOB] = condition_test_path_exists_glob, + [CONDITION_PATH_IS_DIRECTORY] = condition_test_path_is_directory, + [CONDITION_PATH_IS_SYMBOLIC_LINK] = condition_test_path_is_symbolic_link, + [CONDITION_PATH_IS_MOUNT_POINT] = condition_test_path_is_mount_point, + [CONDITION_PATH_IS_READ_WRITE] = condition_test_path_is_read_write, + [CONDITION_DIRECTORY_NOT_EMPTY] = condition_test_directory_not_empty, + [CONDITION_FILE_NOT_EMPTY] = condition_test_file_not_empty, + [CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable, + [CONDITION_KERNEL_COMMAND_LINE] = condition_test_kernel_command_line, + [CONDITION_VIRTUALIZATION] = condition_test_virtualization, + [CONDITION_SECURITY] = condition_test_security, + [CONDITION_CAPABILITY] = condition_test_capability, + [CONDITION_HOST] = condition_test_host, + [CONDITION_AC_POWER] = condition_test_ac_power, + [CONDITION_ARCHITECTURE] = condition_test_architecture, + [CONDITION_NEEDS_UPDATE] = condition_test_needs_update, + [CONDITION_FIRST_BOOT] = condition_test_first_boot, + [CONDITION_NULL] = condition_test_null, + }; + + int r, b; + + assert(c); + assert(c->type >= 0); + assert(c->type < _CONDITION_TYPE_MAX); + + r = condition_tests[c->type](c); + if (r < 0) { + c->result = CONDITION_ERROR; + return r; + } + + b = (r > 0) == !c->negate; + c->result = b ? CONDITION_SUCCEEDED : CONDITION_FAILED; + return b; +} + +void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) { + assert(c); + assert(f); + + if (!prefix) + prefix = ""; + + fprintf(f, + "%s\t%s: %s%s%s %s\n", + prefix, + to_string(c->type), + c->trigger ? "|" : "", + c->negate ? "!" : "", + c->parameter, + condition_result_to_string(c->result)); +} + +void condition_dump_list(Condition *first, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) { + Condition *c; + + LIST_FOREACH(conditions, c, first) + condition_dump(c, f, prefix, to_string); +} + +static const char* const condition_type_table[_CONDITION_TYPE_MAX] = { + [CONDITION_ARCHITECTURE] = "ConditionArchitecture", + [CONDITION_VIRTUALIZATION] = "ConditionVirtualization", + [CONDITION_HOST] = "ConditionHost", + [CONDITION_KERNEL_COMMAND_LINE] = "ConditionKernelCommandLine", + [CONDITION_SECURITY] = "ConditionSecurity", + [CONDITION_CAPABILITY] = "ConditionCapability", + [CONDITION_AC_POWER] = "ConditionACPower", + [CONDITION_NEEDS_UPDATE] = "ConditionNeedsUpdate", + [CONDITION_FIRST_BOOT] = "ConditionFirstBoot", + [CONDITION_PATH_EXISTS] = "ConditionPathExists", + [CONDITION_PATH_EXISTS_GLOB] = "ConditionPathExistsGlob", + [CONDITION_PATH_IS_DIRECTORY] = "ConditionPathIsDirectory", + [CONDITION_PATH_IS_SYMBOLIC_LINK] = "ConditionPathIsSymbolicLink", + [CONDITION_PATH_IS_MOUNT_POINT] = "ConditionPathIsMountPoint", + [CONDITION_PATH_IS_READ_WRITE] = "ConditionPathIsReadWrite", + [CONDITION_DIRECTORY_NOT_EMPTY] = "ConditionDirectoryNotEmpty", + [CONDITION_FILE_NOT_EMPTY] = "ConditionFileNotEmpty", + [CONDITION_FILE_IS_EXECUTABLE] = "ConditionFileIsExecutable", + [CONDITION_NULL] = "ConditionNull" +}; + +DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType); + +static const char* const assert_type_table[_CONDITION_TYPE_MAX] = { + [CONDITION_ARCHITECTURE] = "AssertArchitecture", + [CONDITION_VIRTUALIZATION] = "AssertVirtualization", + [CONDITION_HOST] = "AssertHost", + [CONDITION_KERNEL_COMMAND_LINE] = "AssertKernelCommandLine", + [CONDITION_SECURITY] = "AssertSecurity", + [CONDITION_CAPABILITY] = "AssertCapability", + [CONDITION_AC_POWER] = "AssertACPower", + [CONDITION_NEEDS_UPDATE] = "AssertNeedsUpdate", + [CONDITION_FIRST_BOOT] = "AssertFirstBoot", + [CONDITION_PATH_EXISTS] = "AssertPathExists", + [CONDITION_PATH_EXISTS_GLOB] = "AssertPathExistsGlob", + [CONDITION_PATH_IS_DIRECTORY] = "AssertPathIsDirectory", + [CONDITION_PATH_IS_SYMBOLIC_LINK] = "AssertPathIsSymbolicLink", + [CONDITION_PATH_IS_MOUNT_POINT] = "AssertPathIsMountPoint", + [CONDITION_PATH_IS_READ_WRITE] = "AssertPathIsReadWrite", + [CONDITION_DIRECTORY_NOT_EMPTY] = "AssertDirectoryNotEmpty", + [CONDITION_FILE_NOT_EMPTY] = "AssertFileNotEmpty", + [CONDITION_FILE_IS_EXECUTABLE] = "AssertFileIsExecutable", + [CONDITION_NULL] = "AssertNull" +}; + +DEFINE_STRING_TABLE_LOOKUP(assert_type, ConditionType); + +static const char* const condition_result_table[_CONDITION_RESULT_MAX] = { + [CONDITION_UNTESTED] = "untested", + [CONDITION_SUCCEEDED] = "succeeded", + [CONDITION_FAILED] = "failed", + [CONDITION_ERROR] = "error", +}; + +DEFINE_STRING_TABLE_LOOKUP(condition_result, ConditionResult); diff --git a/src/shared/condition-util.h b/src/shared/condition.h index 047fdbfd86..28d1d94ff4 100644 --- a/src/shared/condition-util.h +++ b/src/shared/condition.h @@ -28,6 +28,17 @@ #include "macro.h" typedef enum ConditionType { + CONDITION_ARCHITECTURE, + CONDITION_VIRTUALIZATION, + CONDITION_HOST, + CONDITION_KERNEL_COMMAND_LINE, + CONDITION_SECURITY, + CONDITION_CAPABILITY, + CONDITION_AC_POWER, + + CONDITION_NEEDS_UPDATE, + CONDITION_FIRST_BOOT, + CONDITION_PATH_EXISTS, CONDITION_PATH_EXISTS_GLOB, CONDITION_PATH_IS_DIRECTORY, @@ -37,29 +48,31 @@ typedef enum ConditionType { CONDITION_DIRECTORY_NOT_EMPTY, CONDITION_FILE_NOT_EMPTY, CONDITION_FILE_IS_EXECUTABLE, - CONDITION_KERNEL_COMMAND_LINE, - CONDITION_VIRTUALIZATION, - CONDITION_SECURITY, - CONDITION_CAPABILITY, - CONDITION_HOST, - CONDITION_AC_POWER, - CONDITION_ARCHITECTURE, - CONDITION_NEEDS_UPDATE, - CONDITION_FIRST_BOOT, + CONDITION_NULL, + _CONDITION_TYPE_MAX, _CONDITION_TYPE_INVALID = -1 } ConditionType; +typedef enum ConditionResult { + CONDITION_UNTESTED, + CONDITION_SUCCEEDED, + CONDITION_FAILED, + CONDITION_ERROR, + _CONDITION_RESULT_MAX, + _CONDITION_RESULT_INVALID = -1 +} ConditionResult; + typedef struct Condition { - ConditionType type; + ConditionType type:8; bool trigger:1; bool negate:1; - char *parameter; + ConditionResult result:6; - int state; + char *parameter; LIST_FIELDS(struct Condition, conditions); } Condition; @@ -68,14 +81,16 @@ Condition* condition_new(ConditionType type, const char *parameter, bool trigger void condition_free(Condition *c); void condition_free_list(Condition *c); -bool condition_test_kernel_command_line(Condition *c); -bool condition_test_virtualization(Condition *c); -bool condition_test_architecture(Condition *c); -bool condition_test_host(Condition *c); -bool condition_test_ac_power(Condition *c); +int condition_test(Condition *c); -void condition_dump(Condition *c, FILE *f, const char *prefix); -void condition_dump_list(Condition *c, FILE *f, const char *prefix); +void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)); +void condition_dump_list(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)); const char* condition_type_to_string(ConditionType t) _const_; -int condition_type_from_string(const char *s) _pure_; +ConditionType condition_type_from_string(const char *s) _pure_; + +const char* assert_type_to_string(ConditionType t) _const_; +ConditionType assert_type_from_string(const char *s) _pure_; + +const char* condition_result_to_string(ConditionResult r) _const_; +ConditionResult condition_result_from_string(const char *s) _pure_; diff --git a/src/shared/conf-files.c b/src/shared/conf-files.c index e6ee97a978..51f4e0105c 100644 --- a/src/shared/conf-files.c +++ b/src/shared/conf-files.c @@ -118,8 +118,8 @@ static int conf_files_list_strv_internal(char ***strv, const char *suffix, const if (r == -ENOMEM) { return r; } else if (r < 0) - log_debug("Failed to search for files in %s: %s", - *p, strerror(-r)); + log_debug_errno(r, "Failed to search for files in %s: %m", + *p); } files = hashmap_get_strv(fh); diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c index ee6de653e1..5fe983a847 100644 --- a/src/shared/conf-parser.c +++ b/src/shared/conf-parser.c @@ -27,6 +27,7 @@ #include <netinet/ether.h> #include "conf-parser.h" +#include "conf-files.h" #include "util.h" #include "macro.h" #include "strv.h" @@ -37,10 +38,16 @@ #include "exit-status.h" #include "sd-messages.h" -int log_syntax_internal(const char *unit, int level, - const char *file, unsigned line, const char *func, - const char *config_file, unsigned config_line, - int error, const char *format, ...) { +int log_syntax_internal( + const char *unit, + int level, + const char *file, + int line, + const char *func, + const char *config_file, + unsigned config_line, + int error, + const char *format, ...) { _cleanup_free_ char *msg = NULL; int r; @@ -54,22 +61,22 @@ int log_syntax_internal(const char *unit, int level, if (unit) r = log_struct_internal(level, + error > 0 ? error : EINVAL, file, line, func, getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s", unit, - MESSAGE_ID(SD_MESSAGE_CONFIG_ERROR), + LOG_MESSAGE_ID(SD_MESSAGE_CONFIG_ERROR), "CONFIG_FILE=%s", config_file, "CONFIG_LINE=%u", config_line, - "ERRNO=%d", error > 0 ? error : EINVAL, - "MESSAGE=[%s:%u] %s", config_file, config_line, msg, + LOG_MESSAGE("[%s:%u] %s", config_file, config_line, msg), NULL); else r = log_struct_internal(level, + error > 0 ? error : EINVAL, file, line, func, - MESSAGE_ID(SD_MESSAGE_CONFIG_ERROR), + LOG_MESSAGE_ID(SD_MESSAGE_CONFIG_ERROR), "CONFIG_FILE=%s", config_file, "CONFIG_LINE=%u", config_line, - "ERRNO=%d", error > 0 ? error : EINVAL, - "MESSAGE=[%s:%u] %s", config_file, config_line, msg, + LOG_MESSAGE("[%s:%u] %s", config_file, config_line, msg), NULL); return r; @@ -360,7 +367,7 @@ int config_parse(const char *unit, if (feof(f)) break; - log_error("Failed to read configuration file '%s': %m", filename); + log_error_errno(errno, "Failed to read configuration file '%s': %m", filename); return -errno; } @@ -421,8 +428,8 @@ int config_parse(const char *unit, if (r < 0) { if (warn) - log_warning("Failed to parse file '%s': %s", - filename, strerror(-r)); + log_warning_errno(r, "Failed to parse file '%s': %m", + filename); return r; } } @@ -430,6 +437,37 @@ int config_parse(const char *unit, return 0; } +/* Parse each config file in the specified directories. */ +int config_parse_many(const char *conf_file, + const char *conf_file_dirs, + const char *sections, + ConfigItemLookup lookup, + const void *table, + bool relaxed, + void *userdata) { + _cleanup_strv_free_ char **files = NULL; + char **fn; + int r; + + r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); + if (r < 0) + return r; + + if (conf_file) { + r = config_parse(NULL, conf_file, NULL, sections, lookup, table, relaxed, false, true, userdata); + if (r < 0) + return r; + } + + STRV_FOREACH(fn, files) { + r = config_parse(NULL, *fn, NULL, sections, lookup, table, relaxed, false, true, userdata); + if (r < 0) + return r; + } + + return 0; +} + #define DEFINE_PARSER(type, vartype, conv_func) \ int config_parse_##type(const char *unit, \ const char *filename, \ diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h index 62f2a01e5e..2507a44444 100644 --- a/src/shared/conf-parser.h +++ b/src/shared/conf-parser.h @@ -92,6 +92,14 @@ int config_parse(const char *unit, bool warn, void *userdata); +int config_parse_many(const char *conf_file, /* possibly NULL */ + const char *conf_file_dirs, /* nulstr */ + const char *sections, /* nulstr */ + ConfigItemLookup lookup, + const void *table, + bool relaxed, + void *userdata); + /* Generic parsers */ int config_parse_int(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_unsigned(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); @@ -111,10 +119,16 @@ int config_parse_mode(const char *unit, const char *filename, unsigned line, con int config_parse_log_facility(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_log_level(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int log_syntax_internal(const char *unit, int level, - const char *file, unsigned line, const char *func, - const char *config_file, unsigned config_line, - int error, const char *format, ...) _printf_(9, 10); +int log_syntax_internal( + const char *unit, + int level, + const char *file, + int line, + const char *func, + const char *config_file, + unsigned config_line, + int error, + const char *format, ...) _printf_(9, 10); #define log_syntax(unit, level, config_file, config_line, error, ...) \ log_syntax_internal(unit, level, \ diff --git a/src/shared/copy.c b/src/shared/copy.c index 3744797b95..b8b1ba1866 100644 --- a/src/shared/copy.c +++ b/src/shared/copy.c @@ -19,40 +19,65 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <sys/sendfile.h> + #include "util.h" #include "copy.h" int copy_bytes(int fdf, int fdt, off_t max_bytes) { + bool try_sendfile = true; + assert(fdf >= 0); assert(fdt >= 0); for (;;) { - char buf[PIPE_BUF]; - ssize_t n, k; - size_t m = sizeof(buf); + size_t m = PIPE_BUF; + ssize_t n; if (max_bytes != (off_t) -1) { if (max_bytes <= 0) - return -E2BIG; + return -EFBIG; if ((off_t) m > max_bytes) m = (size_t) max_bytes; } - n = read(fdf, buf, m); - if (n < 0) - return -errno; - if (n == 0) - break; + /* First try sendfile(), unless we already tried */ + if (try_sendfile) { + + n = sendfile(fdt, fdf, NULL, m); + if (n < 0) { + if (errno != EINVAL && errno != ENOSYS) + return -errno; + + try_sendfile = false; + /* use fallback below */ + } else if (n == 0) /* EOF */ + break; + else if (n > 0) + /* Succcess! */ + goto next; + } + + /* As a fallback just copy bits by hand */ + { + char buf[m]; + int r; - errno = 0; - k = loop_write(fdt, buf, n, false); - if (k < 0) - return k; - if (k != n) - return errno ? -errno : -EIO; + n = read(fdf, buf, m); + if (n < 0) + return -errno; + if (n == 0) /* EOF */ + break; + + r = loop_write(fdt, buf, n, false); + if (r < 0) + return r; + + } + next: if (max_bytes != (off_t) -1) { assert(max_bytes >= n); max_bytes -= n; @@ -262,34 +287,39 @@ int copy_tree(const char *from, const char *to, bool merge) { return -ENOTSUP; } -int copy_file(const char *from, const char *to, int flags, mode_t mode) { - _cleanup_close_ int fdf = -1, fdt = -1; - int r; +int copy_file_fd(const char *from, int fdt) { + _cleanup_close_ int fdf = -1; assert(from); - assert(to); + assert(fdt >= 0); fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY); if (fdf < 0) return -errno; + return copy_bytes(fdf, fdt, (off_t) -1); +} + +int copy_file(const char *from, const char *to, int flags, mode_t mode) { + int fdt, r; + + assert(from); + assert(to); + fdt = open(to, flags|O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, mode); if (fdt < 0) return -errno; - r = copy_bytes(fdf, fdt, (off_t) -1); + r = copy_file_fd(from, fdt); if (r < 0) { + close(fdt); unlink(to); return r; } - r = close(fdt); - fdt = -1; - - if (r < 0) { - r = -errno; - unlink(to); - return r; + if (close(fdt) < 0) { + unlink_noerrno(to); + return -errno; } return 0; diff --git a/src/shared/copy.h b/src/shared/copy.h index 0bf2598f60..62932112af 100644 --- a/src/shared/copy.h +++ b/src/shared/copy.h @@ -21,6 +21,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <stdbool.h> +#include <sys/types.h> + +int copy_file_fd(const char *from, int to); int copy_file(const char *from, const char *to, int flags, mode_t mode); int copy_tree(const char *from, const char *to, bool merge); int copy_bytes(int fdf, int fdt, off_t max_bytes); diff --git a/src/shared/def.h b/src/shared/def.h index 92394e8183..96c45a6b72 100644 --- a/src/shared/def.h +++ b/src/shared/def.h @@ -61,17 +61,17 @@ "/usr/lib/kbd/keymaps/\0" #endif -#define UNIX_SYSTEM_BUS_PATH "unix:path=/var/run/dbus/system_bus_socket" -#define KERNEL_SYSTEM_BUS_PATH "kernel:path=/dev/kdbus/0-system/bus" +#define UNIX_SYSTEM_BUS_ADDRESS "unix:path=/var/run/dbus/system_bus_socket" +#define KERNEL_SYSTEM_BUS_ADDRESS "kernel:path=/sys/fs/kdbus/0-system/bus" #ifdef ENABLE_KDBUS -# define DEFAULT_SYSTEM_BUS_PATH KERNEL_SYSTEM_BUS_PATH ";" UNIX_SYSTEM_BUS_PATH +# define DEFAULT_SYSTEM_BUS_ADDRESS KERNEL_SYSTEM_BUS_ADDRESS ";" UNIX_SYSTEM_BUS_ADDRESS #else -# define DEFAULT_SYSTEM_BUS_PATH UNIX_SYSTEM_BUS_PATH +# define DEFAULT_SYSTEM_BUS_ADDRESS UNIX_SYSTEM_BUS_ADDRESS #endif -#define UNIX_USER_BUS_FMT "unix:path=%s/bus" -#define KERNEL_USER_BUS_FMT "kernel:path=/dev/kdbus/"UID_FMT"-user/bus" +#define UNIX_USER_BUS_ADDRESS_FMT "unix:path=%s/bus" +#define KERNEL_USER_BUS_ADDRESS_FMT "kernel:path=/sys/fs/kdbus/"UID_FMT"-user/bus" #define PLYMOUTH_SOCKET { \ .un.sun_family = AF_UNIX, \ diff --git a/src/shared/env-util.c b/src/shared/env-util.c index d90b878d19..fbdc73dd2c 100644 --- a/src/shared/env-util.c +++ b/src/shared/env-util.c @@ -425,7 +425,7 @@ char **strv_env_clean_log(char **e, const char *unit_id, const char *message) { if (!env_assignment_is_valid(*p)) { if (message) - log_error_unit(unit_id, "Ignoring invalid environment '%s': %s", *p, message); + log_unit_error(unit_id, "Ignoring invalid environment '%s': %s", *p, message); free(*p); continue; } diff --git a/src/shared/ether-addr-util.h b/src/shared/ether-addr-util.h new file mode 100644 index 0000000000..7033138788 --- /dev/null +++ b/src/shared/ether-addr-util.h @@ -0,0 +1,27 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Tom Gundersen + + 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 <net/ethernet.h> + +#define ETHER_ADDR_FORMAT_STR "%02X%02X%02X%02X%02X%02X" +#define ETHER_ADDR_FORMAT_VAL(x) (x).ether_addr_octet[0], (x).ether_addr_octet[1], (x).ether_addr_octet[2], (x).ether_addr_octet[3], (x).ether_addr_octet[4], (x).ether_addr_octet[5] diff --git a/src/shared/exit-status.h b/src/shared/exit-status.h index f719580426..1d774f25dc 100644 --- a/src/shared/exit-status.h +++ b/src/shared/exit-status.h @@ -78,6 +78,7 @@ typedef enum ExitStatus { EXIT_MAKE_STARTER, EXIT_CHOWN, EXIT_BUS_ENDPOINT, + EXIT_SMACK_PROCESS_LABEL, } ExitStatus; typedef enum ExitStatusLevel { diff --git a/src/shared/fileio.c b/src/shared/fileio.c index 38028b972e..ff6b1a7ed7 100644 --- a/src/shared/fileio.c +++ b/src/shared/fileio.c @@ -20,12 +20,12 @@ ***/ #include <unistd.h> -#include <sys/sendfile.h> -#include "fileio.h" + #include "util.h" #include "strv.h" #include "utf8.h" #include "ctype.h" +#include "fileio.h" int write_string_stream(FILE *f, const char *line) { assert(f); @@ -66,7 +66,7 @@ int write_string_file_no_create(const char *fn, const char *line) { assert(line); /* We manually build our own version of fopen(..., "we") that - * without O_CREAT */ + * works without O_CREAT */ fd = open(fn, O_WRONLY|O_CLOEXEC|O_NOCTTY); if (fd < 0) return -errno; @@ -94,20 +94,10 @@ int write_string_file_atomic(const char *fn, const char *line) { fchmod_umask(fileno(f), 0644); - errno = 0; - fputs(line, f); - if (!endswith(line, "\n")) - fputc('\n', f); - - fflush(f); - - if (ferror(f)) - r = errno ? -errno : -EIO; - else { + r = write_string_stream(f, line); + if (r >= 0) { if (rename(p, fn) < 0) r = -errno; - else - r = 0; } if (r < 0) @@ -144,77 +134,6 @@ int read_one_line_file(const char *fn, char **line) { return 0; } -ssize_t sendfile_full(int out_fd, const char *fn) { - _cleanup_fclose_ FILE *f; - struct stat st; - int r; - ssize_t s; - - size_t n, l; - _cleanup_free_ char *buf = NULL; - - assert(out_fd > 0); - assert(fn); - - f = fopen(fn, "re"); - if (!f) - return -errno; - - r = fstat(fileno(f), &st); - if (r < 0) - return -errno; - - s = sendfile(out_fd, fileno(f), NULL, st.st_size); - if (s < 0) - if (errno == EINVAL || errno == ENOSYS) { - /* continue below */ - } else - return -errno; - else - return s; - - /* sendfile() failed, fall back to read/write */ - - /* Safety check */ - if (st.st_size > 4*1024*1024) - return -E2BIG; - - n = st.st_size > 0 ? st.st_size : LINE_MAX; - l = 0; - - while (true) { - char *t; - size_t k; - - t = realloc(buf, n); - if (!t) - return -ENOMEM; - - buf = t; - k = fread(buf + l, 1, n - l, f); - - if (k <= 0) { - if (ferror(f)) - return -errno; - - break; - } - - l += k; - n *= 2; - - /* Safety check */ - if (n > 4*1024*1024) - return -E2BIG; - } - - r = write(out_fd, buf, l); - if (r < 0) - return -errno; - - return (ssize_t) l; -} - int read_full_stream(FILE *f, char **contents, size_t *size) { size_t n, l; _cleanup_free_ char *buf = NULL; diff --git a/src/shared/fileio.h b/src/shared/fileio.h index c256915799..5ae51c1e28 100644 --- a/src/shared/fileio.h +++ b/src/shared/fileio.h @@ -33,7 +33,6 @@ int write_string_file_atomic(const char *fn, const char *line); int read_one_line_file(const char *fn, char **line); int read_full_file(const char *fn, char **contents, size_t *size); int read_full_stream(FILE *f, char **contents, size_t *size); -ssize_t sendfile_full(int out_fd, const char *fn); int parse_env_file(const char *fname, const char *separator, ...) _sentinel_; int load_env_file(FILE *f, const char *fname, const char *separator, char ***l); diff --git a/src/shared/generator.c b/src/shared/generator.c index 414470be1c..465e5f6cc8 100644 --- a/src/shared/generator.c +++ b/src/shared/generator.c @@ -52,12 +52,10 @@ int generator_write_fsck_deps( r = fsck_exists(fstype); if (r == -ENOENT) { /* treat missing check as essentially OK */ - log_debug("Checking was requested for %s, but fsck.%s does not exist: %s", what, fstype, strerror(-r)); + log_debug_errno(r, "Checking was requested for %s, but fsck.%s does not exist: %m", what, fstype); return 0; - } else if (r < 0) { - log_warning("Checking was requested for %s, but fsck.%s cannot be used: %s", what, fstype, strerror(-r)); - return r; - } + } else if (r < 0) + return log_warning_errno(r, "Checking was requested for %s, but fsck.%s cannot be used: %m", what, fstype); } if (streq(where, "/")) { @@ -66,10 +64,8 @@ int generator_write_fsck_deps( lnk = strappenda(dest, "/" SPECIAL_LOCAL_FS_TARGET ".wants/systemd-fsck-root.service"); mkdir_parents(lnk, 0755); - if (symlink(SYSTEM_DATA_UNIT_PATH "/systemd-fsck-root.service", lnk) < 0) { - log_error("Failed to create symlink %s: %m", lnk); - return -errno; - } + if (symlink(SYSTEM_DATA_UNIT_PATH "/systemd-fsck-root.service", lnk) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); } else { _cleanup_free_ char *fsck = NULL; diff --git a/src/shared/hashmap.c b/src/shared/hashmap.c index 6f5f8204dd..5b329e0851 100644 --- a/src/shared/hashmap.c +++ b/src/shared/hashmap.c @@ -4,6 +4,7 @@ This file is part of systemd. Copyright 2010 Lennart Poettering + Copyright 2014 Michal Schmidt 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 @@ -26,49 +27,250 @@ #include "util.h" #include "hashmap.h" +#include "set.h" #include "macro.h" #include "siphash24.h" +#include "strv.h" +#include "list.h" #include "mempool.h" -#define INITIAL_N_BUCKETS 31 - -struct hashmap_entry { +/* + * Implementation of hashmaps. + * Addressing: open + * - uses less RAM compared to closed addressing (chaining), because + * our entries are small (especially in Sets, which tend to contain + * the majority of entries in systemd). + * Collision resolution: Robin Hood + * - tends to equalize displacement of entries from their optimal buckets. + * Probe sequence: linear + * - though theoretically worse than random probing/uniform hashing/double + * hashing, it is good for cache locality. + * + * References: + * Celis, P. 1986. Robin Hood Hashing. + * Ph.D. Dissertation. University of Waterloo, Waterloo, Ont., Canada, Canada. + * https://cs.uwaterloo.ca/research/tr/1986/CS-86-14.pdf + * - The results are derived for random probing. Suggests deletion with + * tombstones and two mean-centered search methods. None of that works + * well for linear probing. + * + * Janson, S. 2005. Individual displacements for linear probing hashing with different insertion policies. + * ACM Trans. Algorithms 1, 2 (October 2005), 177-213. + * DOI=10.1145/1103963.1103964 http://doi.acm.org/10.1145/1103963.1103964 + * http://www.math.uu.se/~svante/papers/sj157.pdf + * - Applies to Robin Hood with linear probing. Contains remarks on + * the unsuitability of mean-centered search with linear probing. + * + * Viola, A. 2005. Exact distribution of individual displacements in linear probing hashing. + * ACM Trans. Algorithms 1, 2 (October 2005), 214-242. + * DOI=10.1145/1103963.1103965 http://doi.acm.org/10.1145/1103963.1103965 + * - Similar to Janson. Note that Viola writes about C_{m,n} (number of probes + * in a successful search), and Janson writes about displacement. C = d + 1. + * + * Goossaert, E. 2013. Robin Hood hashing: backward shift deletion. + * http://codecapsule.com/2013/11/17/robin-hood-hashing-backward-shift-deletion/ + * - Explanation of backward shift deletion with pictures. + * + * Khuong, P. 2013. The Other Robin Hood Hashing. + * http://www.pvk.ca/Blog/2013/11/26/the-other-robin-hood-hashing/ + * - Short summary of random vs. linear probing, and tombstones vs. backward shift. + */ + +/* + * XXX Ideas for improvement: + * For unordered hashmaps, randomize iteration order, similarly to Perl: + * http://blog.booking.com/hardening-perls-hash-function.html + */ + +/* INV_KEEP_FREE = 1 / (1 - max_load_factor) + * e.g. 1 / (1 - 0.8) = 5 ... keep one fifth of the buckets free. */ +#define INV_KEEP_FREE 5U + +/* Fields common to entries of all hashmap/set types */ +struct hashmap_base_entry { const void *key; +}; + +/* Entry types for specific hashmap/set types + * hashmap_base_entry must be at the beginning of each entry struct. */ + +struct plain_hashmap_entry { + struct hashmap_base_entry b; void *value; - struct hashmap_entry *bucket_next, *bucket_previous; - struct hashmap_entry *iterate_next, *iterate_previous; }; -struct Hashmap { - const struct hash_ops *hash_ops; +struct ordered_hashmap_entry { + struct plain_hashmap_entry p; + unsigned iterate_next, iterate_previous; +}; - struct hashmap_entry *iterate_list_head, *iterate_list_tail; +struct set_entry { + struct hashmap_base_entry b; +}; - struct hashmap_entry ** buckets; - unsigned n_buckets, n_entries; +/* In several functions it is advantageous to have the hash table extended + * virtually by a couple of additional buckets. We reserve special index values + * for these "swap" buckets. */ +#define _IDX_SWAP_BEGIN (UINT_MAX - 3) +#define IDX_PUT (_IDX_SWAP_BEGIN + 0) +#define IDX_TMP (_IDX_SWAP_BEGIN + 1) +#define _IDX_SWAP_END (_IDX_SWAP_BEGIN + 2) - uint8_t hash_key[HASH_KEY_SIZE]; - bool from_pool:1; +#define IDX_FIRST (UINT_MAX - 1) /* special index for freshly initialized iterators */ +#define IDX_NIL UINT_MAX /* special index value meaning "none" or "end" */ + +assert_cc(IDX_FIRST == _IDX_SWAP_END); +assert_cc(IDX_FIRST == _IDX_ITERATOR_FIRST); + +/* Storage space for the "swap" buckets. + * All entry types can fit into a ordered_hashmap_entry. */ +struct swap_entries { + struct ordered_hashmap_entry e[_IDX_SWAP_END - _IDX_SWAP_BEGIN]; }; -struct hashmap_tile { - Hashmap h; - struct hashmap_entry *initial_buckets[INITIAL_N_BUCKETS]; +/* Distance from Initial Bucket */ +typedef uint8_t dib_raw_t; +#define DIB_RAW_OVERFLOW ((dib_raw_t)0xfdU) /* indicates DIB value is greater than representable */ +#define DIB_RAW_REHASH ((dib_raw_t)0xfeU) /* entry yet to be rehashed during in-place resize */ +#define DIB_RAW_FREE ((dib_raw_t)0xffU) /* a free bucket */ +#define DIB_RAW_INIT ((char)DIB_RAW_FREE) /* a byte to memset a DIB store with when initializing */ + +#define DIB_FREE UINT_MAX + +#ifdef ENABLE_HASHMAP_DEBUG +struct hashmap_debug_info { + LIST_FIELDS(struct hashmap_debug_info, debug_list); + unsigned max_entries; /* high watermark of n_entries */ + + /* who allocated this hashmap */ + int line; + const char *file; + const char *func; + + /* fields to detect modification while iterating */ + unsigned put_count; /* counts puts into the hashmap */ + unsigned rem_count; /* counts removals from hashmap */ + unsigned last_rem_idx; /* remembers last removal index */ }; -static DEFINE_MEMPOOL(hashmap_pool, struct hashmap_tile, 8); -static DEFINE_MEMPOOL(hashmap_entry_pool, struct hashmap_entry, 64); +/* Tracks all existing hashmaps. Get at it from gdb. See sd_dump_hashmaps.py */ +static LIST_HEAD(struct hashmap_debug_info, hashmap_debug_list); -#ifdef VALGRIND +#define HASHMAP_DEBUG_FIELDS struct hashmap_debug_info debug; -__attribute__((destructor)) static void cleanup_pools(void) { - /* Be nice to valgrind */ +#else /* !ENABLE_HASHMAP_DEBUG */ +#define HASHMAP_DEBUG_FIELDS +#endif /* ENABLE_HASHMAP_DEBUG */ - mempool_drop(&hashmap_entry_pool); - mempool_drop(&hashmap_pool); -} +enum HashmapType { + HASHMAP_TYPE_PLAIN, + HASHMAP_TYPE_ORDERED, + HASHMAP_TYPE_SET, + _HASHMAP_TYPE_MAX +}; -#endif +struct _packed_ indirect_storage { + char *storage; /* where buckets and DIBs are stored */ + uint8_t hash_key[HASH_KEY_SIZE]; /* hash key; changes during resize */ + + unsigned n_entries; /* number of stored entries */ + unsigned n_buckets; /* number of buckets */ + + unsigned idx_lowest_entry; /* Index below which all buckets are free. + Makes "while(hashmap_steal_first())" loops + O(n) instead of O(n^2) for unordered hashmaps. */ + uint8_t _pad[3]; /* padding for the whole HashmapBase */ + /* The bitfields in HashmapBase complete the alignment of the whole thing. */ +}; + +struct direct_storage { + /* This gives us 39 bytes on 64bit, or 35 bytes on 32bit. + * That's room for 4 set_entries + 4 DIB bytes + 3 unused bytes on 64bit, + * or 7 set_entries + 7 DIB bytes + 0 unused bytes on 32bit. */ + char storage[sizeof(struct indirect_storage)]; +}; + +#define DIRECT_BUCKETS(entry_t) \ + (sizeof(struct direct_storage) / (sizeof(entry_t) + sizeof(dib_raw_t))) + +/* We should be able to store at least one entry directly. */ +assert_cc(DIRECT_BUCKETS(struct ordered_hashmap_entry) >= 1); + +/* We have 3 bits for n_direct_entries. */ +assert_cc(DIRECT_BUCKETS(struct set_entry) < (1 << 3)); + +/* Hashmaps with directly stored entries all use this shared hash key. + * It's no big deal if the key is guessed, because there can be only + * a handful of directly stored entries in a hashmap. When a hashmap + * outgrows direct storage, it gets its own key for indirect storage. */ +static uint8_t shared_hash_key[HASH_KEY_SIZE]; +static bool shared_hash_key_initialized; + +/* Fields that all hashmap/set types must have */ +struct HashmapBase { + const struct hash_ops *hash_ops; /* hash and compare ops to use */ + + union _packed_ { + struct indirect_storage indirect; /* if has_indirect */ + struct direct_storage direct; /* if !has_indirect */ + }; + + enum HashmapType type:2; /* HASHMAP_TYPE_* */ + bool has_indirect:1; /* whether indirect storage is used */ + unsigned n_direct_entries:3; /* Number of entries in direct storage. + * Only valid if !has_indirect. */ + bool from_pool:1; /* whether was allocated from mempool */ + HASHMAP_DEBUG_FIELDS /* optional hashmap_debug_info */ +}; + +/* Specific hash types + * HashmapBase must be at the beginning of each hashmap struct. */ + +struct Hashmap { + struct HashmapBase b; +}; + +struct OrderedHashmap { + struct HashmapBase b; + unsigned iterate_list_head, iterate_list_tail; +}; + +struct Set { + struct HashmapBase b; +}; + +DEFINE_MEMPOOL(hashmap_pool, Hashmap, 8); +DEFINE_MEMPOOL(ordered_hashmap_pool, OrderedHashmap, 8); +/* No need for a separate Set pool */ +assert_cc(sizeof(Hashmap) == sizeof(Set)); + +struct hashmap_type_info { + size_t head_size; + size_t entry_size; + struct mempool *mempool; + unsigned n_direct_buckets; +}; + +static const struct hashmap_type_info hashmap_type_info[_HASHMAP_TYPE_MAX] = { + [HASHMAP_TYPE_PLAIN] = { + .head_size = sizeof(Hashmap), + .entry_size = sizeof(struct plain_hashmap_entry), + .mempool = &hashmap_pool, + .n_direct_buckets = DIRECT_BUCKETS(struct plain_hashmap_entry), + }, + [HASHMAP_TYPE_ORDERED] = { + .head_size = sizeof(OrderedHashmap), + .entry_size = sizeof(struct ordered_hashmap_entry), + .mempool = &ordered_hashmap_pool, + .n_direct_buckets = DIRECT_BUCKETS(struct ordered_hashmap_entry), + }, + [HASHMAP_TYPE_SET] = { + .head_size = sizeof(Set), + .entry_size = sizeof(struct set_entry), + .mempool = &hashmap_pool, + .n_direct_buckets = DIRECT_BUCKETS(struct set_entry), + }, +}; unsigned long string_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) { uint64_t u; @@ -138,9 +340,44 @@ const struct hash_ops devt_hash_ops = { }; #endif -static unsigned bucket_hash(Hashmap *h, const void *p) { - return (unsigned) (h->hash_ops->hash(p, h->hash_key) % h->n_buckets); +static unsigned n_buckets(HashmapBase *h) { + return h->has_indirect ? h->indirect.n_buckets + : hashmap_type_info[h->type].n_direct_buckets; +} + +static unsigned n_entries(HashmapBase *h) { + return h->has_indirect ? h->indirect.n_entries + : h->n_direct_entries; +} + +static void n_entries_inc(HashmapBase *h) { + if (h->has_indirect) + h->indirect.n_entries++; + else + h->n_direct_entries++; +} + +static void n_entries_dec(HashmapBase *h) { + if (h->has_indirect) + h->indirect.n_entries--; + else + h->n_direct_entries--; +} + +static char *storage_ptr(HashmapBase *h) { + return h->has_indirect ? h->indirect.storage + : h->direct.storage; +} + +static uint8_t *hash_key(HashmapBase *h) { + return h->has_indirect ? h->indirect.hash_key + : shared_hash_key; +} + +static unsigned base_bucket_hash(HashmapBase *h, const void *p) { + return (unsigned) (h->hash_ops->hash(p, hash_key(h)) % n_buckets(h)); } +#define bucket_hash(h, p) base_bucket_hash(HASHMAP_BASE(h), p) static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) { static uint8_t current[HASH_KEY_SIZE]; @@ -161,147 +398,484 @@ static void get_hash_key(uint8_t hash_key[HASH_KEY_SIZE], bool reuse_is_ok) { memcpy(hash_key, current, sizeof(current)); } -Hashmap *hashmap_new(const struct hash_ops *hash_ops) { - bool b; - struct hashmap_tile *ht; - Hashmap *h; +static struct hashmap_base_entry *bucket_at(HashmapBase *h, unsigned idx) { + return (struct hashmap_base_entry*) + (storage_ptr(h) + idx * hashmap_type_info[h->type].entry_size); +} + +static struct plain_hashmap_entry *plain_bucket_at(Hashmap *h, unsigned idx) { + return (struct plain_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx); +} + +static struct ordered_hashmap_entry *ordered_bucket_at(OrderedHashmap *h, unsigned idx) { + return (struct ordered_hashmap_entry*) bucket_at(HASHMAP_BASE(h), idx); +} - b = is_main_thread(); +static struct set_entry *set_bucket_at(Set *h, unsigned idx) { + return (struct set_entry*) bucket_at(HASHMAP_BASE(h), idx); +} - if (b) { - ht = mempool_alloc_tile(&hashmap_pool); - if (!ht) - return NULL; +static struct ordered_hashmap_entry *bucket_at_swap(struct swap_entries *swap, unsigned idx) { + return &swap->e[idx - _IDX_SWAP_BEGIN]; +} - memzero(ht, sizeof(struct hashmap_tile)); - } else { - ht = malloc0(sizeof(struct hashmap_tile)); +/* Returns a pointer to the bucket at index idx. + * Understands real indexes and swap indexes, hence "_virtual". */ +static struct hashmap_base_entry *bucket_at_virtual(HashmapBase *h, struct swap_entries *swap, + unsigned idx) { + if (idx < _IDX_SWAP_BEGIN) + return bucket_at(h, idx); + + if (idx < _IDX_SWAP_END) + return &bucket_at_swap(swap, idx)->p.b; + + assert_not_reached("Invalid index"); +} + +static dib_raw_t *dib_raw_ptr(HashmapBase *h) { + return (dib_raw_t*) + (storage_ptr(h) + hashmap_type_info[h->type].entry_size * n_buckets(h)); +} + +static unsigned bucket_distance(HashmapBase *h, unsigned idx, unsigned from) { + return idx >= from ? idx - from + : n_buckets(h) + idx - from; +} + +static unsigned bucket_calculate_dib(HashmapBase *h, unsigned idx, dib_raw_t raw_dib) { + unsigned initial_bucket; + + if (raw_dib == DIB_RAW_FREE) + return DIB_FREE; + + if (_likely_(raw_dib < DIB_RAW_OVERFLOW)) + return raw_dib; + + /* + * Having an overflow DIB value is very unlikely. The hash function + * would have to be bad. For example, in a table of size 2^24 filled + * to load factor 0.9 the maximum observed DIB is only about 60. + * In theory (assuming I used Maxima correctly), for an infinite size + * hash table with load factor 0.8 the probability of a given entry + * having DIB > 40 is 1.9e-8. + * This returns the correct DIB value by recomputing the hash value in + * the unlikely case. XXX Hitting this case could be a hint to rehash. + */ + initial_bucket = bucket_hash(h, bucket_at(h, idx)->key); + return bucket_distance(h, idx, initial_bucket); +} + +static void bucket_set_dib(HashmapBase *h, unsigned idx, unsigned dib) { + dib_raw_ptr(h)[idx] = dib != DIB_FREE ? MIN(dib, DIB_RAW_OVERFLOW) : DIB_RAW_FREE; +} + +static unsigned skip_free_buckets(HashmapBase *h, unsigned idx) { + dib_raw_t *dibs; + + dibs = dib_raw_ptr(h); + + for ( ; idx < n_buckets(h); idx++) + if (dibs[idx] != DIB_RAW_FREE) + return idx; + + return IDX_NIL; +} + +static void bucket_mark_free(HashmapBase *h, unsigned idx) { + memset(bucket_at(h, idx), 0, hashmap_type_info[h->type].entry_size); + bucket_set_dib(h, idx, DIB_FREE); +} + +static void bucket_move_entry(HashmapBase *h, struct swap_entries *swap, + unsigned from, unsigned to) { + struct hashmap_base_entry *e_from, *e_to; + + assert(from != to); - if (!ht) - return NULL; + e_from = bucket_at_virtual(h, swap, from); + e_to = bucket_at_virtual(h, swap, to); + + memcpy(e_to, e_from, hashmap_type_info[h->type].entry_size); + + if (h->type == HASHMAP_TYPE_ORDERED) { + OrderedHashmap *lh = (OrderedHashmap*) h; + struct ordered_hashmap_entry *le, *le_to; + + le_to = (struct ordered_hashmap_entry*) e_to; + + if (le_to->iterate_next != IDX_NIL) { + le = (struct ordered_hashmap_entry*) + bucket_at_virtual(h, swap, le_to->iterate_next); + le->iterate_previous = to; + } + + if (le_to->iterate_previous != IDX_NIL) { + le = (struct ordered_hashmap_entry*) + bucket_at_virtual(h, swap, le_to->iterate_previous); + le->iterate_next = to; + } + + if (lh->iterate_list_head == from) + lh->iterate_list_head = to; + if (lh->iterate_list_tail == from) + lh->iterate_list_tail = to; } +} - h = &ht->h; - h->hash_ops = hash_ops ? hash_ops : &trivial_hash_ops; +static unsigned next_idx(HashmapBase *h, unsigned idx) { + return (idx + 1U) % n_buckets(h); +} - h->n_buckets = INITIAL_N_BUCKETS; - h->n_entries = 0; - h->iterate_list_head = h->iterate_list_tail = NULL; +static unsigned prev_idx(HashmapBase *h, unsigned idx) { + return (n_buckets(h) + idx - 1U) % n_buckets(h); +} - h->buckets = ht->initial_buckets; +static void *entry_value(HashmapBase *h, struct hashmap_base_entry *e) { + switch (h->type) { - h->from_pool = b; + case HASHMAP_TYPE_PLAIN: + case HASHMAP_TYPE_ORDERED: + return ((struct plain_hashmap_entry*)e)->value; - get_hash_key(h->hash_key, true); + case HASHMAP_TYPE_SET: + return (void*) e->key; - return h; + default: + assert_not_reached("Unknown hashmap type"); + } } -int hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops) { - Hashmap *q; +static void base_remove_entry(HashmapBase *h, unsigned idx) { + unsigned left, right, prev, dib; + dib_raw_t raw_dib, *dibs; - assert(h); + dibs = dib_raw_ptr(h); + assert(dibs[idx] != DIB_RAW_FREE); - if (*h) - return 0; +#ifdef ENABLE_HASHMAP_DEBUG + h->debug.rem_count++; + h->debug.last_rem_idx = idx; +#endif - q = hashmap_new(hash_ops); - if (!q) - return -ENOMEM; + left = idx; + /* Find the stop bucket ("right"). It is either free or has DIB == 0. */ + for (right = next_idx(h, left); ; right = next_idx(h, right)) { + raw_dib = dibs[right]; + if (raw_dib == 0 || raw_dib == DIB_RAW_FREE) + break; + + /* The buckets are not supposed to be all occupied and with DIB > 0. + * That would mean we could make everyone better off by shifting them + * backward. This scenario is impossible. */ + assert(left != right); + } - *h = q; - return 0; + if (h->type == HASHMAP_TYPE_ORDERED) { + OrderedHashmap *lh = (OrderedHashmap*) h; + struct ordered_hashmap_entry *le = ordered_bucket_at(lh, idx); + + if (le->iterate_next != IDX_NIL) + ordered_bucket_at(lh, le->iterate_next)->iterate_previous = le->iterate_previous; + else + lh->iterate_list_tail = le->iterate_previous; + + if (le->iterate_previous != IDX_NIL) + ordered_bucket_at(lh, le->iterate_previous)->iterate_next = le->iterate_next; + else + lh->iterate_list_head = le->iterate_next; + } + + /* Now shift all buckets in the interval (left, right) one step backwards */ + for (prev = left, left = next_idx(h, left); left != right; + prev = left, left = next_idx(h, left)) { + dib = bucket_calculate_dib(h, left, dibs[left]); + assert(dib != 0); + bucket_move_entry(h, NULL, left, prev); + bucket_set_dib(h, prev, dib - 1); + } + + bucket_mark_free(h, prev); + n_entries_dec(h); } +#define remove_entry(h, idx) base_remove_entry(HASHMAP_BASE(h), idx) + +static unsigned hashmap_iterate_in_insertion_order(OrderedHashmap *h, Iterator *i) { + struct ordered_hashmap_entry *e; + unsigned idx; -static void link_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) { assert(h); - assert(e); - - /* Insert into hash table */ - e->bucket_next = h->buckets[hash]; - e->bucket_previous = NULL; - if (h->buckets[hash]) - h->buckets[hash]->bucket_previous = e; - h->buckets[hash] = e; - - /* Insert into iteration list */ - e->iterate_previous = h->iterate_list_tail; - e->iterate_next = NULL; - if (h->iterate_list_tail) { - assert(h->iterate_list_head); - h->iterate_list_tail->iterate_next = e; + assert(i); + + if (i->idx == IDX_NIL) + goto at_end; + + if (i->idx == IDX_FIRST && h->iterate_list_head == IDX_NIL) + goto at_end; + + if (i->idx == IDX_FIRST) { + idx = h->iterate_list_head; + e = ordered_bucket_at(h, idx); } else { - assert(!h->iterate_list_head); - h->iterate_list_head = e; + idx = i->idx; + e = ordered_bucket_at(h, idx); + /* + * We allow removing the current entry while iterating, but removal may cause + * a backward shift. The next entry may thus move one bucket to the left. + * To detect when it happens, we remember the key pointer of the entry we were + * going to iterate next. If it does not match, there was a backward shift. + */ + if (e->p.b.key != i->next_key) { + idx = prev_idx(HASHMAP_BASE(h), idx); + e = ordered_bucket_at(h, idx); + } + assert(e->p.b.key == i->next_key); } - h->iterate_list_tail = e; - h->n_entries++; - assert(h->n_entries >= 1); +#ifdef ENABLE_HASHMAP_DEBUG + i->prev_idx = idx; +#endif + + if (e->iterate_next != IDX_NIL) { + struct ordered_hashmap_entry *n; + i->idx = e->iterate_next; + n = ordered_bucket_at(h, i->idx); + i->next_key = n->p.b.key; + } else + i->idx = IDX_NIL; + + return idx; + +at_end: + i->idx = IDX_NIL; + return IDX_NIL; } -static void unlink_entry(Hashmap *h, struct hashmap_entry *e, unsigned hash) { +static unsigned hashmap_iterate_in_internal_order(HashmapBase *h, Iterator *i) { + unsigned idx; + assert(h); - assert(e); + assert(i); - /* Remove from iteration list */ - if (e->iterate_next) - e->iterate_next->iterate_previous = e->iterate_previous; - else - h->iterate_list_tail = e->iterate_previous; + if (i->idx == IDX_NIL) + goto at_end; - if (e->iterate_previous) - e->iterate_previous->iterate_next = e->iterate_next; - else - h->iterate_list_head = e->iterate_next; + if (i->idx == IDX_FIRST) { + /* fast forward to the first occupied bucket */ + if (h->has_indirect) { + i->idx = skip_free_buckets(h, h->indirect.idx_lowest_entry); + h->indirect.idx_lowest_entry = i->idx; + } else + i->idx = skip_free_buckets(h, 0); + + if (i->idx == IDX_NIL) + goto at_end; + } else { + struct hashmap_base_entry *e; + + assert(i->idx > 0); - /* Remove from hash table bucket list */ - if (e->bucket_next) - e->bucket_next->bucket_previous = e->bucket_previous; + e = bucket_at(h, i->idx); + /* + * We allow removing the current entry while iterating, but removal may cause + * a backward shift. The next entry may thus move one bucket to the left. + * To detect when it happens, we remember the key pointer of the entry we were + * going to iterate next. If it does not match, there was a backward shift. + */ + if (e->key != i->next_key) + e = bucket_at(h, --i->idx); - if (e->bucket_previous) - e->bucket_previous->bucket_next = e->bucket_next; + assert(e->key == i->next_key); + } + + idx = i->idx; +#ifdef ENABLE_HASHMAP_DEBUG + i->prev_idx = idx; +#endif + + i->idx = skip_free_buckets(h, i->idx + 1); + if (i->idx != IDX_NIL) + i->next_key = bucket_at(h, i->idx)->key; else - h->buckets[hash] = e->bucket_next; + i->idx = IDX_NIL; + + return idx; - assert(h->n_entries >= 1); - h->n_entries--; +at_end: + i->idx = IDX_NIL; + return IDX_NIL; } -static void remove_entry(Hashmap *h, struct hashmap_entry *e) { - unsigned hash; +static unsigned hashmap_iterate_entry(HashmapBase *h, Iterator *i) { + if (!h) { + i->idx = IDX_NIL; + return IDX_NIL; + } - assert(h); - assert(e); +#ifdef ENABLE_HASHMAP_DEBUG + if (i->idx == IDX_FIRST) { + i->put_count = h->debug.put_count; + i->rem_count = h->debug.rem_count; + } else { + /* While iterating, must not add any new entries */ + assert(i->put_count == h->debug.put_count); + /* ... or remove entries other than the current one */ + assert(i->rem_count == h->debug.rem_count || + (i->rem_count == h->debug.rem_count - 1 && + i->prev_idx == h->debug.last_rem_idx)); + /* Reset our removals counter */ + i->rem_count = h->debug.rem_count; + } +#endif - hash = bucket_hash(h, e->key); - unlink_entry(h, e, hash); + return h->type == HASHMAP_TYPE_ORDERED ? hashmap_iterate_in_insertion_order((OrderedHashmap*) h, i) + : hashmap_iterate_in_internal_order(h, i); +} - if (h->from_pool) - mempool_free_tile(&hashmap_entry_pool, e); - else - free(e); +void *internal_hashmap_iterate(HashmapBase *h, Iterator *i, const void **key) { + struct hashmap_base_entry *e; + void *data; + unsigned idx; + + idx = hashmap_iterate_entry(h, i); + if (idx == IDX_NIL) { + if (key) + *key = NULL; + + return NULL; + } + + e = bucket_at(h, idx); + data = entry_value(h, e); + if (key) + *key = e->key; + + return data; +} + +void *set_iterate(Set *s, Iterator *i) { + return internal_hashmap_iterate(HASHMAP_BASE(s), i, NULL); } -void hashmap_free(Hashmap*h) { +#define HASHMAP_FOREACH_IDX(idx, h, i) \ + for ((i) = ITERATOR_FIRST, (idx) = hashmap_iterate_entry((h), &(i)); \ + (idx != IDX_NIL); \ + (idx) = hashmap_iterate_entry((h), &(i))) - /* Free the hashmap, but nothing in it */ +static void reset_direct_storage(HashmapBase *h) { + const struct hashmap_type_info *hi = &hashmap_type_info[h->type]; + void *p; + + assert(!h->has_indirect); + + p = mempset(h->direct.storage, 0, hi->entry_size * hi->n_direct_buckets); + memset(p, DIB_RAW_INIT, sizeof(dib_raw_t) * hi->n_direct_buckets); +} + +static struct HashmapBase *hashmap_base_new(const struct hash_ops *hash_ops, enum HashmapType type HASHMAP_DEBUG_PARAMS) { + HashmapBase *h; + const struct hashmap_type_info *hi = &hashmap_type_info[type]; + bool use_pool; + + use_pool = is_main_thread(); + + h = use_pool ? mempool_alloc0_tile(hi->mempool) : malloc0(hi->head_size); if (!h) - return; + return NULL; + + h->type = type; + h->from_pool = use_pool; + h->hash_ops = hash_ops ? hash_ops : &trivial_hash_ops; + + if (type == HASHMAP_TYPE_ORDERED) { + OrderedHashmap *lh = (OrderedHashmap*)h; + lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL; + } + + reset_direct_storage(h); + + if (!shared_hash_key_initialized) { + random_bytes(shared_hash_key, sizeof(shared_hash_key)); + shared_hash_key_initialized= true; + } + +#ifdef ENABLE_HASHMAP_DEBUG + LIST_PREPEND(debug_list, hashmap_debug_list, &h->debug); + h->debug.func = func; + h->debug.file = file; + h->debug.line = line; +#endif + + return h; +} + +Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { + return (Hashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); +} + +OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { + return (OrderedHashmap*) hashmap_base_new(hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); +} + +Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { + return (Set*) hashmap_base_new(hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); +} + +static int hashmap_base_ensure_allocated(HashmapBase **h, const struct hash_ops *hash_ops, + enum HashmapType type HASHMAP_DEBUG_PARAMS) { + HashmapBase *q; + + assert(h); + + if (*h) + return 0; + + q = hashmap_base_new(hash_ops, type HASHMAP_DEBUG_PASS_ARGS); + if (!q) + return -ENOMEM; + + *h = q; + return 0; +} + +int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { + return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_PLAIN HASHMAP_DEBUG_PASS_ARGS); +} + +int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { + return hashmap_base_ensure_allocated((HashmapBase**)h, hash_ops, HASHMAP_TYPE_ORDERED HASHMAP_DEBUG_PASS_ARGS); +} + +int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS) { + return hashmap_base_ensure_allocated((HashmapBase**)s, hash_ops, HASHMAP_TYPE_SET HASHMAP_DEBUG_PASS_ARGS); +} - hashmap_clear(h); +static void hashmap_free_no_clear(HashmapBase *h) { + assert(!h->has_indirect); + assert(!h->n_direct_entries); - if (h->buckets != (struct hashmap_entry**) ((uint8_t*) h + ALIGN(sizeof(Hashmap)))) - free(h->buckets); +#ifdef ENABLE_HASHMAP_DEBUG + LIST_REMOVE(debug_list, hashmap_debug_list, &h->debug); +#endif if (h->from_pool) - mempool_free_tile(&hashmap_pool, container_of(h, struct hashmap_tile, h)); + mempool_free_tile(hashmap_type_info[h->type].mempool, h); else free(h); } -void hashmap_free_free(Hashmap *h) { +void internal_hashmap_free(HashmapBase *h) { + + /* Free the hashmap, but nothing in it */ + + if (!h) + return; + + internal_hashmap_clear(h); + hashmap_free_no_clear(h); +} + +void internal_hashmap_free_free(HashmapBase *h) { /* Free the hashmap and all data objects in it, but not the * keys */ @@ -309,8 +883,8 @@ void hashmap_free_free(Hashmap *h) { if (!h) return; - hashmap_clear_free(h); - hashmap_free(h); + internal_hashmap_clear_free(h); + hashmap_free_no_clear(h); } void hashmap_free_free_free(Hashmap *h) { @@ -321,258 +895,499 @@ void hashmap_free_free_free(Hashmap *h) { return; hashmap_clear_free_free(h); - hashmap_free(h); + hashmap_free_no_clear(HASHMAP_BASE(h)); } -void hashmap_clear(Hashmap *h) { +void internal_hashmap_clear(HashmapBase *h) { if (!h) return; - while (h->iterate_list_head) - remove_entry(h, h->iterate_list_head); + if (h->has_indirect) { + free(h->indirect.storage); + h->has_indirect = false; + } + + h->n_direct_entries = 0; + reset_direct_storage(h); + + if (h->type == HASHMAP_TYPE_ORDERED) { + OrderedHashmap *lh = (OrderedHashmap*) h; + lh->iterate_list_head = lh->iterate_list_tail = IDX_NIL; + } } -void hashmap_clear_free(Hashmap *h) { - void *p; +void internal_hashmap_clear_free(HashmapBase *h) { + unsigned idx; if (!h) return; - while ((p = hashmap_steal_first(h))) - free(p); + for (idx = skip_free_buckets(h, 0); idx != IDX_NIL; + idx = skip_free_buckets(h, idx + 1)) + free(entry_value(h, bucket_at(h, idx))); + + internal_hashmap_clear(h); } void hashmap_clear_free_free(Hashmap *h) { + unsigned idx; + if (!h) return; - while (h->iterate_list_head) { - void *a, *b; - - a = h->iterate_list_head->value; - b = (void*) h->iterate_list_head->key; - remove_entry(h, h->iterate_list_head); - free(a); - free(b); + for (idx = skip_free_buckets(HASHMAP_BASE(h), 0); idx != IDX_NIL; + idx = skip_free_buckets(HASHMAP_BASE(h), idx + 1)) { + struct plain_hashmap_entry *e = plain_bucket_at(h, idx); + free((void*)e->b.key); + free(e->value); } + + internal_hashmap_clear(HASHMAP_BASE(h)); } -static struct hashmap_entry *hash_scan(Hashmap *h, unsigned hash, const void *key) { - struct hashmap_entry *e; - assert(h); - assert(hash < h->n_buckets); +static int resize_buckets(HashmapBase *h, unsigned entries_add); + +/* + * Finds an empty bucket to put an entry into, starting the scan at 'idx'. + * Performs Robin Hood swaps as it goes. The entry to put must be placed + * by the caller into swap slot IDX_PUT. + * If used for in-place resizing, may leave a displaced entry in swap slot + * IDX_PUT. Caller must rehash it next. + * Returns: true if it left a displaced entry to rehash next in IDX_PUT, + * false otherwise. + */ +static bool hashmap_put_robin_hood(HashmapBase *h, unsigned idx, + struct swap_entries *swap) { + dib_raw_t raw_dib, *dibs; + unsigned dib, distance; + +#ifdef ENABLE_HASHMAP_DEBUG + h->debug.put_count++; +#endif + + dibs = dib_raw_ptr(h); + + for (distance = 0; ; distance++) { + raw_dib = dibs[idx]; + if (raw_dib == DIB_RAW_FREE || raw_dib == DIB_RAW_REHASH) { + if (raw_dib == DIB_RAW_REHASH) + bucket_move_entry(h, swap, idx, IDX_TMP); - for (e = h->buckets[hash]; e; e = e->bucket_next) - if (h->hash_ops->compare(e->key, key) == 0) - return e; + if (h->has_indirect && h->indirect.idx_lowest_entry > idx) + h->indirect.idx_lowest_entry = idx; - return NULL; + bucket_set_dib(h, idx, distance); + bucket_move_entry(h, swap, IDX_PUT, idx); + if (raw_dib == DIB_RAW_REHASH) { + bucket_move_entry(h, swap, IDX_TMP, IDX_PUT); + return true; + } + + return false; + } + + dib = bucket_calculate_dib(h, idx, raw_dib); + + if (dib < distance) { + /* Found a wealthier entry. Go Robin Hood! */ + + bucket_set_dib(h, idx, distance); + + /* swap the entries */ + bucket_move_entry(h, swap, idx, IDX_TMP); + bucket_move_entry(h, swap, IDX_PUT, idx); + bucket_move_entry(h, swap, IDX_TMP, IDX_PUT); + + distance = dib; + } + + idx = next_idx(h, idx); + } } -static int resize_buckets(Hashmap *h, unsigned entries_add) { - struct hashmap_entry **n, *i; - unsigned m, new_n_entries, new_n_buckets; - uint8_t nkey[HASH_KEY_SIZE]; +/* + * Puts an entry into a hashmap, boldly - no check whether key already exists. + * The caller must place the entry (only its key and value, not link indexes) + * in swap slot IDX_PUT. + * Caller must ensure: the key does not exist yet in the hashmap. + * that resize is not needed if !may_resize. + * Returns: 1 if entry was put successfully. + * -ENOMEM if may_resize==true and resize failed with -ENOMEM. + * Cannot return -ENOMEM if !may_resize. + */ +static int hashmap_base_put_boldly(HashmapBase *h, unsigned idx, + struct swap_entries *swap, bool may_resize) { + struct ordered_hashmap_entry *new_entry; + int r; + + assert(idx < n_buckets(h)); + + new_entry = bucket_at_swap(swap, IDX_PUT); + + if (may_resize) { + r = resize_buckets(h, 1); + if (r < 0) + return r; + if (r > 0) + idx = bucket_hash(h, new_entry->p.b.key); + } + assert(n_entries(h) < n_buckets(h)); + + if (h->type == HASHMAP_TYPE_ORDERED) { + OrderedHashmap *lh = (OrderedHashmap*) h; + + new_entry->iterate_next = IDX_NIL; + new_entry->iterate_previous = lh->iterate_list_tail; + + if (lh->iterate_list_tail != IDX_NIL) { + struct ordered_hashmap_entry *old_tail; + + old_tail = ordered_bucket_at(lh, lh->iterate_list_tail); + assert(old_tail->iterate_next == IDX_NIL); + old_tail->iterate_next = IDX_PUT; + } + + lh->iterate_list_tail = IDX_PUT; + if (lh->iterate_list_head == IDX_NIL) + lh->iterate_list_head = IDX_PUT; + } + + assert_se(hashmap_put_robin_hood(h, idx, swap) == false); + + n_entries_inc(h); +#ifdef ENABLE_HASHMAP_DEBUG + h->debug.max_entries = MAX(h->debug.max_entries, n_entries(h)); +#endif + + return 1; +} +#define hashmap_put_boldly(h, idx, swap, may_resize) \ + hashmap_base_put_boldly(HASHMAP_BASE(h), idx, swap, may_resize) + +/* + * Returns 0 if resize is not needed. + * 1 if succesfully resized. + * -ENOMEM on allocation failure. + */ +static int resize_buckets(HashmapBase *h, unsigned entries_add) { + struct swap_entries swap; + char *new_storage; + dib_raw_t *old_dibs, *new_dibs; + const struct hashmap_type_info *hi; + unsigned idx, optimal_idx; + unsigned old_n_buckets, new_n_buckets, n_rehashed, new_n_entries; + uint8_t new_shift; + bool rehash_next; assert(h); - new_n_entries = h->n_entries + entries_add; + hi = &hashmap_type_info[h->type]; + new_n_entries = n_entries(h) + entries_add; /* overflow? */ - if (_unlikely_(new_n_entries < entries_add || new_n_entries > UINT_MAX / 4)) + if (_unlikely_(new_n_entries < entries_add)) return -ENOMEM; - new_n_buckets = new_n_entries * 4 / 3; - - if (_likely_(new_n_buckets <= h->n_buckets)) + /* For direct storage we allow 100% load, because it's tiny. */ + if (!h->has_indirect && new_n_entries <= hi->n_direct_buckets) return 0; - /* Increase by four at least */ - m = MAX((h->n_entries+1)*4-1, new_n_buckets); - - /* If we hit OOM we simply risk packed hashmaps... */ - n = new0(struct hashmap_entry*, m); - if (!n) + /* + * Load factor = n/m = 1 - (1/INV_KEEP_FREE). + * From it follows: m = n + n/(INV_KEEP_FREE - 1) + */ + new_n_buckets = new_n_entries + new_n_entries / (INV_KEEP_FREE - 1); + /* overflow? */ + if (_unlikely_(new_n_buckets < new_n_entries)) return -ENOMEM; - /* Let's use a different randomized hash key for the - * extension, so that people cannot guess what we are using - * here forever */ - get_hash_key(nkey, false); + if (_unlikely_(new_n_buckets > UINT_MAX / (hi->entry_size + sizeof(dib_raw_t)))) + return -ENOMEM; - for (i = h->iterate_list_head; i; i = i->iterate_next) { - unsigned long old_bucket, new_bucket; + old_n_buckets = n_buckets(h); - old_bucket = h->hash_ops->hash(i->key, h->hash_key) % h->n_buckets; + if (_likely_(new_n_buckets <= old_n_buckets)) + return 0; - /* First, drop from old bucket table */ - if (i->bucket_next) - i->bucket_next->bucket_previous = i->bucket_previous; + new_shift = log2u_round_up(MAX( + new_n_buckets * (hi->entry_size + sizeof(dib_raw_t)), + 2 * sizeof(struct direct_storage))); - if (i->bucket_previous) - i->bucket_previous->bucket_next = i->bucket_next; - else - h->buckets[old_bucket] = i->bucket_next; + /* Realloc storage (buckets and DIB array). */ + new_storage = realloc(h->has_indirect ? h->indirect.storage : NULL, + 1U << new_shift); + if (!new_storage) + return -ENOMEM; - /* Then, add to new backet table */ - new_bucket = h->hash_ops->hash(i->key, nkey) % m; + /* Must upgrade direct to indirect storage. */ + if (!h->has_indirect) { + memcpy(new_storage, h->direct.storage, + old_n_buckets * (hi->entry_size + sizeof(dib_raw_t))); + h->indirect.n_entries = h->n_direct_entries; + h->indirect.idx_lowest_entry = 0; + h->n_direct_entries = 0; + } - i->bucket_next = n[new_bucket]; - i->bucket_previous = NULL; - if (n[new_bucket]) - n[new_bucket]->bucket_previous = i; - n[new_bucket] = i; + /* Get a new hash key. If we've just upgraded to indirect storage, + * allow reusing a previously generated key. It's still a different key + * from the shared one that we used for direct storage. */ + get_hash_key(h->indirect.hash_key, !h->has_indirect); + + h->has_indirect = true; + h->indirect.storage = new_storage; + h->indirect.n_buckets = (1U << new_shift) / + (hi->entry_size + sizeof(dib_raw_t)); + + old_dibs = (dib_raw_t*)(new_storage + hi->entry_size * old_n_buckets); + new_dibs = dib_raw_ptr(h); + + /* + * Move the DIB array to the new place, replacing valid DIB values with + * DIB_RAW_REHASH to indicate all of the used buckets need rehashing. + * Note: Overlap is not possible, because we have at least doubled the + * number of buckets and dib_raw_t is smaller than any entry type. + */ + for (idx = 0; idx < old_n_buckets; idx++) { + assert(old_dibs[idx] != DIB_RAW_REHASH); + new_dibs[idx] = old_dibs[idx] == DIB_RAW_FREE ? DIB_RAW_FREE + : DIB_RAW_REHASH; } - if (h->buckets != (struct hashmap_entry**) ((uint8_t*) h + ALIGN(sizeof(Hashmap)))) - free(h->buckets); + /* Zero the area of newly added entries (including the old DIB area) */ + memset(bucket_at(h, old_n_buckets), 0, + (n_buckets(h) - old_n_buckets) * hi->entry_size); - h->buckets = n; - h->n_buckets = m; + /* The upper half of the new DIB array needs initialization */ + memset(&new_dibs[old_n_buckets], DIB_RAW_INIT, + (n_buckets(h) - old_n_buckets) * sizeof(dib_raw_t)); - memcpy(h->hash_key, nkey, HASH_KEY_SIZE); + /* Rehash entries that need it */ + n_rehashed = 0; + for (idx = 0; idx < old_n_buckets; idx++) { + if (new_dibs[idx] != DIB_RAW_REHASH) + continue; - return 1; -} + optimal_idx = bucket_hash(h, bucket_at(h, idx)->key); -static int __hashmap_put(Hashmap *h, const void *key, void *value, unsigned hash) { - /* For when we know no such entry exists yet */ + /* + * Not much to do if by luck the entry hashes to its current + * location. Just set its DIB. + */ + if (optimal_idx == idx) { + new_dibs[idx] = 0; + n_rehashed++; + continue; + } + + new_dibs[idx] = DIB_RAW_FREE; + bucket_move_entry(h, &swap, idx, IDX_PUT); + /* bucket_move_entry does not clear the source */ + memset(bucket_at(h, idx), 0, hi->entry_size); + + do { + /* + * Find the new bucket for the current entry. This may make + * another entry homeless and load it into IDX_PUT. + */ + rehash_next = hashmap_put_robin_hood(h, optimal_idx, &swap); + n_rehashed++; + + /* Did the current entry displace another one? */ + if (rehash_next) + optimal_idx = bucket_hash(h, bucket_at_swap(&swap, IDX_PUT)->p.b.key); + } while (rehash_next); + } - struct hashmap_entry *e; + assert(n_rehashed == n_entries(h)); - if (resize_buckets(h, 1) > 0) - hash = bucket_hash(h, key); + return 1; +} - if (h->from_pool) - e = mempool_alloc_tile(&hashmap_entry_pool); - else - e = new(struct hashmap_entry, 1); +/* + * Finds an entry with a matching key + * Returns: index of the found entry, or IDX_NIL if not found. + */ +static unsigned base_bucket_scan(HashmapBase *h, unsigned idx, const void *key) { + struct hashmap_base_entry *e; + unsigned dib, distance; + dib_raw_t *dibs = dib_raw_ptr(h); - if (!e) - return -ENOMEM; + assert(idx < n_buckets(h)); - e->key = key; - e->value = value; + for (distance = 0; ; distance++) { + if (dibs[idx] == DIB_RAW_FREE) + return IDX_NIL; - link_entry(h, e, hash); + dib = bucket_calculate_dib(h, idx, dibs[idx]); - return 1; + if (dib < distance) + return IDX_NIL; + if (dib == distance) { + e = bucket_at(h, idx); + if (h->hash_ops->compare(e->key, key) == 0) + return idx; + } + + idx = next_idx(h, idx); + } } +#define bucket_scan(h, idx, key) base_bucket_scan(HASHMAP_BASE(h), idx, key) int hashmap_put(Hashmap *h, const void *key, void *value) { - struct hashmap_entry *e; - unsigned hash; + struct swap_entries swap; + struct plain_hashmap_entry *e; + unsigned hash, idx; assert(h); hash = bucket_hash(h, key); - e = hash_scan(h, hash, key); - if (e) { + idx = bucket_scan(h, hash, key); + if (idx != IDX_NIL) { + e = plain_bucket_at(h, idx); if (e->value == value) return 0; return -EEXIST; } - return __hashmap_put(h, key, value, hash); + e = &bucket_at_swap(&swap, IDX_PUT)->p; + e->b.key = key; + e->value = value; + return hashmap_put_boldly(h, hash, &swap, true); +} + +int set_put(Set *s, const void *key) { + struct swap_entries swap; + struct hashmap_base_entry *e; + unsigned hash, idx; + + assert(s); + + hash = bucket_hash(s, key); + idx = bucket_scan(s, hash, key); + if (idx != IDX_NIL) + return 0; + + e = &bucket_at_swap(&swap, IDX_PUT)->p.b; + e->key = key; + return hashmap_put_boldly(s, hash, &swap, true); } int hashmap_replace(Hashmap *h, const void *key, void *value) { - struct hashmap_entry *e; - unsigned hash; + struct swap_entries swap; + struct plain_hashmap_entry *e; + unsigned hash, idx; assert(h); hash = bucket_hash(h, key); - e = hash_scan(h, hash, key); - if (e) { - e->key = key; + idx = bucket_scan(h, hash, key); + if (idx != IDX_NIL) { + e = plain_bucket_at(h, idx); +#ifdef ENABLE_HASHMAP_DEBUG + /* Although the key is equal, the key pointer may have changed, + * and this would break our assumption for iterating. So count + * this operation as incompatible with iteration. */ + if (e->b.key != key) { + h->b.debug.put_count++; + h->b.debug.rem_count++; + h->b.debug.last_rem_idx = idx; + } +#endif + e->b.key = key; e->value = value; return 0; } - return __hashmap_put(h, key, value, hash); + e = &bucket_at_swap(&swap, IDX_PUT)->p; + e->b.key = key; + e->value = value; + return hashmap_put_boldly(h, hash, &swap, true); } int hashmap_update(Hashmap *h, const void *key, void *value) { - struct hashmap_entry *e; - unsigned hash; + struct plain_hashmap_entry *e; + unsigned hash, idx; assert(h); hash = bucket_hash(h, key); - e = hash_scan(h, hash, key); - if (!e) + idx = bucket_scan(h, hash, key); + if (idx == IDX_NIL) return -ENOENT; + e = plain_bucket_at(h, idx); e->value = value; return 0; } -void* hashmap_get(Hashmap *h, const void *key) { - unsigned hash; - struct hashmap_entry *e; +void *internal_hashmap_get(HashmapBase *h, const void *key) { + struct hashmap_base_entry *e; + unsigned hash, idx; if (!h) return NULL; hash = bucket_hash(h, key); - e = hash_scan(h, hash, key); - if (!e) + idx = bucket_scan(h, hash, key); + if (idx == IDX_NIL) return NULL; - return e->value; + e = bucket_at(h, idx); + return entry_value(h, e); } -void* hashmap_get2(Hashmap *h, const void *key, void **key2) { - unsigned hash; - struct hashmap_entry *e; +void *hashmap_get2(Hashmap *h, const void *key, void **key2) { + struct plain_hashmap_entry *e; + unsigned hash, idx; if (!h) return NULL; hash = bucket_hash(h, key); - e = hash_scan(h, hash, key); - if (!e) + idx = bucket_scan(h, hash, key); + if (idx == IDX_NIL) return NULL; + e = plain_bucket_at(h, idx); if (key2) - *key2 = (void*) e->key; + *key2 = (void*) e->b.key; return e->value; } -bool hashmap_contains(Hashmap *h, const void *key) { +bool internal_hashmap_contains(HashmapBase *h, const void *key) { unsigned hash; if (!h) return false; hash = bucket_hash(h, key); - return !!hash_scan(h, hash, key); + return bucket_scan(h, hash, key) != IDX_NIL; } -void* hashmap_remove(Hashmap *h, const void *key) { - struct hashmap_entry *e; - unsigned hash; +void *internal_hashmap_remove(HashmapBase *h, const void *key) { + struct hashmap_base_entry *e; + unsigned hash, idx; void *data; if (!h) return NULL; hash = bucket_hash(h, key); - e = hash_scan(h, hash, key); - if (!e) + idx = bucket_scan(h, hash, key); + if (idx == IDX_NIL) return NULL; - data = e->value; - remove_entry(h, e); + e = bucket_at(h, idx); + data = entry_value(h, e); + remove_entry(h, idx); return data; } -void* hashmap_remove2(Hashmap *h, const void *key, void **rkey) { - struct hashmap_entry *e; - unsigned hash; +void *hashmap_remove2(Hashmap *h, const void *key, void **rkey) { + struct plain_hashmap_entry *e; + unsigned hash, idx; void *data; if (!h) { @@ -582,228 +1397,249 @@ void* hashmap_remove2(Hashmap *h, const void *key, void **rkey) { } hash = bucket_hash(h, key); - e = hash_scan(h, hash, key); - if (!e) { + idx = bucket_scan(h, hash, key); + if (idx == IDX_NIL) { if (rkey) *rkey = NULL; return NULL; } + e = plain_bucket_at(h, idx); data = e->value; if (rkey) - *rkey = (void*) e->key; + *rkey = (void*) e->b.key; - remove_entry(h, e); + remove_entry(h, idx); return data; } int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value) { - struct hashmap_entry *e; - unsigned old_hash, new_hash; + struct swap_entries swap; + struct plain_hashmap_entry *e; + unsigned old_hash, new_hash, idx; if (!h) return -ENOENT; old_hash = bucket_hash(h, old_key); - e = hash_scan(h, old_hash, old_key); - if (!e) + idx = bucket_scan(h, old_hash, old_key); + if (idx == IDX_NIL) return -ENOENT; new_hash = bucket_hash(h, new_key); - if (hash_scan(h, new_hash, new_key)) + if (bucket_scan(h, new_hash, new_key) != IDX_NIL) return -EEXIST; - unlink_entry(h, e, old_hash); + remove_entry(h, idx); - e->key = new_key; + e = &bucket_at_swap(&swap, IDX_PUT)->p; + e->b.key = new_key; e->value = value; + assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1); + + return 0; +} - link_entry(h, e, new_hash); +int set_remove_and_put(Set *s, const void *old_key, const void *new_key) { + struct swap_entries swap; + struct hashmap_base_entry *e; + unsigned old_hash, new_hash, idx; + + if (!s) + return -ENOENT; + + old_hash = bucket_hash(s, old_key); + idx = bucket_scan(s, old_hash, old_key); + if (idx == IDX_NIL) + return -ENOENT; + + new_hash = bucket_hash(s, new_key); + if (bucket_scan(s, new_hash, new_key) != IDX_NIL) + return -EEXIST; + + remove_entry(s, idx); + + e = &bucket_at_swap(&swap, IDX_PUT)->p.b; + e->key = new_key; + assert_se(hashmap_put_boldly(s, new_hash, &swap, false) == 1); return 0; } int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value) { - struct hashmap_entry *e, *k; - unsigned old_hash, new_hash; + struct swap_entries swap; + struct plain_hashmap_entry *e; + unsigned old_hash, new_hash, idx_old, idx_new; if (!h) return -ENOENT; old_hash = bucket_hash(h, old_key); - e = hash_scan(h, old_hash, old_key); - if (!e) + idx_old = bucket_scan(h, old_hash, old_key); + if (idx_old == IDX_NIL) return -ENOENT; - new_hash = bucket_hash(h, new_key); - k = hash_scan(h, new_hash, new_key); - if (k) - if (e != k) - remove_entry(h, k); - - unlink_entry(h, e, old_hash); + old_key = bucket_at(HASHMAP_BASE(h), idx_old)->key; - e->key = new_key; + new_hash = bucket_hash(h, new_key); + idx_new = bucket_scan(h, new_hash, new_key); + if (idx_new != IDX_NIL) + if (idx_old != idx_new) { + remove_entry(h, idx_new); + /* Compensate for a possible backward shift. */ + if (old_key != bucket_at(HASHMAP_BASE(h), idx_old)->key) + idx_old = prev_idx(HASHMAP_BASE(h), idx_old); + assert(old_key == bucket_at(HASHMAP_BASE(h), idx_old)->key); + } + + remove_entry(h, idx_old); + + e = &bucket_at_swap(&swap, IDX_PUT)->p; + e->b.key = new_key; e->value = value; - - link_entry(h, e, new_hash); + assert_se(hashmap_put_boldly(h, new_hash, &swap, false) == 1); return 0; } -void* hashmap_remove_value(Hashmap *h, const void *key, void *value) { - struct hashmap_entry *e; - unsigned hash; +void *hashmap_remove_value(Hashmap *h, const void *key, void *value) { + struct plain_hashmap_entry *e; + unsigned hash, idx; if (!h) return NULL; hash = bucket_hash(h, key); - - e = hash_scan(h, hash, key); - if (!e) + idx = bucket_scan(h, hash, key); + if (idx == IDX_NIL) return NULL; + e = plain_bucket_at(h, idx); if (e->value != value) return NULL; - remove_entry(h, e); + remove_entry(h, idx); return value; } -void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key) { - struct hashmap_entry *e; - - assert(i); - - if (!h) - goto at_end; - - if (*i == ITERATOR_LAST) - goto at_end; - - if (*i == ITERATOR_FIRST && !h->iterate_list_head) - goto at_end; +static unsigned find_first_entry(HashmapBase *h) { + Iterator i = ITERATOR_FIRST; - e = *i == ITERATOR_FIRST ? h->iterate_list_head : (struct hashmap_entry*) *i; + if (!h || !n_entries(h)) + return IDX_NIL; - if (e->iterate_next) - *i = (Iterator) e->iterate_next; - else - *i = ITERATOR_LAST; - - if (key) - *key = e->key; - - return e->value; - -at_end: - *i = ITERATOR_LAST; - - if (key) - *key = NULL; - - return NULL; + return hashmap_iterate_entry(h, &i); } -void* hashmap_first(Hashmap *h) { - - if (!h) - return NULL; +void *internal_hashmap_first(HashmapBase *h) { + unsigned idx; - if (!h->iterate_list_head) + idx = find_first_entry(h); + if (idx == IDX_NIL) return NULL; - return h->iterate_list_head->value; + return entry_value(h, bucket_at(h, idx)); } -void* hashmap_first_key(Hashmap *h) { - - if (!h) - return NULL; +void *internal_hashmap_first_key(HashmapBase *h) { + struct hashmap_base_entry *e; + unsigned idx; - if (!h->iterate_list_head) + idx = find_first_entry(h); + if (idx == IDX_NIL) return NULL; - return (void*) h->iterate_list_head->key; + e = bucket_at(h, idx); + return (void*) e->key; } -void* hashmap_steal_first(Hashmap *h) { +void *internal_hashmap_steal_first(HashmapBase *h) { + struct hashmap_base_entry *e; void *data; + unsigned idx; - if (!h) - return NULL; - - if (!h->iterate_list_head) + idx = find_first_entry(h); + if (idx == IDX_NIL) return NULL; - data = h->iterate_list_head->value; - remove_entry(h, h->iterate_list_head); + e = bucket_at(h, idx); + data = entry_value(h, e); + remove_entry(h, idx); return data; } -void* hashmap_steal_first_key(Hashmap *h) { +void *internal_hashmap_steal_first_key(HashmapBase *h) { + struct hashmap_base_entry *e; void *key; + unsigned idx; - if (!h) - return NULL; - - if (!h->iterate_list_head) + idx = find_first_entry(h); + if (idx == IDX_NIL) return NULL; - key = (void*) h->iterate_list_head->key; - remove_entry(h, h->iterate_list_head); + e = bucket_at(h, idx); + key = (void*) e->key; + remove_entry(h, idx); return key; } -unsigned hashmap_size(Hashmap *h) { +unsigned internal_hashmap_size(HashmapBase *h) { if (!h) return 0; - return h->n_entries; + return n_entries(h); } -unsigned hashmap_buckets(Hashmap *h) { +unsigned internal_hashmap_buckets(HashmapBase *h) { if (!h) return 0; - return h->n_buckets; + return n_buckets(h); } -bool hashmap_isempty(Hashmap *h) { +int internal_hashmap_merge(Hashmap *h, Hashmap *other) { + Iterator i; + unsigned idx; - if (!h) - return true; + assert(h); - return h->n_entries == 0; -} + HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) { + struct plain_hashmap_entry *pe = plain_bucket_at(other, idx); + int r; -int hashmap_merge(Hashmap *h, Hashmap *other) { - struct hashmap_entry *e; + r = hashmap_put(h, pe->b.key, pe->value); + if (r < 0 && r != -EEXIST) + return r; + } - assert(h); + return 0; +} - if (!other) - return 0; +int set_merge(Set *s, Set *other) { + Iterator i; + unsigned idx; + + assert(s); - for (e = other->iterate_list_head; e; e = e->iterate_next) { + HASHMAP_FOREACH_IDX(idx, HASHMAP_BASE(other), i) { + struct set_entry *se = set_bucket_at(other, idx); int r; - r = hashmap_put(h, e->key, e->value); - if (r < 0 && r != -EEXIST) + r = set_put(s, se->b.key); + if (r < 0) return r; } return 0; } -int hashmap_reserve(Hashmap *h, unsigned entries_add) { +int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add) { int r; assert(h); @@ -815,96 +1651,144 @@ int hashmap_reserve(Hashmap *h, unsigned entries_add) { return 0; } -int hashmap_move(Hashmap *h, Hashmap *other) { - struct hashmap_entry *e, *n; +/* + * The same as hashmap_merge(), but every new item from other is moved to h. + * Keys already in h are skipped and stay in other. + * Returns: 0 on success. + * -ENOMEM on alloc failure, in which case no move has been done. + */ +int internal_hashmap_move(HashmapBase *h, HashmapBase *other) { + struct swap_entries swap; + struct hashmap_base_entry *e, *n; + Iterator i; + unsigned idx; + int r; assert(h); - /* The same as hashmap_merge(), but every new item from other - * is moved to h. */ - if (!other) return 0; - for (e = other->iterate_list_head; e; e = n) { - unsigned h_hash, other_hash; + assert(other->type == h->type); + + /* + * This reserves buckets for the worst case, where none of other's + * entries are yet present in h. This is preferable to risking + * an allocation failure in the middle of the moving and having to + * rollback or return a partial result. + */ + r = resize_buckets(h, n_entries(other)); + if (r < 0) + return r; - n = e->iterate_next; + HASHMAP_FOREACH_IDX(idx, other, i) { + unsigned h_hash; + e = bucket_at(other, idx); h_hash = bucket_hash(h, e->key); - if (hash_scan(h, h_hash, e->key)) + if (bucket_scan(h, h_hash, e->key) != IDX_NIL) continue; - other_hash = bucket_hash(other, e->key); - unlink_entry(other, e, other_hash); - link_entry(h, e, h_hash); + n = &bucket_at_swap(&swap, IDX_PUT)->p.b; + n->key = e->key; + if (h->type != HASHMAP_TYPE_SET) + ((struct plain_hashmap_entry*) n)->value = + ((struct plain_hashmap_entry*) e)->value; + assert_se(hashmap_put_boldly(h, h_hash, &swap, false) == 1); + + remove_entry(other, idx); } return 0; } -int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) { - unsigned h_hash, other_hash; - struct hashmap_entry *e; +int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key) { + struct swap_entries swap; + unsigned h_hash, other_hash, idx; + struct hashmap_base_entry *e, *n; + int r; assert(h); h_hash = bucket_hash(h, key); - if (hash_scan(h, h_hash, key)) + if (bucket_scan(h, h_hash, key) != IDX_NIL) return -EEXIST; if (!other) return -ENOENT; + assert(other->type == h->type); + other_hash = bucket_hash(other, key); - e = hash_scan(other, other_hash, key); - if (!e) + idx = bucket_scan(other, other_hash, key); + if (idx == IDX_NIL) return -ENOENT; - unlink_entry(other, e, other_hash); - link_entry(h, e, h_hash); + e = bucket_at(other, idx); + + n = &bucket_at_swap(&swap, IDX_PUT)->p.b; + n->key = e->key; + if (h->type != HASHMAP_TYPE_SET) + ((struct plain_hashmap_entry*) n)->value = + ((struct plain_hashmap_entry*) e)->value; + r = hashmap_put_boldly(h, h_hash, &swap, true); + if (r < 0) + return r; + remove_entry(other, idx); return 0; } -Hashmap *hashmap_copy(Hashmap *h) { - Hashmap *copy; +HashmapBase *internal_hashmap_copy(HashmapBase *h) { + HashmapBase *copy; + int r; assert(h); - copy = hashmap_new(h->hash_ops); + copy = hashmap_base_new(h->hash_ops, h->type HASHMAP_DEBUG_SRC_ARGS); if (!copy) return NULL; - if (hashmap_merge(copy, h) < 0) { - hashmap_free(copy); + switch (h->type) { + case HASHMAP_TYPE_PLAIN: + case HASHMAP_TYPE_ORDERED: + r = hashmap_merge((Hashmap*)copy, (Hashmap*)h); + break; + case HASHMAP_TYPE_SET: + r = set_merge((Set*)copy, (Set*)h); + break; + default: + assert_not_reached("Unknown hashmap type"); + } + + if (r < 0) { + internal_hashmap_free(copy); return NULL; } return copy; } -char **hashmap_get_strv(Hashmap *h) { +char **internal_hashmap_get_strv(HashmapBase *h) { char **sv; - Iterator it; - char *item; - int n; + Iterator i; + unsigned idx, n; - sv = new(char*, h->n_entries+1); + sv = new(char*, n_entries(h)+1); if (!sv) return NULL; n = 0; - HASHMAP_FOREACH(item, h, it) - sv[n++] = item; + HASHMAP_FOREACH_IDX(idx, h, i) + sv[n++] = entry_value(h, bucket_at(h, idx)); sv[n] = NULL; return sv; } -void *hashmap_next(Hashmap *h, const void *key) { - unsigned hash; - struct hashmap_entry *e; +void *ordered_hashmap_next(OrderedHashmap *h, const void *key) { + struct ordered_hashmap_entry *e; + unsigned hash, idx; assert(key); @@ -912,13 +1796,55 @@ void *hashmap_next(Hashmap *h, const void *key) { return NULL; hash = bucket_hash(h, key); - e = hash_scan(h, hash, key); - if (!e) + idx = bucket_scan(h, hash, key); + if (idx == IDX_NIL) return NULL; - e = e->iterate_next; - if (!e) + e = ordered_bucket_at(h, idx); + if (e->iterate_next == IDX_NIL) return NULL; + return ordered_bucket_at(h, e->iterate_next)->p.value; +} - return e->value; +int set_consume(Set *s, void *value) { + int r; + + r = set_put(s, value); + if (r <= 0) + free(value); + + return r; +} + +int set_put_strdup(Set *s, const char *p) { + char *c; + int r; + + assert(s); + assert(p); + + c = strdup(p); + if (!c) + return -ENOMEM; + + r = set_consume(s, c); + if (r == -EEXIST) + return 0; + + return r; +} + +int set_put_strdupv(Set *s, char **l) { + int n = 0, r; + char **i; + + STRV_FOREACH(i, l) { + r = set_put_strdup(s, *i); + if (r < 0) + return r; + + n += r; + } + + return n; } diff --git a/src/shared/hashmap.h b/src/shared/hashmap.h index 65fb3c0ee9..9c6e0cab18 100644 --- a/src/shared/hashmap.h +++ b/src/shared/hashmap.h @@ -6,6 +6,7 @@ This file is part of systemd. Copyright 2010 Lennart Poettering + Copyright 2014 Michal Schmidt 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 @@ -26,20 +27,45 @@ #include "macro.h" #include "util.h" -/* Pretty straightforward hash table implementation. As a minor - * optimization a NULL hashmap object will be treated as empty hashmap - * for all read operations. That way it is not necessary to - * instantiate an object for each Hashmap use. */ +/* + * A hash table implementation. As a minor optimization a NULL hashmap object + * will be treated as empty hashmap for all read operations. That way it is not + * necessary to instantiate an object for each Hashmap use. + * + * If ENABLE_HASHMAP_DEBUG is defined (by configuring with --enable-hashmap-debug), + * the implemention will: + * - store extra data for debugging and statistics (see tools/gdb-sd_dump_hashmaps.py) + * - perform extra checks for invalid use of iterators + */ #define HASH_KEY_SIZE 16 -typedef struct Hashmap Hashmap; -typedef struct OrderedHashmap OrderedHashmap; -typedef struct _IteratorStruct _IteratorStruct; -typedef _IteratorStruct* Iterator; +/* The base type for all hashmap and set types. Many functions in the + * implementation take (HashmapBase*) parameters and are run-time polymorphic, + * though the API is not meant to be polymorphic (do not call functions + * prefixed with two underscores directly). */ +typedef struct HashmapBase HashmapBase; + +/* Specific hashmap/set types */ +typedef struct Hashmap Hashmap; /* Maps keys to values */ +typedef struct OrderedHashmap OrderedHashmap; /* Like Hashmap, but also remembers entry insertion order */ +typedef struct Set Set; /* Stores just keys */ + +/* Ideally the Iterator would be an opaque struct, but it is instantiated + * by hashmap users, so the definition has to be here. Do not use its fields + * directly. */ +typedef struct { + unsigned idx; /* index of an entry to be iterated next */ + const void *next_key; /* expected value of that entry's key pointer */ +#ifdef ENABLE_HASHMAP_DEBUG + unsigned put_count; /* hashmap's put_count recorded at start of iteration */ + unsigned rem_count; /* hashmap's rem_count in previous iteration */ + unsigned prev_idx; /* idx in previous iteration */ +#endif +} Iterator; -#define ITERATOR_FIRST ((Iterator) 0) -#define ITERATOR_LAST ((Iterator) -1) +#define _IDX_ITERATOR_FIRST (UINT_MAX - 1) +#define ITERATOR_FIRST ((Iterator) { .idx = _IDX_ITERATOR_FIRST, .next_key = NULL }) typedef unsigned long (*hash_func_t)(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]); typedef int (*compare_func_t)(const void *a, const void *b); @@ -82,153 +108,287 @@ extern const struct hash_ops devt_hash_ops = { #define devt_hash_ops uint64_hash_ops #endif -Hashmap *hashmap_new(const struct hash_ops *hash_ops); -static inline OrderedHashmap *ordered_hashmap_new(const struct hash_ops *hash_ops) { - return (OrderedHashmap*) hashmap_new(hash_ops); +/* Macros for type checking */ +#define PTR_COMPATIBLE_WITH_HASHMAP_BASE(h) \ + (__builtin_types_compatible_p(typeof(h), HashmapBase*) || \ + __builtin_types_compatible_p(typeof(h), Hashmap*) || \ + __builtin_types_compatible_p(typeof(h), OrderedHashmap*) || \ + __builtin_types_compatible_p(typeof(h), Set*)) + +#define PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h) \ + (__builtin_types_compatible_p(typeof(h), Hashmap*) || \ + __builtin_types_compatible_p(typeof(h), OrderedHashmap*)) \ + +#define HASHMAP_BASE(h) \ + __builtin_choose_expr(PTR_COMPATIBLE_WITH_HASHMAP_BASE(h), \ + (HashmapBase*)(h), \ + (void)0) + +#define PLAIN_HASHMAP(h) \ + __builtin_choose_expr(PTR_COMPATIBLE_WITH_PLAIN_HASHMAP(h), \ + (Hashmap*)(h), \ + (void)0) + +#ifdef ENABLE_HASHMAP_DEBUG +# define HASHMAP_DEBUG_PARAMS , const char *func, const char *file, int line +# define HASHMAP_DEBUG_SRC_ARGS , __func__, __FILE__, __LINE__ +# define HASHMAP_DEBUG_PASS_ARGS , func, file, line +#else +# define HASHMAP_DEBUG_PARAMS +# define HASHMAP_DEBUG_SRC_ARGS +# define HASHMAP_DEBUG_PASS_ARGS +#endif + +Hashmap *internal_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +OrderedHashmap *internal_ordered_hashmap_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define hashmap_new(ops) internal_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) +#define ordered_hashmap_new(ops) internal_ordered_hashmap_new(ops HASHMAP_DEBUG_SRC_ARGS) + +void internal_hashmap_free(HashmapBase *h); +static inline void hashmap_free(Hashmap *h) { + internal_hashmap_free(HASHMAP_BASE(h)); } -void hashmap_free(Hashmap *h); static inline void ordered_hashmap_free(OrderedHashmap *h) { - hashmap_free((Hashmap*) h); + internal_hashmap_free(HASHMAP_BASE(h)); +} + +void internal_hashmap_free_free(HashmapBase *h); +static inline void hashmap_free_free(Hashmap *h) { + internal_hashmap_free_free(HASHMAP_BASE(h)); } -void hashmap_free_free(Hashmap *h); static inline void ordered_hashmap_free_free(OrderedHashmap *h) { - hashmap_free_free((Hashmap*) h); + internal_hashmap_free_free(HASHMAP_BASE(h)); } + void hashmap_free_free_free(Hashmap *h); static inline void ordered_hashmap_free_free_free(OrderedHashmap *h) { - hashmap_free_free_free((Hashmap*) h); + hashmap_free_free_free(PLAIN_HASHMAP(h)); } -Hashmap *hashmap_copy(Hashmap *h); -static inline OrderedHashmap *ordered_hashmap_copy(OrderedHashmap *h) { - return (OrderedHashmap*) hashmap_copy((Hashmap*) h); + +HashmapBase *internal_hashmap_copy(HashmapBase *h); +static inline Hashmap *hashmap_copy(Hashmap *h) { + return (Hashmap*) internal_hashmap_copy(HASHMAP_BASE(h)); } -int hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops); -static inline int ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops) { - return hashmap_ensure_allocated((Hashmap**) h, hash_ops); +static inline OrderedHashmap *ordered_hashmap_copy(OrderedHashmap *h) { + return (OrderedHashmap*) internal_hashmap_copy(HASHMAP_BASE(h)); } +int internal_hashmap_ensure_allocated(Hashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +int internal_ordered_hashmap_ensure_allocated(OrderedHashmap **h, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define hashmap_ensure_allocated(h, ops) internal_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) +#define ordered_hashmap_ensure_allocated(h, ops) internal_ordered_hashmap_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) + int hashmap_put(Hashmap *h, const void *key, void *value); static inline int ordered_hashmap_put(OrderedHashmap *h, const void *key, void *value) { - return hashmap_put((Hashmap*) h, key, value); + return hashmap_put(PLAIN_HASHMAP(h), key, value); } + int hashmap_update(Hashmap *h, const void *key, void *value); static inline int ordered_hashmap_update(OrderedHashmap *h, const void *key, void *value) { - return hashmap_update((Hashmap*) h, key, value); + return hashmap_update(PLAIN_HASHMAP(h), key, value); } + int hashmap_replace(Hashmap *h, const void *key, void *value); static inline int ordered_hashmap_replace(OrderedHashmap *h, const void *key, void *value) { - return hashmap_replace((Hashmap*) h, key, value); + return hashmap_replace(PLAIN_HASHMAP(h), key, value); +} + +void *internal_hashmap_get(HashmapBase *h, const void *key); +static inline void *hashmap_get(Hashmap *h, const void *key) { + return internal_hashmap_get(HASHMAP_BASE(h), key); } -void *hashmap_get(Hashmap *h, const void *key); static inline void *ordered_hashmap_get(OrderedHashmap *h, const void *key) { - return hashmap_get((Hashmap*) h, key); + return internal_hashmap_get(HASHMAP_BASE(h), key); } + void *hashmap_get2(Hashmap *h, const void *key, void **rkey); static inline void *ordered_hashmap_get2(OrderedHashmap *h, const void *key, void **rkey) { - return hashmap_get2((Hashmap*) h, key, rkey); + return hashmap_get2(PLAIN_HASHMAP(h), key, rkey); +} + +bool internal_hashmap_contains(HashmapBase *h, const void *key); +static inline bool hashmap_contains(Hashmap *h, const void *key) { + return internal_hashmap_contains(HASHMAP_BASE(h), key); } -bool hashmap_contains(Hashmap *h, const void *key); static inline bool ordered_hashmap_contains(OrderedHashmap *h, const void *key) { - return hashmap_contains((Hashmap*) h, key); + return internal_hashmap_contains(HASHMAP_BASE(h), key); +} + +void *internal_hashmap_remove(HashmapBase *h, const void *key); +static inline void *hashmap_remove(Hashmap *h, const void *key) { + return internal_hashmap_remove(HASHMAP_BASE(h), key); } -void *hashmap_remove(Hashmap *h, const void *key); static inline void *ordered_hashmap_remove(OrderedHashmap *h, const void *key) { - return hashmap_remove((Hashmap*) h, key); + return internal_hashmap_remove(HASHMAP_BASE(h), key); } + void *hashmap_remove2(Hashmap *h, const void *key, void **rkey); static inline void *ordered_hashmap_remove2(OrderedHashmap *h, const void *key, void **rkey) { - return hashmap_remove2((Hashmap*) h, key, rkey); + return hashmap_remove2(PLAIN_HASHMAP(h), key, rkey); } + void *hashmap_remove_value(Hashmap *h, const void *key, void *value); static inline void *ordered_hashmap_remove_value(OrderedHashmap *h, const void *key, void *value) { - return hashmap_remove_value((Hashmap*) h, key, value); + return hashmap_remove_value(PLAIN_HASHMAP(h), key, value); } + int hashmap_remove_and_put(Hashmap *h, const void *old_key, const void *new_key, void *value); static inline int ordered_hashmap_remove_and_put(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) { - return hashmap_remove_and_put((Hashmap*) h, old_key, new_key, value); + return hashmap_remove_and_put(PLAIN_HASHMAP(h), old_key, new_key, value); } + int hashmap_remove_and_replace(Hashmap *h, const void *old_key, const void *new_key, void *value); static inline int ordered_hashmap_remove_and_replace(OrderedHashmap *h, const void *old_key, const void *new_key, void *value) { - return hashmap_remove_and_replace((Hashmap*) h, old_key, new_key, value); + return hashmap_remove_and_replace(PLAIN_HASHMAP(h), old_key, new_key, value); } -int hashmap_merge(Hashmap *h, Hashmap *other); -static inline int ordered_hashmap_merge(OrderedHashmap *h, OrderedHashmap *other) { - return hashmap_merge((Hashmap*) h, (Hashmap*) other); +/* Since merging data from a OrderedHashmap into a Hashmap or vice-versa + * should just work, allow this by having looser type-checking here. */ +int internal_hashmap_merge(Hashmap *h, Hashmap *other); +#define hashmap_merge(h, other) internal_hashmap_merge(PLAIN_HASHMAP(h), PLAIN_HASHMAP(other)) +#define ordered_hashmap_merge(h, other) hashmap_merge(h, other) + +int internal_hashmap_reserve(HashmapBase *h, unsigned entries_add); +static inline int hashmap_reserve(Hashmap *h, unsigned entries_add) { + return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); } -int hashmap_reserve(Hashmap *h, unsigned entries_add); static inline int ordered_hashmap_reserve(OrderedHashmap *h, unsigned entries_add) { - return hashmap_reserve((Hashmap*) h, entries_add); + return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); +} + +int internal_hashmap_move(HashmapBase *h, HashmapBase *other); +/* Unlike hashmap_merge, hashmap_move does not allow mixing the types. */ +static inline int hashmap_move(Hashmap *h, Hashmap *other) { + return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); } -int hashmap_move(Hashmap *h, Hashmap *other); static inline int ordered_hashmap_move(OrderedHashmap *h, OrderedHashmap *other) { - return hashmap_move((Hashmap*) h, (Hashmap*) other); + return internal_hashmap_move(HASHMAP_BASE(h), HASHMAP_BASE(other)); +} + +int internal_hashmap_move_one(HashmapBase *h, HashmapBase *other, const void *key); +static inline int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key) { + return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); } -int hashmap_move_one(Hashmap *h, Hashmap *other, const void *key); static inline int ordered_hashmap_move_one(OrderedHashmap *h, OrderedHashmap *other, const void *key) { - return hashmap_move_one((Hashmap*) h, (Hashmap*) other, key); + return internal_hashmap_move_one(HASHMAP_BASE(h), HASHMAP_BASE(other), key); } -unsigned hashmap_size(Hashmap *h) _pure_; +unsigned internal_hashmap_size(HashmapBase *h) _pure_; +static inline unsigned hashmap_size(Hashmap *h) { + return internal_hashmap_size(HASHMAP_BASE(h)); +} static inline unsigned ordered_hashmap_size(OrderedHashmap *h) { - return hashmap_size((Hashmap*) h); + return internal_hashmap_size(HASHMAP_BASE(h)); +} + +static inline bool hashmap_isempty(Hashmap *h) { + return hashmap_size(h) == 0; } -bool hashmap_isempty(Hashmap *h) _pure_; static inline bool ordered_hashmap_isempty(OrderedHashmap *h) { - return hashmap_isempty((Hashmap*) h); + return ordered_hashmap_size(h) == 0; +} + +unsigned internal_hashmap_buckets(HashmapBase *h) _pure_; +static inline unsigned hashmap_buckets(Hashmap *h) { + return internal_hashmap_buckets(HASHMAP_BASE(h)); } -unsigned hashmap_buckets(Hashmap *h) _pure_; static inline unsigned ordered_hashmap_buckets(OrderedHashmap *h) { - return hashmap_buckets((Hashmap*) h); + return internal_hashmap_buckets(HASHMAP_BASE(h)); } -void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key); +void *internal_hashmap_iterate(HashmapBase *h, Iterator *i, const void **key); +static inline void *hashmap_iterate(Hashmap *h, Iterator *i, const void **key) { + return internal_hashmap_iterate(HASHMAP_BASE(h), i, key); +} static inline void *ordered_hashmap_iterate(OrderedHashmap *h, Iterator *i, const void **key) { - return hashmap_iterate((Hashmap*) h, i, key); + return internal_hashmap_iterate(HASHMAP_BASE(h), i, key); } -void hashmap_clear(Hashmap *h); +void internal_hashmap_clear(HashmapBase *h); +static inline void hashmap_clear(Hashmap *h) { + internal_hashmap_clear(HASHMAP_BASE(h)); +} static inline void ordered_hashmap_clear(OrderedHashmap *h) { - hashmap_clear((Hashmap*) h); + internal_hashmap_clear(HASHMAP_BASE(h)); +} + +void internal_hashmap_clear_free(HashmapBase *h); +static inline void hashmap_clear_free(Hashmap *h) { + internal_hashmap_clear_free(HASHMAP_BASE(h)); } -void hashmap_clear_free(Hashmap *h); static inline void ordered_hashmap_clear_free(OrderedHashmap *h) { - hashmap_clear_free((Hashmap*) h); + internal_hashmap_clear_free(HASHMAP_BASE(h)); } + void hashmap_clear_free_free(Hashmap *h); static inline void ordered_hashmap_clear_free_free(OrderedHashmap *h) { - hashmap_clear_free_free((Hashmap*) h); + hashmap_clear_free_free(PLAIN_HASHMAP(h)); } -void *hashmap_steal_first(Hashmap *h); +/* + * Note about all *_first*() functions + * + * For plain Hashmaps and Sets the order of entries is undefined. + * The functions find whatever entry is first in the implementation + * internal order. + * + * Only for OrderedHashmaps the order is well defined and finding + * the first entry is O(1). + */ + +void *internal_hashmap_steal_first(HashmapBase *h); +static inline void *hashmap_steal_first(Hashmap *h) { + return internal_hashmap_steal_first(HASHMAP_BASE(h)); +} static inline void *ordered_hashmap_steal_first(OrderedHashmap *h) { - return hashmap_steal_first((Hashmap*) h); + return internal_hashmap_steal_first(HASHMAP_BASE(h)); +} + +void *internal_hashmap_steal_first_key(HashmapBase *h); +static inline void *hashmap_steal_first_key(Hashmap *h) { + return internal_hashmap_steal_first_key(HASHMAP_BASE(h)); } -void *hashmap_steal_first_key(Hashmap *h); static inline void *ordered_hashmap_steal_first_key(OrderedHashmap *h) { - return hashmap_steal_first_key((Hashmap*) h); + return internal_hashmap_steal_first_key(HASHMAP_BASE(h)); } -void *hashmap_first(Hashmap *h) _pure_; -static inline void *ordered_hashmap_first(OrderedHashmap *h) { - return hashmap_first((Hashmap*) h); + +void *internal_hashmap_first_key(HashmapBase *h) _pure_; +static inline void *hashmap_first_key(Hashmap *h) { + return internal_hashmap_first_key(HASHMAP_BASE(h)); } -void *hashmap_first_key(Hashmap *h) _pure_; static inline void *ordered_hashmap_first_key(OrderedHashmap *h) { - return hashmap_first_key((Hashmap*) h); + return internal_hashmap_first_key(HASHMAP_BASE(h)); } -void *hashmap_next(Hashmap *h, const void *key); -static inline void *ordered_hashmap_next(OrderedHashmap *h, const void *key) { - return hashmap_next((Hashmap*) h, key); +void *internal_hashmap_first(HashmapBase *h) _pure_; +static inline void *hashmap_first(Hashmap *h) { + return internal_hashmap_first(HASHMAP_BASE(h)); } +static inline void *ordered_hashmap_first(OrderedHashmap *h) { + return internal_hashmap_first(HASHMAP_BASE(h)); +} + +/* no hashmap_next */ +void *ordered_hashmap_next(OrderedHashmap *h, const void *key); -char **hashmap_get_strv(Hashmap *h); +char **internal_hashmap_get_strv(HashmapBase *h); +static inline char **hashmap_get_strv(Hashmap *h) { + return internal_hashmap_get_strv(HASHMAP_BASE(h)); +} static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) { - return hashmap_get_strv((Hashmap*) h); + return internal_hashmap_get_strv(HASHMAP_BASE(h)); } +/* + * Hashmaps are iterated in unpredictable order. + * OrderedHashmaps are an exception to this. They are iterated in the order + * the entries were inserted. + * It is safe to remove the current entry. + */ #define HASHMAP_FOREACH(e, h, i) \ - for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), NULL); (e); (e) = hashmap_iterate((h), &(i), NULL)) + for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), NULL); \ + (e); \ + (e) = hashmap_iterate((h), &(i), NULL)) #define ORDERED_HASHMAP_FOREACH(e, h, i) \ for ((i) = ITERATOR_FIRST, (e) = ordered_hashmap_iterate((h), &(i), NULL); \ @@ -236,7 +396,9 @@ static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) { (e) = ordered_hashmap_iterate((h), &(i), NULL)) #define HASHMAP_FOREACH_KEY(e, k, h, i) \ - for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), (const void**) &(k)); (e); (e) = hashmap_iterate((h), &(i), (const void**) &(k))) + for ((i) = ITERATOR_FIRST, (e) = hashmap_iterate((h), &(i), (const void**) &(k)); \ + (e); \ + (e) = hashmap_iterate((h), &(i), (const void**) &(k))) #define ORDERED_HASHMAP_FOREACH_KEY(e, k, h, i) \ for ((i) = ITERATOR_FIRST, (e) = ordered_hashmap_iterate((h), &(i), (const void**) &(k)); \ @@ -249,6 +411,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_free); DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free); DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free); DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_free); + #define _cleanup_hashmap_free_ _cleanup_(hashmap_freep) #define _cleanup_hashmap_free_free_ _cleanup_(hashmap_free_freep) #define _cleanup_hashmap_free_free_free_ _cleanup_(hashmap_free_free_freep) diff --git a/src/shared/in-addr-util.c b/src/shared/in-addr-util.c index 5fbee6caf2..9dc9ec82b4 100644 --- a/src/shared/in-addr-util.c +++ b/src/shared/in-addr-util.c @@ -250,21 +250,20 @@ unsigned in_addr_netmask_to_prefixlen(const struct in_addr *addr) { } int in_addr_default_prefixlen(const struct in_addr *addr, unsigned char *prefixlen) { - uint32_t address; + uint8_t msb_octet = *(uint8_t*) addr; + + /* addr may not be aligned, so make sure we only access it byte-wise */ assert(addr); - assert(addr->s_addr != INADDR_ANY); assert(prefixlen); - address = be32toh(addr->s_addr); - - if ((address >> 31) == 0x0) + if (msb_octet < 128) /* class A, leading bits: 0 */ *prefixlen = 8; - else if ((address >> 30) == 0x2) + else if (msb_octet < 192) /* class B, leading bits 10 */ *prefixlen = 16; - else if ((address >> 29) == 0x6) + else if (msb_octet < 224) /* class C, leading bits 110 */ *prefixlen = 24; else diff --git a/src/shared/install.c b/src/shared/install.c index 035b44cc52..efbe61e874 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -538,7 +538,6 @@ static int find_symlinks_in_scope( assert(scope < _UNIT_FILE_SCOPE_MAX); assert(name); - /* First look in runtime config path */ r = get_config_path(scope, true, root_dir, &path); if (r < 0) @@ -662,7 +661,7 @@ int unit_file_unmask( goto finish; STRV_FOREACH(i, files) { - char *path; + _cleanup_free_ char *path = NULL; if (!unit_name_is_valid(*i, TEMPLATE_VALID)) { if (r == 0) @@ -678,21 +677,16 @@ int unit_file_unmask( q = null_or_empty_path(path); if (q > 0) { - if (unlink(path) >= 0) { - mark_symlink_for_removal(&remove_symlinks_to, path); + if (unlink(path) < 0) + q = -errno; + else { + q = mark_symlink_for_removal(&remove_symlinks_to, path); add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); - - free(path); - continue; } - - q = -errno; } if (q != -ENOENT && r == 0) r = q; - - free(path); } @@ -840,6 +834,7 @@ static void install_info_free(InstallInfo *i) { strv_free(i->aliases); strv_free(i->wanted_by); strv_free(i->required_by); + strv_free(i->also); free(i->default_instance); free(i); } @@ -948,6 +943,7 @@ static int config_parse_also( size_t l; const char *word, *state; InstallContext *c = data; + InstallInfo *i = userdata; assert(filename); assert(lvalue); @@ -964,6 +960,10 @@ static int config_parse_also( r = install_info_add(c, n, NULL); if (r < 0) return r; + + r = strv_extend(&i->also, n); + if (r < 0) + return r; } if (!isempty(state)) log_syntax(unit, LOG_ERR, filename, line, EINVAL, @@ -1043,7 +1043,8 @@ static int unit_file_load( const char *path, const char *root_dir, bool allow_symlink, - bool load) { + bool load, + bool *also) { const ConfigTableItem items[] = { { "Install", "Alias", config_parse_strv, 0, &info->aliases }, @@ -1087,6 +1088,9 @@ static int unit_file_load( if (r < 0) return r; + if (also) + *also = !strv_isempty(info->also); + return (int) strv_length(info->aliases) + (int) strv_length(info->wanted_by) + @@ -1099,7 +1103,8 @@ static int unit_file_search( LookupPaths *paths, const char *root_dir, bool allow_symlink, - bool load) { + bool load, + bool *also) { char **p; int r; @@ -1109,7 +1114,7 @@ static int unit_file_search( assert(paths); if (info->path) - return unit_file_load(c, info, info->path, root_dir, allow_symlink, load); + return unit_file_load(c, info, info->path, root_dir, allow_symlink, load, also); assert(info->name); @@ -1120,7 +1125,7 @@ static int unit_file_search( if (!path) return -ENOMEM; - r = unit_file_load(c, info, path, root_dir, allow_symlink, load); + r = unit_file_load(c, info, path, root_dir, allow_symlink, load, also); if (r >= 0) { info->path = path; path = NULL; @@ -1149,7 +1154,7 @@ static int unit_file_search( if (!path) return -ENOMEM; - r = unit_file_load(c, info, path, root_dir, allow_symlink, load); + r = unit_file_load(c, info, path, root_dir, allow_symlink, load, also); if (r >= 0) { info->path = path; path = NULL; @@ -1167,7 +1172,8 @@ static int unit_file_can_install( LookupPaths *paths, const char *root_dir, const char *name, - bool allow_symlink) { + bool allow_symlink, + bool *also) { _cleanup_(install_context_done) InstallContext c = {}; InstallInfo *i; @@ -1182,7 +1188,7 @@ static int unit_file_can_install( assert_se(i = ordered_hashmap_first(c.will_install)); - r = unit_file_search(&c, i, paths, root_dir, allow_symlink, true); + r = unit_file_search(&c, i, paths, root_dir, allow_symlink, true, also); if (r >= 0) r = @@ -1415,7 +1421,7 @@ static int install_context_apply( while ((i = ordered_hashmap_first(c->will_install))) { assert_se(ordered_hashmap_move_one(c->have_installed, c->will_install, i->name) == 0); - q = unit_file_search(c, i, paths, root_dir, false, true); + q = unit_file_search(c, i, paths, root_dir, false, true, NULL); if (q < 0) { if (r >= 0) r = q; @@ -1462,7 +1468,7 @@ static int install_context_mark_for_removal( while ((i = ordered_hashmap_first(c->will_install))) { assert_se(ordered_hashmap_move_one(c->have_installed, c->will_install, i->name) == 0); - q = unit_file_search(c, i, paths, root_dir, false, true); + q = unit_file_search(c, i, paths, root_dir, false, true, NULL); if (q == -ENOENT) { /* do nothing */ } else if (q < 0) { @@ -1541,10 +1547,8 @@ int unit_file_add_dependency( UnitFileState state; state = unit_file_get_state(scope, root_dir, *i); - if (state < 0) { - log_error("Failed to get unit file state for %s: %s", *i, strerror(-state)); - return state; - } + if (state < 0) + return log_error_errno(state, "Failed to get unit file state for %s: %m", *i); if (state == UNIT_FILE_MASKED || state == UNIT_FILE_MASKED_RUNTIME) { log_error("Failed to enable unit: Unit %s is masked", *i); @@ -1569,7 +1573,7 @@ int unit_file_add_dependency( while ((info = ordered_hashmap_first(c.will_install))) { assert_se(ordered_hashmap_move_one(c.have_installed, c.will_install, info->name) == 0); - r = unit_file_search(&c, info, &paths, root_dir, false, false); + r = unit_file_search(&c, info, &paths, root_dir, false, false, NULL); if (r < 0) return r; @@ -1620,12 +1624,10 @@ int unit_file_enable( STRV_FOREACH(i, files) { UnitFileState state; + /* We only want to know if this unit is masked, so we ignore + * errors from unit_file_get_state, deferring other checks. + * This allows templated units to be enabled on the fly. */ state = unit_file_get_state(scope, root_dir, *i); - if (state < 0) { - log_error("Failed to get unit file state for %s: %s", *i, strerror(-state)); - return state; - } - if (state == UNIT_FILE_MASKED || state == UNIT_FILE_MASKED_RUNTIME) { log_error("Failed to enable unit: Unit %s is masked", *i); return -ENOTSUP; @@ -1740,7 +1742,7 @@ int unit_file_set_default( assert_se(i = ordered_hashmap_first(c.will_install)); - r = unit_file_search(&c, i, &paths, root_dir, false, true); + r = unit_file_search(&c, i, &paths, root_dir, false, true, NULL); if (r < 0) return r; @@ -1827,6 +1829,7 @@ UnitFileState unit_file_get_state( STRV_FOREACH(i, paths.unit_path) { struct stat st; char *partial; + bool also = false; free(path); path = NULL; @@ -1871,13 +1874,16 @@ UnitFileState unit_file_get_state( else if (r > 0) return state; - r = unit_file_can_install(&paths, root_dir, partial, true); + r = unit_file_can_install(&paths, root_dir, partial, true, &also); if (r < 0 && errno != ENOENT) return r; else if (r > 0) return UNIT_FILE_DISABLED; - else if (r == 0) + else if (r == 0) { + if (also) + return UNIT_FILE_INDIRECT; return UNIT_FILE_STATIC; + } } return r < 0 ? r : state; @@ -2244,7 +2250,7 @@ int unit_file_get_list( if (!path) return -ENOMEM; - r = unit_file_can_install(&paths, root_dir, path, true); + r = unit_file_can_install(&paths, root_dir, path, true, NULL); if (r == -EINVAL || /* Invalid setting? */ r == -EBADMSG || /* Invalid format? */ r == -ENOENT /* Included file not found? */) @@ -2276,6 +2282,7 @@ static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = { [UNIT_FILE_MASKED_RUNTIME] = "masked-runtime", [UNIT_FILE_STATIC] = "static", [UNIT_FILE_DISABLED] = "disabled", + [UNIT_FILE_INDIRECT] = "indirect", [UNIT_FILE_INVALID] = "invalid", }; diff --git a/src/shared/install.h b/src/shared/install.h index c0b4df69d5..357be0f92d 100644 --- a/src/shared/install.h +++ b/src/shared/install.h @@ -41,6 +41,7 @@ typedef enum UnitFileState { UNIT_FILE_MASKED_RUNTIME, UNIT_FILE_STATIC, UNIT_FILE_DISABLED, + UNIT_FILE_INDIRECT, UNIT_FILE_INVALID, _UNIT_FILE_STATE_MAX, _UNIT_FILE_STATE_INVALID = -1 @@ -80,6 +81,7 @@ typedef struct { char **aliases; char **wanted_by; char **required_by; + char **also; char *default_instance; } InstallInfo; diff --git a/src/shared/locale-util.h b/src/shared/locale-util.h index d7a3e4fae6..e48aa3d9af 100644 --- a/src/shared/locale-util.h +++ b/src/shared/locale-util.h @@ -21,6 +21,10 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <stdbool.h> + +#include "macro.h" + typedef enum LocaleVariable { /* We don't list LC_ALL here on purpose. People should be * using LANG instead. */ diff --git a/src/shared/log.c b/src/shared/log.c index 26c604afd8..af1a932c86 100644 --- a/src/shared/log.c +++ b/src/shared/log.c @@ -106,8 +106,8 @@ void log_close_syslog(void) { } static int create_log_socket(int type) { - int fd; struct timeval tv; + int fd; fd = socket(AF_UNIX, type|SOCK_CLOEXEC, 0); if (fd < 0) @@ -122,18 +122,20 @@ static int create_log_socket(int type) { timeval_store(&tv, 10 * USEC_PER_MSEC); else timeval_store(&tv, 10 * USEC_PER_SEC); - setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + (void) setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); return fd; } static int log_open_syslog(void) { - int r; - union sockaddr_union sa = { + + static const union sockaddr_union sa = { .un.sun_family = AF_UNIX, .un.sun_path = "/dev/log", }; + int r; + if (syslog_fd >= 0) return 0; @@ -176,10 +178,12 @@ void log_close_journal(void) { } static int log_open_journal(void) { - union sockaddr_union sa = { + + static const union sockaddr_union sa = { .un.sun_family = AF_UNIX, .un.sun_path = "/run/systemd/journal/socket", }; + int r; if (journal_fd >= 0) @@ -302,10 +306,11 @@ void log_set_facility(int facility) { static int write_to_console( int level, - const char*file, + int error, + const char *file, int line, const char *func, - const char *object_name, + const char *object_field, const char *object, const char *buffer) { @@ -356,15 +361,16 @@ static int write_to_console( } static int write_to_syslog( - int level, - const char*file, - int line, - const char *func, - const char *object_name, - const char *object, - const char *buffer) { + int level, + int error, + const char *file, + int line, + const char *func, + const char *object_field, + const char *object, + const char *buffer) { - char header_priority[16], header_time[64], header_pid[16]; + char header_priority[1 + DECIMAL_STR_MAX(int) + 2], header_time[64], header_pid[1 + DECIMAL_STR_MAX(pid_t) + 4]; struct iovec iovec[5] = {}; struct msghdr msghdr = { .msg_iov = iovec, @@ -418,15 +424,16 @@ static int write_to_syslog( } static int write_to_kmsg( - int level, - const char*file, - int line, - const char *func, - const char *object_name, - const char *object, - const char *buffer) { + int level, + int error, + const char*file, + int line, + const char *func, + const char *object_field, + const char *object, + const char *buffer) { - char header_priority[16], header_pid[16]; + char header_priority[1 + DECIMAL_STR_MAX(int) + 2], header_pid[1 + DECIMAL_STR_MAX(pid_t) + 4]; struct iovec iovec[5] = {}; if (kmsg_fd < 0) @@ -450,45 +457,55 @@ static int write_to_kmsg( return 1; } -static int log_do_header(char *header, size_t size, - int level, - const char *file, int line, const char *func, - const char *object_name, const char *object) { +static int log_do_header( + char *header, + size_t size, + int level, + int error, + const char *file, int line, const char *func, + const char *object_field, const char *object) { + snprintf(header, size, "PRIORITY=%i\n" "SYSLOG_FACILITY=%i\n" - "%s%.*s%s" + "%s%s%s" + "%s%.*i%s" + "%s%s%s" "%s%.*i%s" - "%s%.*s%s" - "%s%.*s%s" + "%s%s%s" "SYSLOG_IDENTIFIER=%s\n", LOG_PRI(level), LOG_FAC(level), - file ? "CODE_FILE=" : "", - file ? LINE_MAX : 0, file, /* %.0s means no output */ - file ? "\n" : "", + isempty(file) ? "" : "CODE_FILE=", + isempty(file) ? "" : file, + isempty(file) ? "" : "\n", line ? "CODE_LINE=" : "", line ? 1 : 0, line, /* %.0d means no output too, special case for 0 */ line ? "\n" : "", - func ? "CODE_FUNCTION=" : "", - func ? LINE_MAX : 0, func, - func ? "\n" : "", - object ? object_name : "", - object ? LINE_MAX : 0, object, /* %.0s means no output */ - object ? "\n" : "", + isempty(func) ? "" : "CODE_FUNCTION=", + isempty(func) ? "" : func, + isempty(func) ? "" : "\n", + error ? "ERRNO=" : "", + error ? 1 : 0, error, + error ? "\n" : "", + isempty(object) ? "" : object_field, + isempty(object) ? "" : object, + isempty(object) ? "" : "\n", program_invocation_short_name); header[size - 1] = '\0'; + return 0; } static int write_to_journal( - int level, - const char*file, - int line, - const char *func, - const char *object_name, - const char *object, - const char *buffer) { + int level, + int error, + const char*file, + int line, + const char *func, + const char *object_field, + const char *object, + const char *buffer) { char header[LINE_MAX]; struct iovec iovec[4] = {}; @@ -497,8 +514,7 @@ static int write_to_journal( if (journal_fd < 0) return 0; - log_do_header(header, sizeof(header), level, - file, line, func, object_name, object); + log_do_header(header, sizeof(header), level, error, file, line, func, object_field, object); IOVEC_SET_STRING(iovec[0], header); IOVEC_SET_STRING(iovec[1], "MESSAGE="); @@ -515,23 +531,27 @@ static int write_to_journal( } static int log_dispatch( - int level, - const char*file, - int line, - const char *func, - const char *object_name, - const char *object, - char *buffer) { + int level, + int error, + const char *file, + int line, + const char *func, + const char *object_field, + const char *object, + char *buffer) { - int r = 0; + assert(buffer); if (log_target == LOG_TARGET_NULL) - return 0; + return -error; /* Patch in LOG_DAEMON facility if necessary */ if ((level & LOG_FACMASK) == 0) level = log_facility | LOG_PRI(level); + if (error < 0) + error = -error; + do { char *e; int k = 0; @@ -548,27 +568,23 @@ static int log_dispatch( log_target == LOG_TARGET_JOURNAL_OR_KMSG || log_target == LOG_TARGET_JOURNAL) { - k = write_to_journal(level, file, line, func, - object_name, object, buffer); + k = write_to_journal(level, error, file, line, func, object_field, object, buffer); if (k < 0) { if (k != -EAGAIN) log_close_journal(); log_open_kmsg(); - } else if (k > 0) - r++; + } } if (log_target == LOG_TARGET_SYSLOG_OR_KMSG || log_target == LOG_TARGET_SYSLOG) { - k = write_to_syslog(level, file, line, func, - object_name, object, buffer); + k = write_to_syslog(level, error, file, line, func, object_field, object, buffer); if (k < 0) { if (k != -EAGAIN) log_close_syslog(); log_open_kmsg(); - } else if (k > 0) - r++; + } } if (k <= 0 && @@ -578,31 +594,26 @@ static int log_dispatch( log_target == LOG_TARGET_JOURNAL_OR_KMSG || log_target == LOG_TARGET_KMSG)) { - k = write_to_kmsg(level, file, line, func, - object_name, object, buffer); + k = write_to_kmsg(level, error, file, line, func, object_field, object, buffer); if (k < 0) { log_close_kmsg(); log_open_console(); - } else if (k > 0) - r++; + } } - if (k <= 0) { - k = write_to_console(level, file, line, func, - object_name, object, buffer); - if (k < 0) - return k; - } + if (k <= 0) + (void) write_to_console(level, error, file, line, func, object_field, object, buffer); buffer = e; } while (buffer); - return r; + return -error; } int log_dump_internal( int level, - const char*file, + int error, + const char *file, int line, const char *func, char *buffer) { @@ -611,93 +622,119 @@ int log_dump_internal( /* This modifies the buffer... */ + if (error < 0) + error = -error; + if (_likely_(LOG_PRI(level) > log_max_level)) - return 0; + return -error; - return log_dispatch(level, file, line, func, NULL, NULL, buffer); + return log_dispatch(level, error, file, line, func, NULL, NULL, buffer); } -int log_metav( - int level, - const char*file, - int line, - const char *func, - const char *format, - va_list ap) { +int log_internalv( + int level, + int error, + const char*file, + int line, + const char *func, + const char *format, + va_list ap) { PROTECT_ERRNO; char buffer[LINE_MAX]; + if (error < 0) + error = -error; + if (_likely_(LOG_PRI(level) > log_max_level)) - return 0; + return -error; + + /* Make sure that %m maps to the specified error */ + if (error != 0) + errno = error; vsnprintf(buffer, sizeof(buffer), format, ap); char_array_0(buffer); - return log_dispatch(level, file, line, func, NULL, NULL, buffer); + return log_dispatch(level, error, file, line, func, NULL, NULL, buffer); } -int log_meta( - int level, - const char*file, - int line, - const char *func, - const char *format, ...) { +int log_internal( + int level, + int error, + const char*file, + int line, + const char *func, + const char *format, ...) { - int r; va_list ap; + int r; va_start(ap, format); - r = log_metav(level, file, line, func, format, ap); + r = log_internalv(level, error, file, line, func, format, ap); va_end(ap); return r; } -int log_metav_object( - int level, - const char*file, - int line, - const char *func, - const char *object_name, - const char *object, - const char *format, - va_list ap) { +int log_object_internalv( + int level, + int error, + const char*file, + int line, + const char *func, + const char *object_field, + const char *object, + const char *format, + va_list ap) { PROTECT_ERRNO; char buffer[LINE_MAX]; + if (error < 0) + error = -error; + if (_likely_(LOG_PRI(level) > log_max_level)) - return 0; + return -error; + + /* Make sure that %m maps to the specified error */ + if (error != 0) + errno = error; vsnprintf(buffer, sizeof(buffer), format, ap); char_array_0(buffer); - return log_dispatch(level, file, line, func, - object_name, object, buffer); + return log_dispatch(level, error, file, line, func, object_field, object, buffer); } -int log_meta_object( - int level, - const char*file, - int line, - const char *func, - const char *object_name, - const char *object, - const char *format, ...) { +int log_object_internal( + int level, + int error, + const char*file, + int line, + const char *func, + const char *object_field, + const char *object, + const char *format, ...) { - int r; va_list ap; + int r; va_start(ap, format); - r = log_metav_object(level, file, line, func, - object_name, object, format, ap); + r = log_object_internalv(level, error, file, line, func, object_field, object, format, ap); va_end(ap); return r; } -static void log_assert(int level, const char *text, const char *file, int line, const char *func, const char *format) { +static void log_assert( + int level, + const char *text, + const char *file, + int line, + const char *func, + const char *format) { + static char buffer[LINE_MAX]; if (_likely_(LOG_PRI(level) > log_max_level)) @@ -710,7 +747,7 @@ static void log_assert(int level, const char *text, const char *file, int line, char_array_0(buffer); log_abort_msg = buffer; - log_dispatch(level, file, line, func, NULL, NULL, buffer); + log_dispatch(level, 0, file, line, func, NULL, NULL, buffer); } noreturn void log_assert_failed(const char *text, const char *file, int line, const char *func) { @@ -729,26 +766,31 @@ void log_assert_failed_return(const char *text, const char *file, int line, cons } int log_oom_internal(const char *file, int line, const char *func) { - log_meta(LOG_ERR, file, line, func, "Out of memory."); + log_internal(LOG_ERR, ENOMEM, file, line, func, "Out of memory."); return -ENOMEM; } int log_struct_internal( int level, + int error, const char *file, int line, const char *func, const char *format, ...) { + char buf[LINE_MAX]; + bool found = false; PROTECT_ERRNO; va_list ap; - int r; + + if (error < 0) + error = -error; if (_likely_(LOG_PRI(level) > log_max_level)) - return 0; + return -error; if (log_target == LOG_TARGET_NULL) - return 0; + return -error; if ((level & LOG_FACMASK) == 0) level = log_facility | LOG_PRI(level); @@ -757,7 +799,6 @@ int log_struct_internal( log_target == LOG_TARGET_JOURNAL_OR_KMSG || log_target == LOG_TARGET_JOURNAL) && journal_fd >= 0) { - char header[LINE_MAX]; struct iovec iovec[17] = {}; unsigned n = 0, i; @@ -765,25 +806,28 @@ int log_struct_internal( .msg_iov = iovec, }; static const char nl = '\n'; + bool fallback = false; /* If the journal is available do structured logging */ - log_do_header(header, sizeof(header), level, - file, line, func, NULL, NULL); + log_do_header(header, sizeof(header), level, error, file, line, func, NULL, NULL); IOVEC_SET_STRING(iovec[n++], header); va_start(ap, format); while (format && n + 1 < ELEMENTSOF(iovec)) { - char *buf; va_list aq; + char *m; /* We need to copy the va_list structure, * since vasprintf() leaves it afterwards at * an undefined location */ + if (error != 0) + errno = error; + va_copy(aq, ap); - if (vasprintf(&buf, format, aq) < 0) { + if (vasprintf(&m, format, aq) < 0) { va_end(aq); - r = -ENOMEM; + fallback = true; goto finish; } va_end(aq); @@ -792,7 +836,7 @@ int log_struct_internal( * the next format string */ VA_FORMAT_ADVANCE(format, ap); - IOVEC_SET_STRING(iovec[n++], buf); + IOVEC_SET_STRING(iovec[n++], m); iovec[n].iov_base = (char*) &nl; iovec[n].iov_len = 1; @@ -803,50 +847,46 @@ int log_struct_internal( mh.msg_iovlen = n; - if (sendmsg(journal_fd, &mh, MSG_NOSIGNAL) < 0) - r = -errno; - else - r = 1; + (void) sendmsg(journal_fd, &mh, MSG_NOSIGNAL); finish: va_end(ap); for (i = 1; i < n; i += 2) free(iovec[i].iov_base); - } else { - char buf[LINE_MAX]; - bool found = false; - - /* Fallback if journal logging is not available */ + if (!fallback) + return -error; + } - va_start(ap, format); - while (format) { - va_list aq; + /* Fallback if journal logging is not available or didn't work. */ - va_copy(aq, ap); - vsnprintf(buf, sizeof(buf), format, aq); - va_end(aq); - char_array_0(buf); + va_start(ap, format); + while (format) { + va_list aq; - if (startswith(buf, "MESSAGE=")) { - found = true; - break; - } + if (error != 0) + errno = error; - VA_FORMAT_ADVANCE(format, ap); + va_copy(aq, ap); + vsnprintf(buf, sizeof(buf), format, aq); + va_end(aq); + char_array_0(buf); - format = va_arg(ap, char *); + if (startswith(buf, "MESSAGE=")) { + found = true; + break; } - va_end(ap); - if (found) - r = log_dispatch(level, file, line, func, - NULL, NULL, buf + 8); - else - r = -EINVAL; + VA_FORMAT_ADVANCE(format, ap); + + format = va_arg(ap, char *); } + va_end(ap); - return r; + if (!found) + return -error; + + return log_dispatch(level, error, file, line, func, NULL, NULL, buf + 8); } int log_set_target_from_string(const char *e) { @@ -910,7 +950,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value) { void log_parse_environment(void) { const char *e; - parse_proc_cmdline(parse_proc_cmdline_item); + (void) parse_proc_cmdline(parse_proc_cmdline_item); e = secure_getenv("SYSTEMD_LOG_TARGET"); if (e && log_set_target_from_string(e) < 0) diff --git a/src/shared/log.h b/src/shared/log.h index a3e23a855c..2b6971f248 100644 --- a/src/shared/log.h +++ b/src/shared/log.h @@ -25,6 +25,8 @@ #include <stdarg.h> #include <syslog.h> #include <sys/signalfd.h> +#include <sys/types.h> +#include <unistd.h> #include <errno.h> #include "macro.h" @@ -73,46 +75,51 @@ void log_close_console(void); void log_parse_environment(void); -int log_meta( +int log_internal( int level, - const char*file, + int error, + const char *file, int line, const char *func, - const char *format, ...) _printf_(5,6); + const char *format, ...) _printf_(6,7); -int log_metav( +int log_internalv( int level, - const char*file, + int error, + const char *file, int line, const char *func, const char *format, - va_list ap) _printf_(5,0); + va_list ap) _printf_(6,0); -int log_meta_object( +int log_object_internal( int level, - const char*file, + int error, + const char *file, int line, const char *func, - const char *object_name, + const char *object_field, const char *object, - const char *format, ...) _printf_(7,8); + const char *format, ...) _printf_(8,9); -int log_metav_object( +int log_object_internalv( int level, + int error, const char*file, int line, const char *func, - const char *object_name, + const char *object_field, const char *object, const char *format, - va_list ap) _printf_(7,0); + va_list ap) _printf_(8,0); int log_struct_internal( int level, + int error, const char *file, int line, const char *func, - const char *format, ...) _printf_(5,0) _sentinel_; + const char *format, ...) _printf_(6,0) _sentinel_; int log_oom_internal( const char *file, @@ -122,11 +129,13 @@ int log_oom_internal( /* This modifies the buffer passed! */ int log_dump_internal( int level, - const char*file, + int error, + const char *file, int line, const char *func, char *buffer); +/* Logging for various assertions */ noreturn void log_assert_failed( const char *text, const char *file, @@ -145,17 +154,32 @@ void log_assert_failed_return( int line, const char *func); -#define log_full(level, ...) \ -do { \ - if (log_get_max_level() >= (level)) \ - log_meta((level), __FILE__, __LINE__, __func__, __VA_ARGS__); \ -} while (0) - -#define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__) -#define log_info(...) log_full(LOG_INFO, __VA_ARGS__) -#define log_notice(...) log_full(LOG_NOTICE, __VA_ARGS__) -#define log_warning(...) log_full(LOG_WARNING, __VA_ARGS__) -#define log_error(...) log_full(LOG_ERR, __VA_ARGS__) +/* Logging with level */ +#define log_full_errno(level, error, ...) \ + ({ \ + int _l = (level), _e = (error); \ + (log_get_max_level() >= _l) \ + ? log_internal(_l, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \ + : -abs(_e); \ + }) + +#define log_full(level, ...) log_full_errno(level, 0, __VA_ARGS__) + +/* Normal logging */ +#define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__) +#define log_info(...) log_full(LOG_INFO, __VA_ARGS__) +#define log_notice(...) log_full(LOG_NOTICE, __VA_ARGS__) +#define log_warning(...) log_full(LOG_WARNING, __VA_ARGS__) +#define log_error(...) log_full(LOG_ERR, __VA_ARGS__) +#define log_emergency(...) log_full(getpid() == 1 ? LOG_EMERG : LOG_ERR, __VA_ARGS__) + +/* Logging triggered by an errno-like error */ +#define log_debug_errno(error, ...) log_full_errno(LOG_DEBUG, error, __VA_ARGS__) +#define log_info_errno(error, ...) log_full_errno(LOG_INFO, error, __VA_ARGS__) +#define log_notice_errno(error, ...) log_full_errno(LOG_NOTICE, error, __VA_ARGS__) +#define log_warning_errno(error, ...) log_full_errno(LOG_WARNING, error, __VA_ARGS__) +#define log_error_errno(error, ...) log_full_errno(LOG_ERR, error, __VA_ARGS__) +#define log_emergency_errno(error, ...) log_full_errno(getpid() == 1 ? LOG_EMERG : LOG_ERR, error, __VA_ARGS__) #ifdef LOG_TRACE # define log_trace(...) log_debug(__VA_ARGS__) @@ -163,19 +187,24 @@ do { \ # define log_trace(...) do {} while(0) #endif -#define log_struct(level, ...) log_struct_internal(level, __FILE__, __LINE__, __func__, __VA_ARGS__) - -#define log_oom() log_oom_internal(__FILE__, __LINE__, __func__) +/* Structured logging */ +#define log_struct(level, ...) log_struct_internal(level, 0, __FILE__, __LINE__, __func__, __VA_ARGS__) +#define log_struct_errno(level, error, ...) log_struct_internal(level, error, __FILE__, __LINE__, __func__, __VA_ARGS__) /* This modifies the buffer passed! */ -#define log_dump(level, buffer) log_dump_internal(level, __FILE__, __LINE__, __func__, buffer) +#define log_dump(level, buffer) log_dump_internal(level, 0, __FILE__, __LINE__, __func__, buffer) + +#define log_oom() log_oom_internal(__FILE__, __LINE__, __func__) bool log_on_console(void) _pure_; const char *log_target_to_string(LogTarget target) _const_; LogTarget log_target_from_string(const char *s) _pure_; -#define MESSAGE_ID(x) "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(x) +/* Helpers to prepare various fields for structured logging */ +#define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__ +#define LOG_MESSAGE_ID(x) "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(x) +#define LOG_ERRNO(error) "ERRNO=%i", abs(error) void log_received_signal(int level, const struct signalfd_siginfo *si); diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index 04e1165191..98972eda3b 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -295,10 +295,8 @@ static int output_short( if (r < 0) r = sd_journal_get_monotonic_usec(j, &t, &boot_id); - if (r < 0) { - log_error("Failed to get monotonic timestamp: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get monotonic timestamp: %m"); fprintf(f, "[%5llu.%06llu]", (unsigned long long) (t / USEC_PER_SEC), @@ -322,10 +320,8 @@ static int output_short( if (r < 0) r = sd_journal_get_realtime_usec(j, &x); - if (r < 0) { - log_error("Failed to get realtime timestamp: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get realtime timestamp: %m"); t = (time_t) (x / USEC_PER_SEC); @@ -365,7 +361,7 @@ static int output_short( fprintf(f, " %.*s", (int) comm_len, comm); n += comm_len + 1; } else - fputc(' ', f); + fputs(" unknown", f); if (pid && shall_print(pid, pid_len, flags)) { fprintf(f, "[%.*s]", (int) pid_len, pid); @@ -422,12 +418,11 @@ static int output_verbose( r = parse_field(data, length, "_SOURCE_REALTIME_TIMESTAMP=", &value, &size); if (r < 0) - log_debug("_SOURCE_REALTIME_TIMESTAMP invalid: %s", strerror(-r)); + log_debug_errno(r, "_SOURCE_REALTIME_TIMESTAMP invalid: %m"); else { r = safe_atou64(value, &realtime); if (r < 0) - log_debug("Failed to parse realtime timestamp: %s", - strerror(-r)); + log_debug_errno(r, "Failed to parse realtime timestamp: %m"); } } @@ -441,10 +436,8 @@ static int output_verbose( } r = sd_journal_get_cursor(j, &cursor); - if (r < 0) { - log_error("Failed to get cursor: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get cursor: %m"); fprintf(f, "%s [%s]\n", flags & OUTPUT_UTC ? @@ -516,22 +509,16 @@ static int output_export( sd_journal_set_data_threshold(j, 0); r = sd_journal_get_realtime_usec(j, &realtime); - if (r < 0) { - log_error("Failed to get realtime timestamp: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get realtime timestamp: %m"); r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id); - if (r < 0) { - log_error("Failed to get monotonic timestamp: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get monotonic timestamp: %m"); r = sd_journal_get_cursor(j, &cursor); - if (r < 0) { - log_error("Failed to get cursor: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get cursor: %m"); fprintf(f, "__CURSOR=%s\n" @@ -656,22 +643,16 @@ static int output_json( sd_journal_set_data_threshold(j, flags & OUTPUT_SHOW_ALL ? 0 : JSON_THRESHOLD); r = sd_journal_get_realtime_usec(j, &realtime); - if (r < 0) { - log_error("Failed to get realtime timestamp: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get realtime timestamp: %m"); r = sd_journal_get_monotonic_usec(j, &monotonic, &boot_id); - if (r < 0) { - log_error("Failed to get monotonic timestamp: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get monotonic timestamp: %m"); r = sd_journal_get_cursor(j, &cursor); - if (r < 0) { - log_error("Failed to get cursor: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get cursor: %m"); if (mode == OUTPUT_JSON_PRETTY) fprintf(f, @@ -877,8 +858,7 @@ static int output_cat( if (r == -ENOENT) return 0; - log_error("Failed to get data: %s", strerror(-r)); - return r; + return log_error_errno(r, "Failed to get data: %m"); } assert(l >= 8); @@ -1225,24 +1205,18 @@ int add_match_this_boot(sd_journal *j, const char *machine) { if (machine) { r = get_boot_id_for_machine(machine, &boot_id); - if (r < 0) { - log_error("Failed to get boot id of container %s: %s", machine, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get boot id of container %s: %m", machine); } else { r = sd_id128_get_boot(&boot_id); - if (r < 0) { - log_error("Failed to get boot id: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get boot id: %m"); } sd_id128_to_string(boot_id, match + 9); r = sd_journal_add_match(j, match, strlen(match)); - if (r < 0) { - log_error("Failed to add match: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add match: %m"); r = sd_journal_add_conjunction(j); if (r < 0) diff --git a/src/shared/macro.h b/src/shared/macro.h index 9ee332c8df..548294e47b 100644 --- a/src/shared/macro.h +++ b/src/shared/macro.h @@ -95,15 +95,15 @@ #error "Wut? Pointers are neither 4 nor 8 bytes long?" #endif -#define ALIGN_PTR(p) ((void*) ALIGN((unsigned long) p)) -#define ALIGN4_PTR(p) ((void*) ALIGN4((unsigned long) p)) -#define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) p)) +#define ALIGN_PTR(p) ((void*) ALIGN((unsigned long) (p))) +#define ALIGN4_PTR(p) ((void*) ALIGN4((unsigned long) (p))) +#define ALIGN8_PTR(p) ((void*) ALIGN8((unsigned long) (p))) static inline size_t ALIGN_TO(size_t l, size_t ali) { return ((l + ali - 1) & ~(ali - 1)); } -#define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) p, ali)) +#define ALIGN_TO_PTR(p, ali) ((void*) ALIGN_TO((unsigned long) (p), (ali))) /* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */ static inline unsigned long ALIGN_POWER2(unsigned long u) { @@ -384,6 +384,21 @@ do { \ _found; \ }) +/* Return a nulstr for a standard cascade of configuration directories, + * suitable to pass to conf_files_list_nulstr or config_parse_many. */ +#define CONF_DIRS_NULSTR(n) \ + "/etc/" n ".d\0" \ + "/run/" n ".d\0" \ + "/usr/local/lib/" n ".d\0" \ + "/usr/lib/" n ".d\0" \ + CONF_DIR_SPLIT_USR(n) + +#ifdef HAVE_SPLIT_USR +#define CONF_DIR_SPLIT_USR(n) "/lib/" n ".d\0" +#else +#define CONF_DIR_SPLIT_USR(n) +#endif + /* Define C11 thread_local attribute even on older gcc compiler * version */ #ifndef thread_local @@ -408,4 +423,8 @@ do { \ #endif #endif +#define UID_INVALID ((uid_t) -1) +#define GID_INVALID ((gid_t) -1) +#define MODE_INVALID ((mode_t) -1) + #include "log.h" diff --git a/src/shared/memfd.c b/src/shared/memfd-util.c index 162c12f7a7..6624c5e7db 100644 --- a/src/shared/memfd.c +++ b/src/shared/memfd-util.c @@ -31,7 +31,7 @@ #include "util.h" #include "bus-label.h" -#include "memfd.h" +#include "memfd-util.h" #include "utf8.h" #include "missing.h" @@ -65,7 +65,7 @@ int memfd_new(const char *name) { } } - fd = memfd_create(name, MFD_ALLOW_SEALING); + fd = memfd_create(name, MFD_ALLOW_SEALING | MFD_CLOEXEC); if (fd < 0) return -errno; @@ -101,7 +101,7 @@ int memfd_set_sealed(int fd) { assert(fd >= 0); - r = fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE); + r = fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL); if (r < 0) return -errno; @@ -117,8 +117,7 @@ int memfd_get_sealed(int fd) { if (r < 0) return -errno; - return (r & (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE)) == - (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE); + return r == (F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE | F_SEAL_SEAL); } int memfd_get_size(int fd, uint64_t *sz) { @@ -172,50 +171,3 @@ int memfd_new_and_map(const char *name, size_t sz, void **p) { return r; } - -int memfd_get_name(int fd, char **name) { - char path[sizeof("/proc/self/fd/") + DECIMAL_STR_MAX(int)], buf[FILENAME_MAX+1], *e; - const char *delim, *end; - _cleanup_free_ char *n = NULL; - ssize_t k; - - assert(fd >= 0); - assert(name); - - sprintf(path, "/proc/self/fd/%i", fd); - - k = readlink(path, buf, sizeof(buf)); - if (k < 0) - return -errno; - - if ((size_t) k >= sizeof(buf)) - return -E2BIG; - - buf[k] = 0; - - delim = strstr(buf, ":["); - if (!delim) - return -EIO; - - delim = strchr(delim + 2, ':'); - if (!delim) - return -EIO; - - delim++; - - end = strchr(delim, ']'); - if (!end) - return -EIO; - - n = strndup(delim, end - delim); - if (!n) - return -ENOMEM; - - e = utf8_escape_invalid(n); - if (!e) - return -ENOMEM; - - *name = e; - - return 0; -} diff --git a/src/shared/memfd.h b/src/shared/memfd-util.h index 8f02b0ff55..cf588fe02f 100644 --- a/src/shared/memfd.h +++ b/src/shared/memfd-util.h @@ -38,5 +38,3 @@ int memfd_get_sealed(int fd); int memfd_get_size(int fd, uint64_t *sz); int memfd_set_size(int fd, uint64_t sz); - -int memfd_get_name(int fd, char **name); diff --git a/src/shared/mempool.c b/src/shared/mempool.c index b39a37f2db..d5d98d8829 100644 --- a/src/shared/mempool.c +++ b/src/shared/mempool.c @@ -74,6 +74,15 @@ void* mempool_alloc_tile(struct mempool *mp) { return ((uint8_t*) mp->first_pool) + ALIGN(sizeof(struct pool)) + i*mp->tile_size; } +void* mempool_alloc0_tile(struct mempool *mp) { + void *p; + + p = mempool_alloc_tile(mp); + if (p) + memzero(p, mp->tile_size); + return p; +} + void mempool_free_tile(struct mempool *mp, void *p) { * (void**) p = mp->freelist; mp->freelist = p; diff --git a/src/shared/mempool.h b/src/shared/mempool.h index 8b0bf381bf..42f473bee1 100644 --- a/src/shared/mempool.h +++ b/src/shared/mempool.h @@ -34,6 +34,7 @@ struct mempool { }; void* mempool_alloc_tile(struct mempool *mp); +void* mempool_alloc0_tile(struct mempool *mp); void mempool_free_tile(struct mempool *mp, void *p); #define DEFINE_MEMPOOL(pool_name, tile_type, alloc_at_least) \ diff --git a/src/shared/missing.h b/src/shared/missing.h index bb4f8f23a8..478988c8a4 100644 --- a/src/shared/missing.h +++ b/src/shared/missing.h @@ -33,6 +33,8 @@ #include <linux/input.h> #include <linux/if_link.h> #include <linux/loop.h> +#include <linux/audit.h> +#include <linux/capability.h> #ifdef HAVE_AUDIT #include <libaudit.h> @@ -74,7 +76,11 @@ #endif #ifndef MFD_ALLOW_SEALING -#define MFD_ALLOW_SEALING 0x0002ULL +#define MFD_ALLOW_SEALING 0x0002U +#endif + +#ifndef MFD_CLOEXEC +#define MFD_CLOEXEC 0x0001U #endif #ifndef IP_FREEBIND @@ -115,22 +121,26 @@ static inline int pivot_root(const char *new_root, const char *put_old) { } #endif -#ifdef __x86_64__ -# ifndef __NR_memfd_create +#ifndef __NR_memfd_create +# if defined __x86_64__ # define __NR_memfd_create 319 -# endif -#elif defined __arm__ -# ifndef __NR_memfd_create +# elif defined __arm__ # define __NR_memfd_create 385 -# endif -#elif defined _MIPS_SIM -# ifndef __NR_memfd_create -# warning "__NR_memfd_create not yet defined for MIPS" -# define __NR_memfd_create 0xffffffff -# endif -#else -# ifndef __NR_memfd_create +# elif defined _MIPS_SIM +# if _MIPS_SIM == _MIPS_SIM_ABI32 +# define __NR_memfd_create 4354 +# endif +# if _MIPS_SIM == _MIPS_SIM_NABI32 +# define __NR_memfd_create 6318 +# endif +# if _MIPS_SIM == _MIPS_SIM_ABI64 +# define __NR_memfd_create 5314 +# endif +# elif defined __i386__ # define __NR_memfd_create 356 +# else +# warning "__NR_memfd_create unknown for your architecture" +# define __NR_memfd_create 0xffffffff # endif #endif @@ -140,6 +150,39 @@ static inline int memfd_create(const char *name, unsigned int flags) { } #endif +#ifndef __NR_getrandom +# if defined __x86_64__ +# define __NR_getrandom 318 +# elif defined(__i386__) +# define __NR_getrandom 355 +# elif defined(__arm__) || defined(__aarch64__) +# define __NR_getrandom 384 +# elif defined(__ia64__) +# define __NR_getrandom 1339 +# elif defined(__m68k__) +# define __NR_getrandom 352 +# elif defined(__s390x__) +# define __NR_getrandom 349 +# else +# warning "__NR_getrandom unknown for your architecture" +# define __NR_getrandom 0xffffffff +# endif +#endif + +#if !HAVE_DECL_GETRANDOM +static inline int getrandom(void *buffer, size_t count, unsigned flags) { + return syscall(__NR_getrandom, buffer, count, flags); +} +#endif + +#ifndef GRND_NONBLOCK +#define GRND_NONBLOCK 0x0001 +#endif + +#ifndef GRND_RANDOM +#define GRND_RANDOM 0x0002 +#endif + #ifndef BTRFS_IOCTL_MAGIC #define BTRFS_IOCTL_MAGIC 0x94 #endif @@ -411,7 +454,7 @@ static inline int setns(int fd, int nstype) { #define IFLA_BOND_AD_INFO 23 #define __IFLA_BOND_MAX 24 -#define IFLA_BOND_MAX (__IFLA_BOND_MAX - 1) +#define IFLA_BOND_MAX (__IFLA_BOND_MAX - 1) #endif #if !HAVE_DECL_IFLA_VLAN_PROTOCOL @@ -480,6 +523,22 @@ static inline int setns(int fd, int nstype) { #define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1) #endif +#if !HAVE_DECL_IFLA_BRPORT_UNICAST_FLOOD +#define IFLA_BRPORT_UNSPEC 0 +#define IFLA_BRPORT_STATE 1 +#define IFLA_BRPORT_PRIORITY 2 +#define IFLA_BRPORT_COST 3 +#define IFLA_BRPORT_MODE 4 +#define IFLA_BRPORT_GUARD 5 +#define IFLA_BRPORT_PROTECT 6 +#define IFLA_BRPORT_FAST_LEAVE 7 +#define IFLA_BRPORT_LEARNING 8 +#define IFLA_BRPORT_UNICAST_FLOOD 9 +#define __IFLA_BRPORT_MAX 10 + +#define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) +#endif + #ifndef IPV6_UNICAST_IF #define IPV6_UNICAST_IF 76 #endif @@ -508,6 +567,10 @@ static inline int setns(int fd, int nstype) { # define NET_ADDR_RANDOM 1 #endif +#ifndef NET_NAME_UNKNOWN +# define NET_NAME_UNKNOWN 0 +#endif + #ifndef NET_NAME_ENUM # define NET_NAME_ENUM 1 #endif @@ -536,3 +599,35 @@ static inline int setns(int fd, int nstype) { #ifndef LOOPBACK_IFINDEX #define LOOPBACK_IFINDEX 1 #endif + +#ifndef MAX_AUDIT_MESSAGE_LENGTH +#define MAX_AUDIT_MESSAGE_LENGTH 8970 +#endif + +#ifndef AUDIT_NLGRP_MAX +#define AUDIT_NLGRP_READLOG 1 +#endif + +#ifndef CAP_MAC_OVERRIDE +#define CAP_MAC_OVERRIDE 32 +#endif + +#ifndef CAP_MAC_ADMIN +#define CAP_MAC_ADMIN 33 +#endif + +#ifndef CAP_SYSLOG +#define CAP_SYSLOG 34 +#endif + +#ifndef CAP_WAKE_ALARM +#define CAP_WAKE_ALARM 35 +#endif + +#ifndef CAP_BLOCK_SUSPEND +#define CAP_BLOCK_SUSPEND 36 +#endif + +#ifndef CAP_AUDIT_READ +#define CAP_AUDIT_READ 37 +#endif diff --git a/src/shared/mkdir.c b/src/shared/mkdir.c index ef3f494cc8..beefd1052a 100644 --- a/src/shared/mkdir.c +++ b/src/shared/mkdir.c @@ -44,8 +44,8 @@ int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, mkd if ((st.st_mode & 0007) > (mode & 0007) || (st.st_mode & 0070) > (mode & 0070) || (st.st_mode & 0700) > (mode & 0700) || - (uid != (uid_t) -1 && st.st_uid != uid) || - (gid != (gid_t) -1 && st.st_gid != gid) || + (uid != UID_INVALID && st.st_uid != uid) || + (gid != GID_INVALID && st.st_gid != gid) || !S_ISDIR(st.st_mode)) { errno = EEXIST; return -errno; diff --git a/src/shared/pager.c b/src/shared/pager.c index 54790947ba..a9f2b7e4f4 100644 --- a/src/shared/pager.c +++ b/src/shared/pager.c @@ -40,7 +40,7 @@ noreturn static void pager_fallback(void) { } while (n > 0); if (n < 0) { - log_error("Internal pager failed: %m"); + log_error_errno(errno, "Internal pager failed: %m"); _exit(EXIT_FAILURE); } @@ -67,17 +67,15 @@ int pager_open(bool jump_to_end) { * pager so that we get the value from the actual tty */ columns(); - if (pipe(fd) < 0) { - log_error("Failed to create pager pipe: %m"); - return -errno; - } + if (pipe(fd) < 0) + return log_error_errno(errno, "Failed to create pager pipe: %m"); parent_pid = getpid(); pager_pid = fork(); if (pager_pid < 0) { r = -errno; - log_error("Failed to fork pager: %m"); + log_error_errno(errno, "Failed to fork pager: %m"); safe_close_pair(fd); return r; } @@ -126,10 +124,8 @@ int pager_open(bool jump_to_end) { } /* Return in the parent */ - if (dup2(fd[1], STDOUT_FILENO) < 0) { - log_error("Failed to duplicate pager pipe: %m"); - return -errno; - } + if (dup2(fd[1], STDOUT_FILENO) < 0) + return log_error_errno(errno, "Failed to duplicate pager pipe: %m"); safe_close_pair(fd); return 1; @@ -143,7 +139,7 @@ void pager_close(void) { /* Inform pager that we are done */ fclose(stdout); kill(pager_pid, SIGCONT); - wait_for_terminate(pager_pid, NULL); + (void) wait_for_terminate(pager_pid, NULL); pager_pid = 0; } @@ -176,23 +172,21 @@ int show_man_page(const char *desc, bool null_stdio) { args[1] = desc; pid = fork(); - if (pid < 0) { - log_error("Failed to fork: %m"); - return -errno; - } + if (pid < 0) + return log_error_errno(errno, "Failed to fork: %m"); if (pid == 0) { /* Child */ if (null_stdio) { r = make_null_stdio(); if (r < 0) { - log_error("Failed to kill stdio: %s", strerror(-r)); + log_error_errno(r, "Failed to kill stdio: %m"); _exit(EXIT_FAILURE); } } execvp(args[0], (char**) args); - log_error("Failed to execute man: %m"); + log_error_errno(errno, "Failed to execute man: %m"); _exit(EXIT_FAILURE); } diff --git a/src/shared/path-util.c b/src/shared/path-util.c index 67566bc76b..be03695cf8 100644 --- a/src/shared/path-util.c +++ b/src/shared/path-util.c @@ -563,11 +563,11 @@ int path_is_os_tree(const char *path) { return r >= 0; } -int find_binary(const char *name, char **filename) { +int find_binary(const char *name, bool local, char **filename) { assert(name); if (is_path(name)) { - if (access(name, X_OK) < 0) + if (local && access(name, X_OK) < 0) return -errno; if (filename) { @@ -657,7 +657,7 @@ int fsck_exists(const char *fstype) { checker = strappenda("fsck.", fstype); - r = find_binary(checker, &p); + r = find_binary(checker, true, &p); if (r < 0) return r; diff --git a/src/shared/path-util.h b/src/shared/path-util.h index 8d171a57ec..bd0d32473f 100644 --- a/src/shared/path-util.h +++ b/src/shared/path-util.h @@ -55,7 +55,7 @@ int path_is_mount_point(const char *path, bool allow_symlink); int path_is_read_only_fs(const char *path); int path_is_os_tree(const char *path); -int find_binary(const char *name, char **filename); +int find_binary(const char *name, bool local, char **filename); bool paths_check_timestamp(const char* const* paths, usec_t *paths_ts_usec, bool update); diff --git a/src/shared/ptyfwd.c b/src/shared/ptyfwd.c index d44d70bf9f..085d374ed8 100644 --- a/src/shared/ptyfwd.c +++ b/src/shared/ptyfwd.c @@ -28,13 +28,44 @@ #include "util.h" #include "ptyfwd.h" -#define ESCAPE_USEC USEC_PER_SEC +struct PTYForward { + sd_event *event; -static bool look_for_escape(usec_t *timestamp, unsigned *counter, const char *buffer, size_t n) { + int master; + + sd_event_source *stdin_event_source; + sd_event_source *stdout_event_source; + sd_event_source *master_event_source; + + sd_event_source *sigwinch_event_source; + + struct termios saved_stdin_attr; + struct termios saved_stdout_attr; + + bool saved_stdin:1; + bool saved_stdout:1; + + bool stdin_readable:1; + bool stdin_hangup:1; + bool stdout_writable:1; + bool stdout_hangup:1; + bool master_readable:1; + bool master_writable:1; + bool master_hangup:1; + + char in_buffer[LINE_MAX], out_buffer[LINE_MAX]; + size_t in_buffer_full, out_buffer_full; + + usec_t escape_timestamp; + unsigned escape_counter; +}; + +#define ESCAPE_USEC (1*USEC_PER_SEC) + +static bool look_for_escape(PTYForward *f, const char *buffer, size_t n) { const char *p; - assert(timestamp); - assert(counter); + assert(f); assert(buffer); assert(n > 0); @@ -44,343 +75,316 @@ static bool look_for_escape(usec_t *timestamp, unsigned *counter, const char *bu if (*p == 0x1D) { usec_t nw = now(CLOCK_MONOTONIC); - if (*counter == 0 || nw > *timestamp + USEC_PER_SEC) { - *timestamp = nw; - *counter = 1; + if (f->escape_counter == 0 || nw > f->escape_timestamp + ESCAPE_USEC) { + f->escape_timestamp = nw; + f->escape_counter = 1; } else { - (*counter)++; + (f->escape_counter)++; - if (*counter >= 3) + if (f->escape_counter >= 3) return true; } } else { - *timestamp = 0; - *counter = 0; + f->escape_timestamp = 0; + f->escape_counter = 0; } } return false; } -static int process_pty_loop(int master, sigset_t *mask, pid_t kill_pid, int signo) { - char in_buffer[LINE_MAX], out_buffer[LINE_MAX]; - size_t in_buffer_full = 0, out_buffer_full = 0; - struct epoll_event stdin_ev, stdout_ev, master_ev, signal_ev; - bool stdin_readable = false, stdout_writable = false, master_readable = false, master_writable = false; - bool stdin_hangup = false, stdout_hangup = false, master_hangup = false; - bool tried_orderly_shutdown = false, process_signalfd = false, quit = false; - usec_t escape_timestamp = 0; - unsigned escape_counter = 0; - _cleanup_close_ int ep = -1, signal_fd = -1; - - assert(master >= 0); - assert(mask); - assert(kill_pid == 0 || kill_pid > 1); - assert(signo >= 0 && signo < _NSIG); - - fd_nonblock(STDIN_FILENO, true); - fd_nonblock(STDOUT_FILENO, true); - fd_nonblock(master, true); - - signal_fd = signalfd(-1, mask, SFD_NONBLOCK|SFD_CLOEXEC); - if (signal_fd < 0) { - log_error("signalfd(): %m"); - return -errno; - } - - ep = epoll_create1(EPOLL_CLOEXEC); - if (ep < 0) { - log_error("Failed to create epoll: %m"); - return -errno; - } +static int shovel(PTYForward *f) { + ssize_t k; - /* We read from STDIN only if this is actually a TTY, - * otherwise we assume non-interactivity. */ - if (isatty(STDIN_FILENO)) { - zero(stdin_ev); - stdin_ev.events = EPOLLIN|EPOLLET; - stdin_ev.data.fd = STDIN_FILENO; + assert(f); - if (epoll_ctl(ep, EPOLL_CTL_ADD, STDIN_FILENO, &stdin_ev) < 0) { - log_error("Failed to register STDIN in epoll: %m"); - return -errno; - } - } + while ((f->stdin_readable && f->in_buffer_full <= 0) || + (f->master_writable && f->in_buffer_full > 0) || + (f->master_readable && f->out_buffer_full <= 0) || + (f->stdout_writable && f->out_buffer_full > 0)) { - zero(stdout_ev); - stdout_ev.events = EPOLLOUT|EPOLLET; - stdout_ev.data.fd = STDOUT_FILENO; + if (f->stdin_readable && f->in_buffer_full < LINE_MAX) { - zero(master_ev); - master_ev.events = EPOLLIN|EPOLLOUT|EPOLLET; - master_ev.data.fd = master; + k = read(STDIN_FILENO, f->in_buffer + f->in_buffer_full, LINE_MAX - f->in_buffer_full); + if (k < 0) { - zero(signal_ev); - signal_ev.events = EPOLLIN; - signal_ev.data.fd = signal_fd; + if (errno == EAGAIN) + f->stdin_readable = false; + else if (errno == EIO || errno == EPIPE || errno == ECONNRESET) { + f->stdin_readable = false; + f->stdin_hangup = true; - if (epoll_ctl(ep, EPOLL_CTL_ADD, STDOUT_FILENO, &stdout_ev) < 0) { - if (errno != EPERM) { - log_error("Failed to register stdout in epoll: %m"); - return -errno; + f->stdin_event_source = sd_event_source_unref(f->stdin_event_source); + } else { + log_error_errno(errno, "read(): %m"); + return sd_event_exit(f->event, EXIT_FAILURE); + } + } else if (k == 0) { + /* EOF on stdin */ + f->stdin_readable = false; + f->stdin_hangup = true; + + f->stdin_event_source = sd_event_source_unref(f->stdin_event_source); + } else { + /* Check if ^] has been + * pressed three times within + * one second. If we get this + * we quite immediately. */ + if (look_for_escape(f, f->in_buffer + f->in_buffer_full, k)) + return sd_event_exit(f->event, EXIT_FAILURE); + + f->in_buffer_full += (size_t) k; + } } - /* stdout without epoll support. Likely redirected to regular file. */ - stdout_writable = true; - } - - if (epoll_ctl(ep, EPOLL_CTL_ADD, master, &master_ev) < 0 || - epoll_ctl(ep, EPOLL_CTL_ADD, signal_fd, &signal_ev) < 0) { - log_error("Failed to register fds in epoll: %m"); - return -errno; - } - - for (;;) { - struct epoll_event ev[16]; - ssize_t k; - int i, nfds; + if (f->master_writable && f->in_buffer_full > 0) { - nfds = epoll_wait(ep, ev, ELEMENTSOF(ev), quit ? 0 : -1); - if (nfds < 0) { + k = write(f->master, f->in_buffer, f->in_buffer_full); + if (k < 0) { - if (errno == EINTR || errno == EAGAIN) - continue; + if (errno == EAGAIN || errno == EIO) + f->master_writable = false; + else if (errno == EPIPE || errno == ECONNRESET) { + f->master_writable = f->master_readable = false; + f->master_hangup = true; - log_error("epoll_wait(): %m"); - return -errno; + f->master_event_source = sd_event_source_unref(f->master_event_source); + } else { + log_error_errno(errno, "write(): %m"); + return sd_event_exit(f->event, EXIT_FAILURE); + } + } else { + assert(f->in_buffer_full >= (size_t) k); + memmove(f->in_buffer, f->in_buffer + k, f->in_buffer_full - k); + f->in_buffer_full -= k; + } } - if (nfds == 0) - return 0; - - for (i = 0; i < nfds; i++) { - if (ev[i].data.fd == STDIN_FILENO) { - - if (ev[i].events & (EPOLLIN|EPOLLHUP)) - stdin_readable = true; - - } else if (ev[i].data.fd == STDOUT_FILENO) { - - if (ev[i].events & (EPOLLOUT|EPOLLHUP)) - stdout_writable = true; + if (f->master_readable && f->out_buffer_full < LINE_MAX) { - } else if (ev[i].data.fd == master) { + k = read(f->master, f->out_buffer + f->out_buffer_full, LINE_MAX - f->out_buffer_full); + if (k < 0) { - if (ev[i].events & (EPOLLIN|EPOLLHUP)) - master_readable = true; + /* Note that EIO on the master device + * might be cause by vhangup() or + * temporary closing of everything on + * the other side, we treat it like + * EAGAIN here and try again. */ - if (ev[i].events & (EPOLLOUT|EPOLLHUP)) - master_writable = true; + if (errno == EAGAIN || errno == EIO) + f->master_readable = false; + else if (errno == EPIPE || errno == ECONNRESET) { + f->master_readable = f->master_writable = false; + f->master_hangup = true; - } else if (ev[i].data.fd == signal_fd) - process_signalfd = true; - } - - while ((stdin_readable && in_buffer_full <= 0) || - (master_writable && in_buffer_full > 0) || - (master_readable && out_buffer_full <= 0) || - (stdout_writable && out_buffer_full > 0)) { - - if (stdin_readable && in_buffer_full < LINE_MAX) { - - k = read(STDIN_FILENO, in_buffer + in_buffer_full, LINE_MAX - in_buffer_full); - if (k < 0) { - - if (errno == EAGAIN) - stdin_readable = false; - else if (errno == EIO || errno == EPIPE || errno == ECONNRESET) { - stdin_readable = false; - stdin_hangup = true; - epoll_ctl(ep, EPOLL_CTL_DEL, STDIN_FILENO, NULL); - } else { - log_error("read(): %m"); - return -errno; - } + f->master_event_source = sd_event_source_unref(f->master_event_source); } else { - /* Check if ^] has been - * pressed three times within - * one second. If we get this - * we quite immediately. */ - if (look_for_escape(&escape_timestamp, &escape_counter, in_buffer + in_buffer_full, k)) - return !quit; - - in_buffer_full += (size_t) k; + log_error_errno(errno, "read(): %m"); + return sd_event_exit(f->event, EXIT_FAILURE); } - } - - if (master_writable && in_buffer_full > 0) { + } else + f->out_buffer_full += (size_t) k; + } - k = write(master, in_buffer, in_buffer_full); - if (k < 0) { + if (f->stdout_writable && f->out_buffer_full > 0) { - if (errno == EAGAIN || errno == EIO) - master_writable = false; - else if (errno == EPIPE || errno == ECONNRESET) { - master_writable = master_readable = false; - master_hangup = true; - epoll_ctl(ep, EPOLL_CTL_DEL, master, NULL); - } else { - log_error("write(): %m"); - return -errno; - } + k = write(STDOUT_FILENO, f->out_buffer, f->out_buffer_full); + if (k < 0) { + if (errno == EAGAIN) + f->stdout_writable = false; + else if (errno == EIO || errno == EPIPE || errno == ECONNRESET) { + f->stdout_writable = false; + f->stdout_hangup = true; + f->stdout_event_source = sd_event_source_unref(f->stdout_event_source); } else { - assert(in_buffer_full >= (size_t) k); - memmove(in_buffer, in_buffer + k, in_buffer_full - k); - in_buffer_full -= k; + log_error_errno(errno, "write(): %m"); + return sd_event_exit(f->event, EXIT_FAILURE); } - } - if (master_readable && out_buffer_full < LINE_MAX) { - - k = read(master, out_buffer + out_buffer_full, LINE_MAX - out_buffer_full); - if (k < 0) { - - /* Note that EIO on the master - * device might be cause by - * vhangup() or temporary - * closing of everything on - * the other side, we treat it - * like EAGAIN here and try - * again. */ - - if (errno == EAGAIN || errno == EIO) - master_readable = false; - else if (errno == EPIPE || errno == ECONNRESET) { - master_readable = master_writable = false; - master_hangup = true; - epoll_ctl(ep, EPOLL_CTL_DEL, master, NULL); - } else { - log_error("read(): %m"); - return -errno; - } - } else - out_buffer_full += (size_t) k; + } else { + assert(f->out_buffer_full >= (size_t) k); + memmove(f->out_buffer, f->out_buffer + k, f->out_buffer_full - k); + f->out_buffer_full -= k; } + } + } + + if (f->stdin_hangup || f->stdout_hangup || f->master_hangup) { + /* Exit the loop if any side hung up and if there's + * nothing more to write or nothing we could write. */ - if (stdout_writable && out_buffer_full > 0) { + if ((f->out_buffer_full <= 0 || f->stdout_hangup) && + (f->in_buffer_full <= 0 || f->master_hangup)) + return sd_event_exit(f->event, EXIT_SUCCESS); + } - k = write(STDOUT_FILENO, out_buffer, out_buffer_full); - if (k < 0) { + return 0; +} - if (errno == EAGAIN) - stdout_writable = false; - else if (errno == EIO || errno == EPIPE || errno == ECONNRESET) { - stdout_writable = false; - stdout_hangup = true; - epoll_ctl(ep, EPOLL_CTL_DEL, STDOUT_FILENO, NULL); - } else { - log_error("write(): %m"); - return -errno; - } +static int on_master_event(sd_event_source *e, int fd, uint32_t revents, void *userdata) { + PTYForward *f = userdata; - } else { - assert(out_buffer_full >= (size_t) k); - memmove(out_buffer, out_buffer + k, out_buffer_full - k); - out_buffer_full -= k; - } - } + assert(f); + assert(e); + assert(e == f->master_event_source); + assert(fd >= 0); + assert(fd == f->master); - } + if (revents & (EPOLLIN|EPOLLHUP)) + f->master_readable = true; - if (process_signalfd) { - struct signalfd_siginfo sfsi; - ssize_t n; + if (revents & (EPOLLOUT|EPOLLHUP)) + f->master_writable = true; - n = read(signal_fd, &sfsi, sizeof(sfsi)); - if (n != sizeof(sfsi)) { + return shovel(f); +} - if (n >= 0) { - log_error("Failed to read from signalfd: invalid block size"); - return -EIO; - } +static int on_stdin_event(sd_event_source *e, int fd, uint32_t revents, void *userdata) { + PTYForward *f = userdata; - if (errno != EINTR && errno != EAGAIN) { - log_error("Failed to read from signalfd: %m"); - return -errno; - } - } else { + assert(f); + assert(e); + assert(e == f->stdin_event_source); + assert(fd >= 0); + assert(fd == STDIN_FILENO); - if (sfsi.ssi_signo == SIGWINCH) { - struct winsize ws; + if (revents & (EPOLLIN|EPOLLHUP)) + f->stdin_readable = true; - /* The window size changed, let's forward that. */ - if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0) - ioctl(master, TIOCSWINSZ, &ws); + return shovel(f); +} - } else if (sfsi.ssi_signo == SIGTERM && kill_pid > 0 && signo > 0 && !tried_orderly_shutdown) { +static int on_stdout_event(sd_event_source *e, int fd, uint32_t revents, void *userdata) { + PTYForward *f = userdata; - if (kill(kill_pid, signo) < 0) - quit = true; - else { - log_info("Trying to halt container. Send SIGTERM again to trigger immediate termination."); + assert(f); + assert(e); + assert(e == f->stdout_event_source); + assert(fd >= 0); + assert(fd == STDOUT_FILENO); - /* This only works for systemd... */ - tried_orderly_shutdown = true; - } + if (revents & (EPOLLOUT|EPOLLHUP)) + f->stdout_writable = true; - } else - /* Signals that where - * delivered via signalfd that - * we didn't know are a reason - * for us to quit */ - quit = true; - } - } + return shovel(f); +} - if (stdin_hangup || stdout_hangup || master_hangup) { - /* Exit the loop if any side hung up and if - * there's nothing more to write or nothing we - * could write. */ +static int on_sigwinch_event(sd_event_source *e, const struct signalfd_siginfo *si, void *userdata) { + PTYForward *f = userdata; + struct winsize ws; - if ((out_buffer_full <= 0 || stdout_hangup) && - (in_buffer_full <= 0 || master_hangup)) - return !quit; - } - } + assert(f); + assert(e); + assert(e == f->sigwinch_event_source); + + /* The window size changed, let's forward that. */ + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0) + (void)ioctl(f->master, TIOCSWINSZ, &ws); + + return 0; } -int process_pty(int master, sigset_t *mask, pid_t kill_pid, int signo) { - struct termios saved_stdin_attr, raw_stdin_attr; - struct termios saved_stdout_attr, raw_stdout_attr; - bool saved_stdin = false; - bool saved_stdout = false; +int pty_forward_new(sd_event *event, int master, PTYForward **ret) { + _cleanup_(pty_forward_freep) PTYForward *f = NULL; struct winsize ws; int r; + f = new0(PTYForward, 1); + if (!f) + return -ENOMEM; + + if (event) + f->event = sd_event_ref(event); + else { + r = sd_event_default(&f->event); + if (r < 0) + return r; + } + + r = fd_nonblock(STDIN_FILENO, true); + if (r < 0) + return r; + + r = fd_nonblock(STDOUT_FILENO, true); + if (r < 0) + return r; + + r = fd_nonblock(master, true); + if (r < 0) + return r; + + f->master = master; + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0) - ioctl(master, TIOCSWINSZ, &ws); + (void)ioctl(master, TIOCSWINSZ, &ws); - if (tcgetattr(STDIN_FILENO, &saved_stdin_attr) >= 0) { - saved_stdin = true; + if (tcgetattr(STDIN_FILENO, &f->saved_stdin_attr) >= 0) { + struct termios raw_stdin_attr; - raw_stdin_attr = saved_stdin_attr; + f->saved_stdin = true; + + raw_stdin_attr = f->saved_stdin_attr; cfmakeraw(&raw_stdin_attr); - raw_stdin_attr.c_oflag = saved_stdin_attr.c_oflag; + raw_stdin_attr.c_oflag = f->saved_stdin_attr.c_oflag; tcsetattr(STDIN_FILENO, TCSANOW, &raw_stdin_attr); } - if (tcgetattr(STDOUT_FILENO, &saved_stdout_attr) >= 0) { - saved_stdout = true; - raw_stdout_attr = saved_stdout_attr; + if (tcgetattr(STDOUT_FILENO, &f->saved_stdout_attr) >= 0) { + struct termios raw_stdout_attr; + + f->saved_stdout = true; + + raw_stdout_attr = f->saved_stdout_attr; cfmakeraw(&raw_stdout_attr); - raw_stdout_attr.c_iflag = saved_stdout_attr.c_iflag; - raw_stdout_attr.c_lflag = saved_stdout_attr.c_lflag; + raw_stdout_attr.c_iflag = f->saved_stdout_attr.c_iflag; + raw_stdout_attr.c_lflag = f->saved_stdout_attr.c_lflag; tcsetattr(STDOUT_FILENO, TCSANOW, &raw_stdout_attr); } - r = process_pty_loop(master, mask, kill_pid, signo); + r = sd_event_add_io(f->event, &f->master_event_source, master, EPOLLIN|EPOLLOUT|EPOLLET, on_master_event, f); + if (r < 0) + return r; - if (saved_stdout) - tcsetattr(STDOUT_FILENO, TCSANOW, &saved_stdout_attr); - if (saved_stdin) - tcsetattr(STDIN_FILENO, TCSANOW, &saved_stdin_attr); + r = sd_event_add_io(f->event, &f->stdin_event_source, STDIN_FILENO, EPOLLIN|EPOLLET, on_stdin_event, f); + if (r < 0 && r != -EPERM) + return r; + + r = sd_event_add_io(f->event, &f->stdout_event_source, STDOUT_FILENO, EPOLLOUT|EPOLLET, on_stdout_event, f); + if (r == -EPERM) + /* stdout without epoll support. Likely redirected to regular file. */ + f->stdout_writable = true; + else if (r < 0) + return r; + + r = sd_event_add_signal(f->event, &f->sigwinch_event_source, SIGWINCH, on_sigwinch_event, f); + + *ret = f; + f = NULL; + + return 0; +} + +PTYForward *pty_forward_free(PTYForward *f) { + + if (f) { + sd_event_source_unref(f->stdin_event_source); + sd_event_source_unref(f->stdout_event_source); + sd_event_source_unref(f->master_event_source); + sd_event_unref(f->event); + + if (f->saved_stdout) + tcsetattr(STDOUT_FILENO, TCSANOW, &f->saved_stdout_attr); + if (f->saved_stdin) + tcsetattr(STDIN_FILENO, TCSANOW, &f->saved_stdin_attr); + + free(f); + } /* STDIN/STDOUT should not be nonblocking normally, so let's * unconditionally reset it */ fd_nonblock(STDIN_FILENO, false); fd_nonblock(STDOUT_FILENO, false); - return r; - + return NULL; } diff --git a/src/shared/ptyfwd.h b/src/shared/ptyfwd.h index 8b657023a9..5a612fd597 100644 --- a/src/shared/ptyfwd.h +++ b/src/shared/ptyfwd.h @@ -24,4 +24,11 @@ #include <sys/types.h> #include <signal.h> -int process_pty(int master, sigset_t *mask, pid_t kill_pid, int signo); +#include "sd-event.h" + +typedef struct PTYForward PTYForward; + +int pty_forward_new(sd_event *event, int master, PTYForward **f); +PTYForward *pty_forward_free(PTYForward *f); + +DEFINE_TRIVIAL_CLEANUP_FUNC(PTYForward*, pty_forward_free); diff --git a/src/shared/selinux-util.c b/src/shared/selinux-util.c index 1eddd17d27..a2233e0cfb 100644 --- a/src/shared/selinux-util.c +++ b/src/shared/selinux-util.c @@ -233,7 +233,7 @@ int mac_selinux_get_our_label(char **label) { return r; } -int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, char **label) { +int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label) { int r = -EOPNOTSUPP; #ifdef HAVE_SELINUX @@ -257,11 +257,7 @@ int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, char **label if (r < 0) return -errno; - r = getexeccon(&fcon); - if (r < 0) - return -errno; - - if (!fcon) { + if (!exec_label) { /* If there is no context set for next exec let's use context of target executable */ r = getfilecon(exe, &fcon); @@ -332,9 +328,13 @@ int mac_selinux_create_file_prepare(const char *path, mode_t mode) { r = selabel_lookup_raw(label_hnd, &filecon, newpath, mode); } - if (r < 0 && errno != ENOENT) + /* No context specified by the policy? Proceed without setting it. */ + if (r < 0 && errno == ENOENT) + return 0; + + if (r < 0) r = -errno; - else if (r == 0) { + else { r = setfscreatecon(filecon); if (r < 0) { log_enforcing("Failed to set SELinux security context %s for %s: %m", filecon, path); diff --git a/src/shared/selinux-util.h b/src/shared/selinux-util.h index 7ff8c607b4..a694441000 100644 --- a/src/shared/selinux-util.h +++ b/src/shared/selinux-util.h @@ -36,7 +36,7 @@ int mac_selinux_apply(const char *path, const char *label); int mac_selinux_get_create_label_from_exe(const char *exe, char **label); int mac_selinux_get_our_label(char **label); -int mac_selinux_get_child_mls_label(int socket_fd, const char *exec, char **label); +int mac_selinux_get_child_mls_label(int socket_fd, const char *exe, const char *exec_label, char **label); void mac_selinux_free(char *label); int mac_selinux_create_file_prepare(const char *path, mode_t mode); diff --git a/src/shared/set.c b/src/shared/set.c deleted file mode 100644 index 84ab82a701..0000000000 --- a/src/shared/set.c +++ /dev/null @@ -1,166 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2010 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 <stdlib.h> - -#include "set.h" -#include "hashmap.h" -#include "strv.h" - -#define MAKE_SET(h) ((Set*) (h)) -#define MAKE_HASHMAP(s) ((Hashmap*) (s)) - -/* For now this is not much more than a wrapper around a hashmap */ - -Set *set_new(const struct hash_ops *hash_ops) { - return MAKE_SET(hashmap_new(hash_ops)); -} - -void set_free(Set* s) { - hashmap_free(MAKE_HASHMAP(s)); -} - -void set_free_free(Set *s) { - hashmap_free_free(MAKE_HASHMAP(s)); -} - -int set_ensure_allocated(Set **s, const struct hash_ops *hash_ops) { - return hashmap_ensure_allocated((Hashmap**) s, hash_ops); -} - -int set_put(Set *s, void *value) { - return hashmap_put(MAKE_HASHMAP(s), value, value); -} - -int set_consume(Set *s, void *value) { - int r; - - r = set_put(s, value); - if (r < 0) - free(value); - - return r; -} - -int set_put_strdup(Set *s, const char *p) { - char *c; - int r; - - assert(s); - assert(p); - - c = strdup(p); - if (!c) - return -ENOMEM; - - r = set_consume(s, c); - if (r == -EEXIST) - return 0; - - return r; -} - -int set_put_strdupv(Set *s, char **l) { - int n = 0, r; - char **i; - - STRV_FOREACH(i, l) { - r = set_put_strdup(s, *i); - if (r < 0) - return r; - - n += r; - } - - return n; -} - -int set_replace(Set *s, void *value) { - return hashmap_replace(MAKE_HASHMAP(s), value, value); -} - -void *set_get(Set *s, void *value) { - return hashmap_get(MAKE_HASHMAP(s), value); -} - -bool set_contains(Set *s, void *value) { - return hashmap_contains(MAKE_HASHMAP(s), value); -} - -void *set_remove(Set *s, void *value) { - return hashmap_remove(MAKE_HASHMAP(s), value); -} - -int set_remove_and_put(Set *s, void *old_value, void *new_value) { - return hashmap_remove_and_put(MAKE_HASHMAP(s), old_value, new_value, new_value); -} - -unsigned set_size(Set *s) { - return hashmap_size(MAKE_HASHMAP(s)); -} - -bool set_isempty(Set *s) { - return hashmap_isempty(MAKE_HASHMAP(s)); -} - -void *set_iterate(Set *s, Iterator *i) { - return hashmap_iterate(MAKE_HASHMAP(s), i, NULL); -} - -void *set_steal_first(Set *s) { - return hashmap_steal_first(MAKE_HASHMAP(s)); -} - -void* set_first(Set *s) { - return hashmap_first(MAKE_HASHMAP(s)); -} - -int set_merge(Set *s, Set *other) { - return hashmap_merge(MAKE_HASHMAP(s), MAKE_HASHMAP(other)); -} - -int set_reserve(Set *s, unsigned entries_add) { - return hashmap_reserve(MAKE_HASHMAP(s), entries_add); -} - -int set_move(Set *s, Set *other) { - return hashmap_move(MAKE_HASHMAP(s), MAKE_HASHMAP(other)); -} - -int set_move_one(Set *s, Set *other, void *value) { - return hashmap_move_one(MAKE_HASHMAP(s), MAKE_HASHMAP(other), value); -} - -Set* set_copy(Set *s) { - return MAKE_SET(hashmap_copy(MAKE_HASHMAP(s))); -} - -void set_clear(Set *s) { - hashmap_clear(MAKE_HASHMAP(s)); -} - -void set_clear_free(Set *s) { - hashmap_clear_free(MAKE_HASHMAP(s)); -} - -char **set_get_strv(Set *s) { - return hashmap_get_strv(MAKE_HASHMAP(s)); -} diff --git a/src/shared/set.h b/src/shared/set.h index d2622d17ea..4605ecd2c1 100644 --- a/src/shared/set.h +++ b/src/shared/set.h @@ -21,56 +21,114 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -/* Pretty straightforward set implementation. Internally based on the - * hashmap. That means that as a minor optimization a NULL set - * object will be treated as empty set for all read - * operations. That way it is not necessary to instantiate an object - * for each set use. */ - #include "hashmap.h" #include "util.h" -typedef struct Set Set; +Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS) -Set *set_new(const struct hash_ops *hash_ops); -void set_free(Set* s); -void set_free_free(Set *s); -Set* set_copy(Set *s); -int set_ensure_allocated(Set **s, const struct hash_ops *hash_ops); +static inline void set_free(Set *s) { + internal_hashmap_free(HASHMAP_BASE(s)); +} -int set_put(Set *s, void *value); -int set_consume(Set *s, void *value); -int set_put_strdup(Set *s, const char *p); -int set_put_strdupv(Set *s, char **l); -int set_replace(Set *s, void *value); -void *set_get(Set *s, void *value); -bool set_contains(Set *s, void *value); -void *set_remove(Set *s, void *value); -int set_remove_and_put(Set *s, void *old_value, void *new_value); +static inline void set_free_free(Set *s) { + internal_hashmap_free_free(HASHMAP_BASE(s)); +} + +/* no set_free_free_free */ + +static inline Set *set_copy(Set *s) { + return (Set*) internal_hashmap_copy(HASHMAP_BASE(s)); +} + +int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) +int set_put(Set *s, const void *key); +/* no set_update */ +/* no set_replace */ +static inline void *set_get(Set *s, void *key) { + return internal_hashmap_get(HASHMAP_BASE(s), key); +} +/* no set_get2 */ + +static inline bool set_contains(Set *s, const void *key) { + return internal_hashmap_contains(HASHMAP_BASE(s), key); +} + +static inline void *set_remove(Set *s, void *key) { + return internal_hashmap_remove(HASHMAP_BASE(s), key); +} + +/* no set_remove2 */ +/* no set_remove_value */ +int set_remove_and_put(Set *s, const void *old_key, const void *new_key); +/* no set_remove_and_replace */ int set_merge(Set *s, Set *other); -int set_reserve(Set *s, unsigned entries_add); -int set_move(Set *s, Set *other); -int set_move_one(Set *s, Set *other, void *value); -unsigned set_size(Set *s); -bool set_isempty(Set *s); +static inline int set_reserve(Set *h, unsigned entries_add) { + return internal_hashmap_reserve(HASHMAP_BASE(h), entries_add); +} + +static inline int set_move(Set *s, Set *other) { + return internal_hashmap_move(HASHMAP_BASE(s), HASHMAP_BASE(other)); +} + +static inline int set_move_one(Set *s, Set *other, const void *key) { + return internal_hashmap_move_one(HASHMAP_BASE(s), HASHMAP_BASE(other), key); +} + +static inline unsigned set_size(Set *s) { + return internal_hashmap_size(HASHMAP_BASE(s)); +} + +static inline bool set_isempty(Set *s) { + return set_size(s) == 0; +} + +static inline unsigned set_buckets(Set *s) { + return internal_hashmap_buckets(HASHMAP_BASE(s)); +} void *set_iterate(Set *s, Iterator *i); -void set_clear(Set *s); -void set_clear_free(Set *s); +static inline void set_clear(Set *s) { + internal_hashmap_clear(HASHMAP_BASE(s)); +} + +static inline void set_clear_free(Set *s) { + internal_hashmap_clear_free(HASHMAP_BASE(s)); +} + +/* no set_clear_free_free */ + +static inline void *set_steal_first(Set *s) { + return internal_hashmap_steal_first(HASHMAP_BASE(s)); +} + +/* no set_steal_first_key */ +/* no set_first_key */ -void *set_steal_first(Set *s); -void* set_first(Set *s); +static inline void *set_first(Set *s) { + return internal_hashmap_first(HASHMAP_BASE(s)); +} -char **set_get_strv(Set *s); +/* no set_next */ + +static inline char **set_get_strv(Set *s) { + return internal_hashmap_get_strv(HASHMAP_BASE(s)); +} + +int set_consume(Set *s, void *value); +int set_put_strdup(Set *s, const char *p); +int set_put_strdupv(Set *s, char **l); #define SET_FOREACH(e, s, i) \ for ((i) = ITERATOR_FIRST, (e) = set_iterate((s), &(i)); (e); (e) = set_iterate((s), &(i))) DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free); DEFINE_TRIVIAL_CLEANUP_FUNC(Set*, set_free_free); + #define _cleanup_set_free_ _cleanup_(set_freep) #define _cleanup_set_free_free_ _cleanup_(set_free_freep) diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c index d8de644a92..ae14c6bd4d 100644 --- a/src/shared/sleep-config.c +++ b/src/shared/sleep-config.c @@ -48,9 +48,10 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states) { {} }; - config_parse(NULL, PKGSYSCONFDIR "/sleep.conf", NULL, - "Sleep\0", - config_item_table_lookup, items, false, false, true, NULL); + config_parse_many(PKGSYSCONFDIR "/sleep.conf", + CONF_DIRS_NULSTR("systemd/sleep.conf"), + "Sleep\0", config_item_table_lookup, items, + false, NULL); if (streq(verb, "suspend")) { /* empty by default */ @@ -227,14 +228,14 @@ static bool enough_memory_for_hibernation(void) { r = get_status_field("/proc/meminfo", "\nActive(anon):", &active); if (r < 0) { - log_error("Failed to retrieve Active(anon) from /proc/meminfo: %s", strerror(-r)); + log_error_errno(r, "Failed to retrieve Active(anon) from /proc/meminfo: %m"); return false; } r = safe_atollu(active, &act); if (r < 0) { - log_error("Failed to parse Active(anon) from /proc/meminfo: %s: %s", - active, strerror(-r)); + log_error_errno(r, "Failed to parse Active(anon) from /proc/meminfo: %s: %m", + active); return false; } diff --git a/src/shared/smack-util.c b/src/shared/smack-util.c index a8dccd1554..64e213489e 100644 --- a/src/shared/smack-util.c +++ b/src/shared/smack-util.c @@ -25,6 +25,7 @@ #include "util.h" #include "path-util.h" +#include "fileio.h" #include "smack-util.h" #define SMACK_FLOOR_LABEL "_" @@ -123,14 +124,38 @@ int mac_smack_apply_ip_in_fd(int fd, const char *label) { return r; } -int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) { +int mac_smack_apply_pid(pid_t pid, const char *label) { + +#ifdef HAVE_SMACK + const char *p; +#endif int r = 0; + assert(label); + +#ifdef HAVE_SMACK + if (!mac_smack_use()) + return 0; + + p = procfs_file_alloca(pid, "attr/current"); + r = write_string_file(p, label); + if (r < 0) + return r; +#endif + + return r; +} + +int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) { + #ifdef HAVE_SMACK struct stat st; +#endif + int r = 0; assert(path); +#ifdef HAVE_SMACK if (!mac_smack_use()) return 0; @@ -174,8 +199,7 @@ int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) { if (ignore_erofs && errno == EROFS) return 0; - log_debug("Unable to fix SMACK label of %s: %m", path); - r = -errno; + r = log_debug_errno(errno, "Unable to fix SMACK label of %s: %m", path); } #endif diff --git a/src/shared/smack-util.h b/src/shared/smack-util.h index 68778da38b..50f55b1f4b 100644 --- a/src/shared/smack-util.h +++ b/src/shared/smack-util.h @@ -31,5 +31,6 @@ int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs); int mac_smack_apply(const char *path, const char *label); int mac_smack_apply_fd(int fd, const char *label); +int mac_smack_apply_pid(pid_t pid, const char *label); int mac_smack_apply_ip_in_fd(int fd, const char *label); int mac_smack_apply_ip_out_fd(int fd, const char *label); diff --git a/src/shared/socket-label.c b/src/shared/socket-label.c index 47d9488d56..b1ef19f265 100644 --- a/src/shared/socket-label.c +++ b/src/shared/socket-label.c @@ -93,13 +93,13 @@ int socket_address_listen( if (free_bind) { one = 1; if (setsockopt(fd, IPPROTO_IP, IP_FREEBIND, &one, sizeof(one)) < 0) - log_warning("IP_FREEBIND failed: %m"); + log_warning_errno(errno, "IP_FREEBIND failed: %m"); } if (transparent) { one = 1; if (setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &one, sizeof(one)) < 0) - log_warning("IP_TRANSPARENT failed: %m"); + log_warning_errno(errno, "IP_TRANSPARENT failed: %m"); } } @@ -161,13 +161,11 @@ int make_socket_fd(int log_level, const char* address, int flags) { _cleanup_free_ char *p = NULL; r = socket_address_print(&a, &p); - if (r < 0) { - log_error("socket_address_print(): %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "socket_address_print(): %m"); if (fd < 0) - log_error("Failed to listen on %s: %s", p, strerror(-fd)); + log_error_errno(fd, "Failed to listen on %s: %m", p); else log_full(log_level, "Listening on %s", p); } diff --git a/src/shared/socket-util.c b/src/shared/socket-util.c index 911dbfe55a..c6f64876be 100644 --- a/src/shared/socket-util.c +++ b/src/shared/socket-util.c @@ -636,12 +636,10 @@ int socknameinfo_pretty(union sockaddr_union *sa, socklen_t salen, char **_ret) int saved_errno = errno; r = sockaddr_pretty(&sa->sa, salen, true, &ret); - if (r < 0) { - log_error("sockadd_pretty() failed: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "sockadd_pretty() failed: %m"); - log_debug("getnameinfo(%s) failed: %s", ret, strerror(saved_errno)); + log_debug_errno(saved_errno, "getnameinfo(%s) failed: %m", ret); } else { ret = strdup(host); if (!ret) @@ -659,10 +657,8 @@ int getnameinfo_pretty(int fd, char **ret) { assert(fd >= 0); assert(ret); - if (getsockname(fd, &sa.sa, &salen) < 0) { - log_error("getsockname(%d) failed: %m", fd); - return -errno; - } + if (getsockname(fd, &sa.sa, &salen) < 0) + return log_error_errno(errno, "getsockname(%d) failed: %m", fd); return socknameinfo_pretty(&sa, salen, ret); } diff --git a/src/shared/spawn-ask-password-agent.c b/src/shared/spawn-ask-password-agent.c index c1a9c58681..ee267833e6 100644 --- a/src/shared/spawn-ask-password-agent.c +++ b/src/shared/spawn-ask-password-agent.c @@ -49,7 +49,7 @@ int ask_password_agent_open(void) { SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, "--watch", NULL); if (r < 0) - log_error("Failed to fork TTY ask password agent: %s", strerror(-r)); + log_error_errno(r, "Failed to fork TTY ask password agent: %m"); return r; } @@ -62,6 +62,6 @@ void ask_password_agent_close(void) { /* Inform agent that we are done */ kill(agent_pid, SIGTERM); kill(agent_pid, SIGCONT); - wait_for_terminate(agent_pid, NULL); + (void) wait_for_terminate(agent_pid, NULL); agent_pid = 0; } diff --git a/src/shared/spawn-polkit-agent.c b/src/shared/spawn-polkit-agent.c index 29b01db19a..e7419b5ee4 100644 --- a/src/shared/spawn-polkit-agent.c +++ b/src/shared/spawn-polkit-agent.c @@ -64,7 +64,7 @@ int polkit_agent_open(void) { safe_close(pipe_fd[1]); if (r < 0) - log_error("Failed to fork TTY ask password agent: %s", strerror(-r)); + log_error_errno(r, "Failed to fork TTY ask password agent: %m"); else /* Wait until the agent closes the fd */ fd_wait_for_event(pipe_fd[0], POLLHUP, USEC_INFINITY); @@ -82,7 +82,7 @@ void polkit_agent_close(void) { /* Inform agent that we are done */ kill(agent_pid, SIGTERM); kill(agent_pid, SIGCONT); - wait_for_terminate(agent_pid, NULL); + (void) wait_for_terminate(agent_pid, NULL); agent_pid = 0; } diff --git a/src/shared/strv.c b/src/shared/strv.c index 00857e40a7..fdb658c0a3 100644 --- a/src/shared/strv.c +++ b/src/shared/strv.c @@ -248,40 +248,6 @@ char **strv_split(const char *s, const char *separator) { return r; } -int strv_split_quoted(char ***t, const char *s) { - const char *word, *state; - size_t l; - unsigned n, i; - char **r; - - assert(s); - - n = 0; - FOREACH_WORD_QUOTED(word, l, s, state) - n++; - if (!isempty(state)) - /* bad syntax */ - return -EINVAL; - - r = new(char*, n+1); - if (!r) - return -ENOMEM; - - i = 0; - FOREACH_WORD_QUOTED(word, l, s, state) { - r[i] = cunescape_length(word, l); - if (!r[i]) { - strv_free(r); - return -ENOMEM; - } - i++; - } - - r[i] = NULL; - *t = r; - return 0; -} - char **strv_split_newlines(const char *s) { char **l; unsigned n; @@ -307,6 +273,41 @@ char **strv_split_newlines(const char *s) { return l; } +int strv_split_quoted(char ***t, const char *s, bool relax) { + size_t n = 0, allocated = 0; + _cleanup_strv_free_ char **l = NULL; + int r; + + assert(t); + assert(s); + + for (;;) { + _cleanup_free_ char *word = NULL; + + r = unquote_first_word(&s, &word, relax); + if (r < 0) + return r; + if (r == 0) + break; + + if (!GREEDY_REALLOC(l, allocated, n + 2)) + return -ENOMEM; + + l[n++] = word; + word = NULL; + + l[n] = NULL; + } + + if (!l) + l = new0(char*, 1); + + *t = l; + l = NULL; + + return 0; +} + char *strv_join(char **l, const char *separator) { char *r, *e; char **s; @@ -387,7 +388,7 @@ int strv_push(char ***l, char *value) { n = strv_length(*l); - /* increase and check for overflow */ + /* Increase and check for overflow */ m = n + 2; if (m < n) return -ENOMEM; @@ -403,6 +404,34 @@ int strv_push(char ***l, char *value) { return 0; } +int strv_push_pair(char ***l, char *a, char *b) { + char **c; + unsigned n, m; + + if (!a && !b) + return 0; + + n = strv_length(*l); + + /* increase and check for overflow */ + m = n + !!a + !!b + 1; + if (m < n) + return -ENOMEM; + + c = realloc_multiply(*l, sizeof(char*), m); + if (!c) + return -ENOMEM; + + if (a) + c[n++] = a; + if (b) + c[n++] = b; + c[n] = NULL; + + *l = c; + return 0; +} + int strv_push_prepend(char ***l, char *value) { char **c; unsigned n, m, i; @@ -443,6 +472,18 @@ int strv_consume(char ***l, char *value) { return r; } +int strv_consume_pair(char ***l, char *a, char *b) { + int r; + + r = strv_push_pair(l, a, b); + if (r < 0) { + free(a); + free(b); + } + + return r; +} + int strv_consume_prepend(char ***l, char *value) { int r; @@ -586,6 +627,17 @@ char **strv_sort(char **l) { return l; } +bool strv_equal(char **a, char **b) { + if (!a || !b) + return a == b; + + for ( ; *a || *b; ++a, ++b) + if (!streq_ptr(*a, *b)) + return false; + + return true; +} + void strv_print(char **l) { char **s; diff --git a/src/shared/strv.h b/src/shared/strv.h index 9c9633c515..2c0280b713 100644 --- a/src/shared/strv.h +++ b/src/shared/strv.h @@ -42,13 +42,17 @@ int strv_extend_strv_concat(char ***a, char **b, const char *suffix); int strv_extend(char ***l, const char *value); int strv_extendf(char ***l, const char *format, ...) _printf_(2,0); int strv_push(char ***l, char *value); +int strv_push_pair(char ***l, char *a, char *b); int strv_push_prepend(char ***l, char *value); int strv_consume(char ***l, char *value); +int strv_consume_pair(char ***l, char *a, char *b); int strv_consume_prepend(char ***l, char *value); char **strv_remove(char **l, const char *s); char **strv_uniq(char **l); +bool strv_equal(char **a, char **b); + #define strv_contains(l, s) (!!strv_find((l), (s))) char **strv_new(const char *x, ...) _sentinel_; @@ -63,9 +67,10 @@ static inline bool strv_isempty(char * const *l) { } char **strv_split(const char *s, const char *separator); -int strv_split_quoted(char ***t, const char *s); char **strv_split_newlines(const char *s); +int strv_split_quoted(char ***t, const char *s, bool relax); + char *strv_join(char **l, const char *separator); char *strv_join_quoted(char **l); diff --git a/src/shared/switch-root.c b/src/shared/switch-root.c index bac0e5c349..ca3875628a 100644 --- a/src/shared/switch-root.c +++ b/src/shared/switch-root.c @@ -47,7 +47,6 @@ int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot, struct stat new_root_stat; bool old_root_remove; const char *i, *temporary_old_root; - int r; if (path_equal(new_root, "/")) return 0; @@ -57,10 +56,8 @@ int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot, old_root_remove = in_initrd(); - if (stat(new_root, &new_root_stat) < 0) { - log_error("Failed to stat directory %s: %m", new_root); - return -errno; - } + if (stat(new_root, &new_root_stat) < 0) + return log_error_errno(errno, "Failed to stat directory %s: %m", new_root); /* Work-around for kernel design: the kernel refuses switching * root if any file systems are mounted MS_SHARED. Hence @@ -68,7 +65,7 @@ int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot, * * https://bugzilla.redhat.com/show_bug.cgi?id=847418 */ if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0) - log_warning("Failed to make \"/\" private mount: %m"); + log_warning_errno(errno, "Failed to make \"/\" private mount: %m"); NULSTR_FOREACH(i, move_mounts) { char new_mount[PATH_MAX]; @@ -86,38 +83,37 @@ int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot, * stat failed. Unmount the old mount * point. */ if (umount2(i, MNT_DETACH) < 0) - log_warning("Failed to unmount %s: %m", i); + log_warning_errno(errno, "Failed to unmount %s: %m", i); continue; } if (mount(i, new_mount, NULL, mountflags, NULL) < 0) { if (mountflags & MS_MOVE) { - log_error("Failed to move mount %s to %s, forcing unmount: %m", i, new_mount); + log_error_errno(errno, "Failed to move mount %s to %s, forcing unmount: %m", i, new_mount); if (umount2(i, MNT_FORCE) < 0) - log_warning("Failed to unmount %s: %m", i); + log_warning_errno(errno, "Failed to unmount %s: %m", i); } if (mountflags & MS_BIND) - log_error("Failed to bind mount %s to %s: %m", i, new_mount); + log_error_errno(errno, "Failed to bind mount %s to %s: %m", i, new_mount); } } - r = base_filesystem_create(new_root); - if (r < 0) { - log_error("Failed to create the base filesystem: %s", strerror(-r)); - return r; - } + /* Do not fail, if base_filesystem_create() fails. Not all + * switch roots are like base_filesystem_create() wants them + * to look like. They might even boot, if they are RO and + * don't have the FS layout. Just ignore the error and + * switch_root() nevertheless. */ + (void) base_filesystem_create(new_root); - if (chdir(new_root) < 0) { - log_error("Failed to change directory to %s: %m", new_root); - return -errno; - } + if (chdir(new_root) < 0) + return log_error_errno(errno, "Failed to change directory to %s: %m", new_root); if (old_root_remove) { old_root_fd = open("/", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY|O_DIRECTORY); if (old_root_fd < 0) - log_warning("Failed to open root directory: %m"); + log_warning_errno(errno, "Failed to open root directory: %m"); } /* We first try a pivot_root() so that we can umount the old @@ -128,30 +124,24 @@ int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot, /* Immediately get rid of the old root, if detach_oldroot is set. * Since we are running off it we need to do this lazily. */ if (detach_oldroot && umount2(oldroot, MNT_DETACH) < 0) - log_error("Failed to lazily umount old root dir %s, %s: %m", + log_error_errno(errno, "Failed to lazily umount old root dir %s, %s: %m", oldroot, errno == ENOENT ? "ignoring" : "leaving it around"); - } else if (mount(new_root, "/", NULL, MS_MOVE, NULL) < 0) { - log_error("Failed to mount moving %s to /: %m", new_root); - return -errno; - } + } else if (mount(new_root, "/", NULL, MS_MOVE, NULL) < 0) + return log_error_errno(errno, "Failed to mount moving %s to /: %m", new_root); - if (chroot(".") < 0) { - log_error("Failed to change root: %m"); - return -errno; - } + if (chroot(".") < 0) + return log_error_errno(errno, "Failed to change root: %m"); - if (chdir("/") < 0) { - log_error("Failed to change directory: %m"); - return -errno; - } + if (chdir("/") < 0) + return log_error_errno(errno, "Failed to change directory: %m"); if (old_root_fd >= 0) { struct stat rb; if (fstat(old_root_fd, &rb) < 0) - log_warning("Failed to stat old root directory, leaving: %m"); + log_warning_errno(errno, "Failed to stat old root directory, leaving: %m"); else { rm_rf_children(old_root_fd, false, false, &rb); old_root_fd = -1; diff --git a/src/shared/time-dst.c b/src/shared/time-dst.c index 6195b11017..926d22b94b 100644 --- a/src/shared/time-dst.c +++ b/src/shared/time-dst.c @@ -244,6 +244,8 @@ read_again: if (fread(zone_names, 1, chars, f) != chars) return -EINVAL; + zone_names[chars] = '\0'; + for (i = 0; i < num_isstd; ++i) { int c = getc(f); if (c == EOF) diff --git a/src/shared/udev-util.h b/src/shared/udev-util.h index 5f09ce181f..5e0e1a96ef 100644 --- a/src/shared/udev-util.h +++ b/src/shared/udev-util.h @@ -27,6 +27,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev*, udev_unref); DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_device*, udev_device_unref); DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_enumerate*, udev_enumerate_unref); +DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_hwdb*, udev_hwdb_unref); DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_event*, udev_event_unref); DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_rules*, udev_rules_unref); DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl*, udev_ctrl_unref); @@ -35,6 +36,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_monitor*, udev_monitor_unref); #define _cleanup_udev_unref_ _cleanup_(udev_unrefp) #define _cleanup_udev_device_unref_ _cleanup_(udev_device_unrefp) #define _cleanup_udev_enumerate_unref_ _cleanup_(udev_enumerate_unrefp) +#define _cleanup_udev_hwdb_unref_ _cleanup_(udev_hwdb_unrefp) #define _cleanup_udev_event_unref_ _cleanup_(udev_event_unrefp) #define _cleanup_udev_rules_unref_ _cleanup_(udev_rules_unrefp) #define _cleanup_udev_ctrl_unref_ _cleanup_(udev_ctrl_unrefp) diff --git a/src/shared/uid-range.c b/src/shared/uid-range.c index 74c3be4a13..4794ff45bb 100644 --- a/src/shared/uid-range.c +++ b/src/shared/uid-range.c @@ -161,7 +161,7 @@ int uid_range_add_str(UidRange **p, unsigned *n, const char *s) { } int uid_range_next_lower(const UidRange *p, unsigned n, uid_t *uid) { - uid_t closest = (uid_t) -1, candidate; + uid_t closest = UID_INVALID, candidate; unsigned i; assert(p); @@ -184,7 +184,7 @@ int uid_range_next_lower(const UidRange *p, unsigned n, uid_t *uid) { closest = end; } - if (closest == (uid_t) -1) + if (closest == UID_INVALID) return -EBUSY; *uid = closest; diff --git a/src/shared/unaligned.h b/src/shared/unaligned.h new file mode 100644 index 0000000000..d6181dd9a9 --- /dev/null +++ b/src/shared/unaligned.h @@ -0,0 +1,66 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Tom Gundersen + + 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 <stdint.h> + +static inline uint16_t unaligned_read_be16(const void *_u) { + const uint8_t *u = _u; + + return (((uint16_t) u[0]) << 8) | + ((uint16_t) u[1]); +} + +static inline uint32_t unaligned_read_be32(const void *_u) { + const uint8_t *u = _u; + + return (((uint32_t) unaligned_read_be16(u)) << 16) | + ((uint32_t) unaligned_read_be16(u + 2)); +} + +static inline uint64_t unaligned_read_be64(const void *_u) { + const uint8_t *u = _u; + + return (((uint64_t) unaligned_read_be32(u)) << 32) | + ((uint64_t) unaligned_read_be32(u + 4)); +} + +static inline void unaligned_write_be16(void *_u, uint16_t a) { + uint8_t *u = _u; + + u[0] = (uint8_t) (a >> 8); + u[1] = (uint8_t) a; +} + +static inline void unaligned_write_be32(void *_u, uint32_t a) { + uint8_t *u = _u; + + unaligned_write_be16(u, (uint16_t) (a >> 16)); + unaligned_write_be16(u + 2, (uint16_t) a); +} + +static inline void unaligned_write_be64(void *_u, uint64_t a) { + uint8_t *u = _u; + + unaligned_write_be32(u, (uint32_t) (a >> 32)); + unaligned_write_be32(u + 4, (uint32_t) a); +} diff --git a/src/shared/unit-name.c b/src/shared/unit-name.c index 2ef85450e7..21b66913c9 100644 --- a/src/shared/unit-name.c +++ b/src/shared/unit-name.c @@ -243,6 +243,30 @@ static char *do_escape(const char *f, char *t) { return t; } +static char *do_escape_mangle(const char *f, enum unit_name_mangle allow_globs, char *t) { + const char *valid_chars; + + assert(f); + assert(IN_SET(allow_globs, MANGLE_GLOB, MANGLE_NOGLOB)); + assert(t); + + /* We'll only escape the obvious characters here, to play + * safe. */ + + valid_chars = allow_globs == MANGLE_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS; + + for (; *f; f++) { + if (*f == '/') + *(t++) = '-'; + else if (!strchr(valid_chars, *f)) + t = do_escape_char(*f, t); + else + *(t++) = *f; + } + + return t; +} + char *unit_name_escape(const char *f) { char *r, *t; @@ -478,15 +502,18 @@ int unit_name_from_dbus_path(const char *path, char **name) { } /** - * Try to turn a string that might not be a unit name into a - * sensible unit name. + * Convert a string to a unit name. /dev/blah is converted to dev-blah.device, + * /blah/blah is converted to blah-blah.mount, anything else is left alone, + * except that @suffix is appended if a valid unit suffix is not present. + * + * If @allow_globs, globs characters are preserved. Otherwise they are escaped. */ -char *unit_name_mangle(const char *name, enum unit_name_mangle allow_globs) { - const char *valid_chars, *f; +char *unit_name_mangle_with_suffix(const char *name, enum unit_name_mangle allow_globs, const char *suffix) { char *r, *t; assert(name); - assert(IN_SET(allow_globs, MANGLE_GLOB, MANGLE_NOGLOB)); + assert(suffix); + assert(suffix[0] == '.'); if (is_device_path(name)) return unit_name_from_path(name, ".device"); @@ -494,59 +521,13 @@ char *unit_name_mangle(const char *name, enum unit_name_mangle allow_globs) { if (path_is_absolute(name)) return unit_name_from_path(name, ".mount"); - /* We'll only escape the obvious characters here, to play - * safe. */ - - valid_chars = allow_globs == MANGLE_GLOB ? "@" VALID_CHARS "[]!-*?" : "@" VALID_CHARS; - - r = new(char, strlen(name) * 4 + strlen(".service") + 1); - if (!r) - return NULL; - - for (f = name, t = r; *f; f++) { - if (*f == '/') - *(t++) = '-'; - else if (!strchr(valid_chars, *f)) - t = do_escape_char(*f, t); - else - *(t++) = *f; - } - - if (unit_name_to_type(name) < 0) - strcpy(t, ".service"); - else - *t = 0; - - return r; -} - -/** - * Similar to unit_name_mangle(), but is called when we know - * that this is about a specific unit type. - */ -char *unit_name_mangle_with_suffix(const char *name, enum unit_name_mangle allow_globs, const char *suffix) { - char *r, *t; - const char *f; - - assert(name); - assert(IN_SET(allow_globs, MANGLE_GLOB, MANGLE_NOGLOB)); - assert(suffix); - assert(suffix[0] == '.'); - r = new(char, strlen(name) * 4 + strlen(suffix) + 1); if (!r) return NULL; - for (f = name, t = r; *f; f++) { - if (*f == '/') - *(t++) = '-'; - else if (!strchr(VALID_CHARS, *f)) - t = do_escape_char(*f, t); - else - *(t++) = *f; - } + t = do_escape_mangle(name, allow_globs, r); - if (!endswith(name, suffix)) + if (unit_name_to_type(name) < 0) strcpy(t, suffix); else *t = 0; diff --git a/src/shared/unit-name.h b/src/shared/unit-name.h index daeb56a860..6f139cc4c4 100644 --- a/src/shared/unit-name.h +++ b/src/shared/unit-name.h @@ -156,8 +156,10 @@ enum unit_name_mangle { MANGLE_GLOB, }; -char *unit_name_mangle(const char *name, enum unit_name_mangle allow_globs); char *unit_name_mangle_with_suffix(const char *name, enum unit_name_mangle allow_globs, const char *suffix); +static inline char *unit_name_mangle(const char *name, enum unit_name_mangle allow_globs) { + return unit_name_mangle_with_suffix(name, allow_globs, ".service"); +} int build_subslice(const char *slice, const char*name, char **subslice); diff --git a/src/shared/utf8.c b/src/shared/utf8.c index 9353559b76..4469a73751 100644 --- a/src/shared/utf8.c +++ b/src/shared/utf8.c @@ -142,18 +142,20 @@ int utf8_encoded_to_unichar(const char *str) { } bool utf8_is_printable_newline(const char* str, size_t length, bool newline) { - const uint8_t *p; + const char *p; assert(str); - for (p = (const uint8_t*) str; length;) { + for (p = str; length;) { int encoded_len, val; - encoded_len = utf8_encoded_valid_unichar((const char *) p); - val = utf8_encoded_to_unichar((const char*) p); - + encoded_len = utf8_encoded_valid_unichar(p); if (encoded_len < 0 || - val < 0 || + (size_t) encoded_len > length) + return false; + + val = utf8_encoded_to_unichar(p); + if (val < 0 || is_unicode_control(val) || (!newline && val == '\n')) return false; @@ -200,7 +202,46 @@ char *utf8_escape_invalid(const char *str) { s = mempcpy(s, str, len); str += len; } else { - s = mempcpy(s, UTF8_REPLACEMENT_CHARACTER, strlen(UTF8_REPLACEMENT_CHARACTER)); + s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER); + str += 1; + } + } + + *s = '\0'; + + return p; +} + +char *utf8_escape_non_printable(const char *str) { + char *p, *s; + + assert(str); + + p = s = malloc(strlen(str) * 4 + 1); + if (!p) + return NULL; + + while (*str) { + int len; + + len = utf8_encoded_valid_unichar(str); + if (len > 0) { + if (utf8_is_printable(str, len)) { + s = mempcpy(s, str, len); + str += len; + } else { + while (len > 0) { + *(s++) = '\\'; + *(s++) = 'x'; + *(s++) = hexchar((int) *str >> 4); + *(s++) = hexchar((int) *str); + + str += 1; + len --; + } + } + } else { + s = stpcpy(s, UTF8_REPLACEMENT_CHARACTER); str += 1; } } diff --git a/src/shared/utf8.h b/src/shared/utf8.h index c087995930..59abee50ac 100644 --- a/src/shared/utf8.h +++ b/src/shared/utf8.h @@ -29,12 +29,12 @@ const char *utf8_is_valid(const char *s) _pure_; char *ascii_is_valid(const char *s) _pure_; -char *utf8_escape_invalid(const char *s); bool utf8_is_printable_newline(const char* str, size_t length, bool newline) _pure_; -_pure_ static inline bool utf8_is_printable(const char* str, size_t length) { - return utf8_is_printable_newline(str, length, true); -} +#define utf8_is_printable(str, length) utf8_is_printable_newline(str, length, true) + +char *utf8_escape_invalid(const char *s); +char *utf8_escape_non_printable(const char *str); char *utf16_to_utf8(const void *s, size_t length); diff --git a/src/shared/util.c b/src/shared/util.c index 4143f6d643..26a4f72b43 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -174,6 +174,69 @@ char* first_word(const char *s, const char *word) { return (char*) p; } +static size_t cescape_char(char c, char *buf) { + char * buf_old = buf; + + switch (c) { + + case '\a': + *(buf++) = '\\'; + *(buf++) = 'a'; + break; + case '\b': + *(buf++) = '\\'; + *(buf++) = 'b'; + break; + case '\f': + *(buf++) = '\\'; + *(buf++) = 'f'; + break; + case '\n': + *(buf++) = '\\'; + *(buf++) = 'n'; + break; + case '\r': + *(buf++) = '\\'; + *(buf++) = 'r'; + break; + case '\t': + *(buf++) = '\\'; + *(buf++) = 't'; + break; + case '\v': + *(buf++) = '\\'; + *(buf++) = 'v'; + break; + case '\\': + *(buf++) = '\\'; + *(buf++) = '\\'; + break; + case '"': + *(buf++) = '\\'; + *(buf++) = '"'; + break; + case '\'': + *(buf++) = '\\'; + *(buf++) = '\''; + break; + + default: + /* For special chars we prefer octal over + * hexadecimal encoding, simply because glib's + * g_strescape() does the same */ + if ((c < ' ') || (c >= 127)) { + *(buf++) = '\\'; + *(buf++) = octchar((unsigned char) c >> 6); + *(buf++) = octchar((unsigned char) c >> 3); + *(buf++) = octchar((unsigned char) c); + } else + *(buf++) = c; + break; + } + + return buf - buf_old; +} + int close_nointr(int fd) { assert(fd >= 0); @@ -291,7 +354,7 @@ int parse_uid(const char *s, uid_t* ret_uid) { if ((unsigned long) uid != ul) return -ERANGE; - /* Some libc APIs use (uid_t) -1 as special placeholder */ + /* Some libc APIs use UID_INVALID as special placeholder */ if (uid == (uid_t) 0xFFFFFFFF) return -ENXIO; @@ -363,6 +426,46 @@ int safe_atou8(const char *s, uint8_t *ret) { return 0; } +int safe_atou16(const char *s, uint16_t *ret) { + char *x = NULL; + unsigned long l; + + assert(s); + assert(ret); + + errno = 0; + l = strtoul(s, &x, 0); + + if (!x || x == s || *x || errno) + return errno > 0 ? -errno : -EINVAL; + + if ((unsigned long) (uint16_t) l != l) + return -ERANGE; + + *ret = (uint16_t) l; + return 0; +} + +int safe_atoi16(const char *s, int16_t *ret) { + char *x = NULL; + long l; + + assert(s); + assert(ret); + + errno = 0; + l = strtol(s, &x, 0); + + if (!x || x == s || *x || errno) + return errno > 0 ? -errno : -EINVAL; + + if ((long) (int16_t) l != l) + return -ERANGE; + + *ret = (int16_t) l; + return 0; +} + int safe_atollu(const char *s, long long unsigned *ret_llu) { char *x = NULL; unsigned long long l; @@ -515,56 +618,6 @@ int get_parent_of_pid(pid_t pid, pid_t *_ppid) { return 0; } -int get_starttime_of_pid(pid_t pid, unsigned long long *st) { - int r; - _cleanup_free_ char *line = NULL; - const char *p; - - assert(pid >= 0); - assert(st); - - p = procfs_file_alloca(pid, "stat"); - r = read_one_line_file(p, &line); - if (r < 0) - return r; - - /* Let's skip the pid and comm fields. The latter is enclosed - * in () but does not escape any () in its value, so let's - * skip over it manually */ - - p = strrchr(line, ')'); - if (!p) - return -EIO; - - p++; - - if (sscanf(p, " " - "%*c " /* state */ - "%*d " /* ppid */ - "%*d " /* pgrp */ - "%*d " /* session */ - "%*d " /* tty_nr */ - "%*d " /* tpgid */ - "%*u " /* flags */ - "%*u " /* minflt */ - "%*u " /* cminflt */ - "%*u " /* majflt */ - "%*u " /* cmajflt */ - "%*u " /* utime */ - "%*u " /* stime */ - "%*d " /* cutime */ - "%*d " /* cstime */ - "%*d " /* priority */ - "%*d " /* nice */ - "%*d " /* num_threads */ - "%*d " /* itrealvalue */ - "%llu " /* starttime */, - st) != 1) - return -EIO; - - return 0; -} - int fchmod_umask(int fd, mode_t m) { mode_t u; int r; @@ -695,7 +748,7 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char * } /* Kernel threads have no argv[] */ - if (r == NULL || r[0] == 0) { + if (isempty(r)) { _cleanup_free_ char *t = NULL; int h; @@ -757,19 +810,30 @@ int get_process_capeff(pid_t pid, char **capeff) { return get_status_field(p, "\nCapEff:", capeff); } +static int get_process_link_contents(const char *proc_file, char **name) { + int r; + + assert(proc_file); + assert(name); + + r = readlink_malloc(proc_file, name); + if (r < 0) + return r == -ENOENT ? -ESRCH : r; + + return 0; +} + int get_process_exe(pid_t pid, char **name) { const char *p; char *d; int r; assert(pid >= 0); - assert(name); p = procfs_file_alloca(pid, "exe"); - - r = readlink_malloc(p, name); + r = get_process_link_contents(p, name); if (r < 0) - return r == -ENOENT ? -ESRCH : r; + return r; d = endswith(*name, " (deleted)"); if (d) @@ -821,6 +885,59 @@ int get_process_gid(pid_t pid, gid_t *gid) { return get_process_id(pid, "Gid:", gid); } +int get_process_cwd(pid_t pid, char **cwd) { + const char *p; + + assert(pid >= 0); + + p = procfs_file_alloca(pid, "cwd"); + + return get_process_link_contents(p, cwd); +} + +int get_process_root(pid_t pid, char **root) { + const char *p; + + assert(pid >= 0); + + p = procfs_file_alloca(pid, "root"); + + return get_process_link_contents(p, root); +} + +int get_process_environ(pid_t pid, char **env) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *outcome = NULL; + int c; + const char *p; + size_t allocated = 0, sz = 0; + + assert(pid >= 0); + assert(env); + + p = procfs_file_alloca(pid, "environ"); + + f = fopen(p, "re"); + if (!f) + return -errno; + + while ((c = fgetc(f)) != EOF) { + if (!GREEDY_REALLOC(outcome, allocated, sz + 5)) + return -ENOMEM; + + if (c == '\0') + outcome[sz++] = '\n'; + else + sz += cescape_char(c, outcome + sz); + } + + outcome[sz] = '\0'; + *env = outcome; + outcome = NULL; + + return 0; +} + char *strnappend(const char *s, const char *suffix, size_t b) { size_t a; char *r; @@ -893,6 +1010,28 @@ int readlink_malloc(const char *p, char **ret) { return readlinkat_malloc(AT_FDCWD, p, ret); } +int readlink_value(const char *p, char **ret) { + _cleanup_free_ char *link = NULL; + char *value; + int r; + + r = readlink_malloc(p, &link); + if (r < 0) + return r; + + value = basename(link); + if (!value) + return -ENOENT; + + value = strdup(value); + if (!value) + return -ENOMEM; + + *ret = value; + + return 0; +} + int readlink_and_make_absolute(const char *p, char **r) { _cleanup_free_ char *target = NULL; char *k; @@ -1178,63 +1317,7 @@ char *cescape(const char *s) { return NULL; for (f = s, t = r; *f; f++) - - switch (*f) { - - case '\a': - *(t++) = '\\'; - *(t++) = 'a'; - break; - case '\b': - *(t++) = '\\'; - *(t++) = 'b'; - break; - case '\f': - *(t++) = '\\'; - *(t++) = 'f'; - break; - case '\n': - *(t++) = '\\'; - *(t++) = 'n'; - break; - case '\r': - *(t++) = '\\'; - *(t++) = 'r'; - break; - case '\t': - *(t++) = '\\'; - *(t++) = 't'; - break; - case '\v': - *(t++) = '\\'; - *(t++) = 'v'; - break; - case '\\': - *(t++) = '\\'; - *(t++) = '\\'; - break; - case '"': - *(t++) = '\\'; - *(t++) = '"'; - break; - case '\'': - *(t++) = '\\'; - *(t++) = '\''; - break; - - default: - /* For special chars we prefer octal over - * hexadecimal encoding, simply because glib's - * g_strescape() does the same */ - if ((*f < ' ') || (*f >= 127)) { - *(t++) = '\\'; - *(t++) = octchar((unsigned char) *f >> 6); - *(t++) = octchar((unsigned char) *f >> 3); - *(t++) = octchar((unsigned char) *f); - } else - *(t++) = *f; - break; - } + t += cescape_char(*f, t); *t = 0; @@ -2017,9 +2100,9 @@ int acquire_terminal( assert(notify >= 0); for (;;) { - uint8_t inotify_buffer[sizeof(struct inotify_event) + FILENAME_MAX]; - ssize_t l; + uint8_t buffer[INOTIFY_EVENT_MAX] _alignas_(struct inotify_event); struct inotify_event *e; + ssize_t l; if (timeout != USEC_INFINITY) { usec_t n; @@ -2040,9 +2123,8 @@ int acquire_terminal( } } - l = read(notify, inotify_buffer, sizeof(inotify_buffer)); + l = read(notify, buffer, sizeof(buffer)); if (l < 0) { - if (errno == EINTR || errno == EAGAIN) continue; @@ -2050,21 +2132,11 @@ int acquire_terminal( goto fail; } - e = (struct inotify_event*) inotify_buffer; - - while (l > 0) { - size_t step; - + FOREACH_INOTIFY_EVENT(e, buffer, l) { if (e->wd != wd || !(e->mask & IN_CLOSE)) { r = -EIO; goto fail; } - - step = sizeof(struct inotify_event) + e->len; - assert(step <= (size_t) l); - - e = (struct inotify_event*) ((uint8_t*) e + step); - l -= step; } break; @@ -2081,7 +2153,7 @@ int acquire_terminal( r = reset_terminal_fd(fd, true); if (r < 0) - log_warning("Failed to reset terminal: %s", strerror(-r)); + log_warning_errno(r, "Failed to reset terminal: %m"); return fd; @@ -2220,13 +2292,15 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) { return n; } -ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) { +int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) { const uint8_t *p = buf; ssize_t n = 0; assert(fd >= 0); assert(buf); + errno = 0; + while (nbytes > 0) { ssize_t k; @@ -2245,14 +2319,15 @@ ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) { } if (k <= 0) - return n > 0 ? n : (k < 0 ? -errno : 0); + /* We were not done yet, and a write error occured. */ + return errno ? -errno : -EIO; p += k; nbytes -= k; n += k; } - return n; + return 0; } int parse_size(const char *t, off_t base, off_t *size) { @@ -2466,14 +2541,53 @@ char* dirname_malloc(const char *path) { } int dev_urandom(void *p, size_t n) { - _cleanup_close_ int fd; + static int have_syscall = -1; + int r, fd; ssize_t k; + /* Gathers some randomness from the kernel. This call will + * never block, and will always return some data from the + * kernel, regardless if the random pool is fully initialized + * or not. It thus makes no guarantee for the quality of the + * returned entropy, but is good enough for or usual usecases + * of seeding the hash functions for hashtable */ + + /* Use the getrandom() syscall unless we know we don't have + * it, or when the requested size is too large for it. */ + if (have_syscall != 0 || (size_t) (int) n != n) { + r = getrandom(p, n, GRND_NONBLOCK); + if (r == (int) n) { + have_syscall = true; + return 0; + } + + if (r < 0) { + if (errno == ENOSYS) + /* we lack the syscall, continue with + * reading from /dev/urandom */ + have_syscall = false; + else if (errno == EAGAIN) + /* not enough entropy for now. Let's + * remember to use the syscall the + * next time, again, but also read + * from /dev/urandom for now, which + * doesn't care about the current + * amount of entropy. */ + have_syscall = true; + else + return -errno; + } else + /* too short read? */ + return -EIO; + } + fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY); if (fd < 0) return errno == ENOENT ? -ENOSYS : -errno; k = loop_read(fd, p, n, true); + safe_close(fd); + if (k < 0) return (int) k; if ((size_t) k != n) @@ -2482,8 +2596,36 @@ int dev_urandom(void *p, size_t n) { return 0; } -void random_bytes(void *p, size_t n) { +void initialize_srand(void) { static bool srand_called = false; + unsigned x; +#ifdef HAVE_SYS_AUXV_H + void *auxv; +#endif + + if (srand_called) + return; + + x = 0; + +#ifdef HAVE_SYS_AUXV_H + /* The kernel provides us with a bit of entropy in auxv, so + * let's try to make use of that to seed the pseudo-random + * generator. It's better than nothing... */ + + auxv = (void*) getauxval(AT_RANDOM); + if (auxv) + x ^= *(unsigned*) auxv; +#endif + + x ^= (unsigned) now(CLOCK_REALTIME); + x ^= (unsigned) gettid(); + + srand(x); + srand_called = true; +} + +void random_bytes(void *p, size_t n) { uint8_t *q; int r; @@ -2494,28 +2636,7 @@ void random_bytes(void *p, size_t n) { /* If some idiot made /dev/urandom unavailable to us, he'll * get a PRNG instead. */ - if (!srand_called) { - unsigned x = 0; - -#ifdef HAVE_SYS_AUXV_H - /* The kernel provides us with a bit of entropy in - * auxv, so let's try to make use of that to seed the - * pseudo-random generator. It's better than - * nothing... */ - - void *auxv; - - auxv = (void*) getauxval(AT_RANDOM); - if (auxv) - x ^= *(unsigned*) auxv; -#endif - - x ^= (unsigned) now(CLOCK_REALTIME); - x ^= (unsigned) gettid(); - - srand(x); - srand_called = true; - } + initialize_srand(); for (q = p; q < (uint8_t*) p + n; q ++) *q = rand(); @@ -2737,7 +2858,7 @@ int get_ctty(pid_t pid, dev_t *_devnr, char **r) { if (k < 0) return k; - snprintf(fn, sizeof(fn), "/dev/char/%u:%u", major(devnr), minor(devnr)); + sprintf(fn, "/dev/char/%u:%u", major(devnr), minor(devnr)); k = readlink_malloc(fn, &s); if (k < 0) { @@ -2873,6 +2994,15 @@ _pure_ static int is_temporary_fs(struct statfs *s) { F_TYPE_EQUAL(s->f_type, RAMFS_MAGIC); } +int is_fd_on_temporary_fs(int fd) { + struct statfs s; + + if (fstatfs(fd, &s) < 0) + return -errno; + + return is_temporary_fs(&s); +} + int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) { struct statfs s; @@ -2895,6 +3025,19 @@ int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root return rm_rf_children_dangerous(fd, only_dirs, honour_sticky, root_dev); } +static int file_is_priv_sticky(const char *p) { + struct stat st; + + assert(p); + + if (lstat(p, &st) < 0) + return -errno; + + return + (st.st_uid == 0 || st.st_uid == getuid()) && + (st.st_mode & S_ISVTX); +} + static int rm_rf_internal(const char *path, bool only_dirs, bool delete_root, bool honour_sticky, bool dangerous) { int fd, r; struct statfs s; @@ -2975,11 +3118,11 @@ int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { * first change the access mode and only then hand out * ownership to avoid a window where access is too open. */ - if (mode != (mode_t) -1) + if (mode != MODE_INVALID) if (chmod(path, mode) < 0) return -errno; - if (uid != (uid_t) -1 || gid != (gid_t) -1) + if (uid != UID_INVALID || gid != GID_INVALID) if (chown(path, uid, gid) < 0) return -errno; @@ -2993,11 +3136,11 @@ int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) { * first change the access mode and only then hand out * ownership to avoid a window where access is too open. */ - if (mode != (mode_t) -1) + if (mode != MODE_INVALID) if (fchmod(fd, mode) < 0) return -errno; - if (uid != (uid_t) -1 || gid != (gid_t) -1) + if (uid != UID_INVALID || gid != GID_INVALID) if (fchown(fd, uid, gid) < 0) return -errno; @@ -3133,7 +3276,8 @@ char *replace_env(const char *format, char **env) { case CURLY: if (*e == '{') { - if (!(k = strnappend(r, word, e-word-1))) + k = strnappend(r, word, e-word-1); + if (!k) goto fail; free(r); @@ -3143,7 +3287,8 @@ char *replace_env(const char *format, char **env) { state = VARIABLE; } else if (*e == '$') { - if (!(k = strnappend(r, word, e-word))) + k = strnappend(r, word, e-word); + if (!k) goto fail; free(r); @@ -3175,7 +3320,8 @@ char *replace_env(const char *format, char **env) { } } - if (!(k = strnappend(r, word, e-word))) + k = strnappend(r, word, e-word); + if (!k) goto fail; free(r); @@ -3208,7 +3354,7 @@ char **replace_env_argv(char **argv, char **env) { if (e) { int r; - r = strv_split_quoted(&m, e); + r = strv_split_quoted(&m, e, true); if (r < 0) { ret[k] = NULL; strv_free(ret); @@ -3485,7 +3631,7 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi return -errno; } - if (uid != (uid_t) -1 || gid != (gid_t) -1) { + if (uid != UID_INVALID || gid != GID_INVALID) { r = fchown(fd, uid, gid); if (r < 0) return -errno; @@ -3506,7 +3652,7 @@ int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gi } int touch(const char *path) { - return touch_file(path, false, USEC_INFINITY, (uid_t) -1, (gid_t) -1, 0); + return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, 0); } char *unquote(const char *s, const char* quotes) { @@ -3529,41 +3675,33 @@ char *unquote(const char *s, const char* quotes) { } char *normalize_env_assignment(const char *s) { - _cleanup_free_ char *name = NULL, *value = NULL, *p = NULL; - char *eq, *r; + _cleanup_free_ char *value = NULL; + const char *eq; + char *p, *name; eq = strchr(s, '='); if (!eq) { - char *t; + char *r, *t; r = strdup(s); if (!r) return NULL; t = strstrip(r); - if (t == r) - return r; + if (t != r) + memmove(r, t, strlen(t) + 1); - memmove(r, t, strlen(t) + 1); return r; } - name = strndup(s, eq - s); - if (!name) - return NULL; - - p = strdup(eq + 1); - if (!p) - return NULL; + name = strndupa(s, eq - s); + p = strdupa(eq + 1); value = unquote(strstrip(p), QUOTES); if (!value) return NULL; - if (asprintf(&r, "%s=%s", strstrip(name), value) < 0) - r = NULL; - - return r; + return strjoin(strstrip(name), "=", value, NULL); } int wait_for_terminate(pid_t pid, siginfo_t *status) { @@ -3599,8 +3737,11 @@ int wait_for_terminate(pid_t pid, siginfo_t *status) { * * That is, success is indicated by a return value of zero, and an * error is indicated by a non-zero value. + * + * A warning is emitted if the process terminates abnormally, + * and also if it returns non-zero unless check_exit_code is true. */ -int wait_for_terminate_and_warn(const char *name, pid_t pid) { +int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code) { int r; siginfo_t status; @@ -3608,20 +3749,17 @@ int wait_for_terminate_and_warn(const char *name, pid_t pid) { assert(pid > 1); r = wait_for_terminate(pid, &status); - if (r < 0) { - log_warning("Failed to wait for %s: %s", name, strerror(-r)); - return r; - } + if (r < 0) + return log_warning_errno(r, "Failed to wait for %s: %m", name); if (status.si_code == CLD_EXITED) { - if (status.si_status != 0) { - log_warning("%s failed with error code %i.", name, status.si_status); - return status.si_status; - } - - log_debug("%s succeeded.", name); - return 0; + if (status.si_status != 0) + log_full(check_exit_code ? LOG_WARNING : LOG_DEBUG, + "%s failed with error code %i.", name, status.si_status); + else + log_debug("%s succeeded.", name); + return status.si_status; } else if (status.si_code == CLD_KILLED || status.si_code == CLD_DUMPED) { @@ -3880,7 +4018,7 @@ void execute_directory(const char *directory, DIR *d, usec_t timeout, char *argv executor_pid = fork(); if (executor_pid < 0) { - log_error("Failed to fork: %m"); + log_error_errno(errno, "Failed to fork: %m"); return; } else if (executor_pid == 0) { @@ -3903,7 +4041,7 @@ void execute_directory(const char *directory, DIR *d, usec_t timeout, char *argv if (errno == ENOENT) _exit(EXIT_SUCCESS); - log_error("Failed to enumerate directory %s: %m", directory); + log_error_errno(errno, "Failed to enumerate directory %s: %m", directory); _exit(EXIT_FAILURE); } } @@ -3929,7 +4067,7 @@ void execute_directory(const char *directory, DIR *d, usec_t timeout, char *argv pid = fork(); if (pid < 0) { - log_error("Failed to fork: %m"); + log_error_errno(errno, "Failed to fork: %m"); continue; } else if (pid == 0) { char *_argv[2]; @@ -3944,7 +4082,7 @@ void execute_directory(const char *directory, DIR *d, usec_t timeout, char *argv argv[0] = path; execv(path, argv); - log_error("Failed to execute %s: %m", path); + log_error_errno(errno, "Failed to execute %s: %m", path); _exit(EXIT_FAILURE); } @@ -3976,13 +4114,13 @@ void execute_directory(const char *directory, DIR *d, usec_t timeout, char *argv path = hashmap_remove(pids, UINT_TO_PTR(pid)); assert(path); - wait_for_terminate_and_warn(path, pid); + wait_for_terminate_and_warn(path, pid, true); } _exit(EXIT_SUCCESS); } - wait_for_terminate_and_warn(directory, executor_pid); + wait_for_terminate_and_warn(directory, executor_pid, true); } int kill_and_sigcont(pid_t pid, int sig) { @@ -4780,19 +4918,6 @@ int block_get_whole_disk(dev_t d, dev_t *ret) { return -ENOENT; } -int file_is_priv_sticky(const char *p) { - struct stat st; - - assert(p); - - if (lstat(p, &st) < 0) - return -errno; - - return - (st.st_uid == 0 || st.st_uid == getuid()) && - (st.st_mode & S_ISVTX); -} - static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", @@ -5149,7 +5274,7 @@ int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *pa * keep an unused copy of stdin around. */ fd = open("/dev/tty", O_WRONLY); if (fd < 0) { - log_error("Failed to open /dev/tty: %m"); + log_error_errno(errno, "Failed to open /dev/tty: %m"); _exit(EXIT_FAILURE); } @@ -5332,16 +5457,12 @@ int make_console_stdio(void) { /* Make /dev/console the controlling terminal and stdin/stdout/stderr */ fd = acquire_terminal("/dev/console", false, true, true, USEC_INFINITY); - if (fd < 0) { - log_error("Failed to acquire terminal: %s", strerror(-fd)); - return fd; - } + if (fd < 0) + return log_error_errno(fd, "Failed to acquire terminal: %m"); r = make_stdio(fd); - if (r < 0) { - log_error("Failed to duplicate terminal fd: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to duplicate terminal fd: %m"); return 0; } @@ -6096,85 +6217,48 @@ int split_pair(const char *s, const char *sep, char **l, char **r) { } int shall_restore_state(void) { - _cleanup_free_ char *line = NULL; - const char *word, *state; - size_t l; + _cleanup_free_ char *value = NULL; int r; - r = proc_cmdline(&line); + r = get_proc_cmdline_key("systemd.restore_state=", &value); if (r < 0) return r; - if (r == 0) /* Container ... */ - return 1; - - r = 1; - - FOREACH_WORD_QUOTED(word, l, line, state) { - const char *e; - char n[l+1]; - int k; - - memcpy(n, word, l); - n[l] = 0; - - e = startswith(n, "systemd.restore_state="); - if (!e) - continue; - - k = parse_boolean(e); - if (k >= 0) - r = k; - } + if (r == 0) + return true; - return r; + return parse_boolean(value) != 0; } int proc_cmdline(char **ret) { - int r; - - if (detect_container(NULL) > 0) { - char *buf = NULL, *p; - size_t sz = 0; - - r = read_full_file("/proc/1/cmdline", &buf, &sz); - if (r < 0) - return r; - - for (p = buf; p + 1 < buf + sz; p++) - if (*p == 0) - *p = ' '; - - *p = 0; - *ret = buf; - return 1; - } - - r = read_one_line_file("/proc/cmdline", ret); - if (r < 0) - return r; + assert(ret); - return 1; + if (detect_container(NULL) > 0) + return get_process_cmdline(1, 0, false, ret); + else + return read_one_line_file("/proc/cmdline", ret); } int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value)) { _cleanup_free_ char *line = NULL; - const char *w, *state; - size_t l; + const char *p; int r; assert(parse_item); r = proc_cmdline(&line); if (r < 0) - log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r)); - if (r <= 0) - return 0; + return r; - FOREACH_WORD_QUOTED(w, l, line, state) { - char word[l+1], *value; + p = line; + for (;;) { + _cleanup_free_ char *word = NULL; + char *value = NULL; - memcpy(word, w, l); - word[l] = 0; + r = unquote_first_word(&p, &word, true); + if (r < 0) + return r; + if (r == 0) + break; /* Filter out arguments that are intended only for the * initrd */ @@ -6193,6 +6277,59 @@ int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value)) { return 0; } +int get_proc_cmdline_key(const char *key, char **value) { + _cleanup_free_ char *line = NULL, *ret = NULL; + bool found = false; + const char *p; + int r; + + assert(key); + + r = proc_cmdline(&line); + if (r < 0) + return r; + + p = line; + for (;;) { + _cleanup_free_ char *word = NULL; + const char *e; + + r = unquote_first_word(&p, &word, true); + if (r < 0) + return r; + if (r == 0) + break; + + /* Filter out arguments that are intended only for the + * initrd */ + if (!in_initrd() && startswith(word, "rd.")) + continue; + + if (value) { + e = startswith(word, key); + if (!e) + continue; + + r = free_and_strdup(&ret, e); + if (r < 0) + return r; + + found = true; + } else { + if (streq(word, key)) + found = true; + } + } + + if (value) { + *value = ret; + ret = NULL; + } + + return found; + +} + int container_get_leader(const char *machine, pid_t *pid) { _cleanup_free_ char *s = NULL, *class = NULL; const char *p; @@ -6363,6 +6500,10 @@ int getpeercred(int fd, struct ucred *ucred) { * to namespacing issues */ if (u.pid <= 0) return -ENODATA; + if (u.uid == UID_INVALID) + return -ENODATA; + if (u.gid == GID_INVALID) + return -ENODATA; *ucred = u; return 0; @@ -6938,19 +7079,19 @@ int is_symlink(const char *path) { int is_dir(const char* path, bool follow) { struct stat st; + int r; - if (follow) { - if (stat(path, &st) < 0) - return -errno; - } else { - if (lstat(path, &st) < 0) - return -errno; - } + if (follow) + r = stat(path, &st); + else + r = lstat(path, &st); + if (r < 0) + return -errno; return !!S_ISDIR(st.st_mode); } -int unquote_first_word(const char **p, char **ret) { +int unquote_first_word(const char **p, char **ret, bool relax) { _cleanup_free_ char *s = NULL; size_t allocated = 0, sz = 0; @@ -7009,8 +7150,11 @@ int unquote_first_word(const char **p, char **ret) { break; case VALUE_ESCAPE: - if (c == 0) + if (c == 0) { + if (relax) + goto finish; return -EINVAL; + } if (!GREEDY_REALLOC(s, allocated, sz+2)) return -ENOMEM; @@ -7021,9 +7165,11 @@ int unquote_first_word(const char **p, char **ret) { break; case SINGLE_QUOTE: - if (c == 0) + if (c == 0) { + if (relax) + goto finish; return -EINVAL; - else if (c == '\'') + } else if (c == '\'') state = VALUE; else if (c == '\\') state = SINGLE_QUOTE_ESCAPE; @@ -7037,8 +7183,11 @@ int unquote_first_word(const char **p, char **ret) { break; case SINGLE_QUOTE_ESCAPE: - if (c == 0) + if (c == 0) { + if (relax) + goto finish; return -EINVAL; + } if (!GREEDY_REALLOC(s, allocated, sz+2)) return -ENOMEM; @@ -7064,8 +7213,11 @@ int unquote_first_word(const char **p, char **ret) { break; case DOUBLE_QUOTE_ESCAPE: - if (c == 0) + if (c == 0) { + if (relax) + goto finish; return -EINVAL; + } if (!GREEDY_REALLOC(s, allocated, sz+2)) return -ENOMEM; @@ -7125,7 +7277,7 @@ int unquote_many_words(const char **p, ...) { l = newa0(char*, n); for (c = 0; c < n; c++) { - r = unquote_first_word(p, &l[c]); + r = unquote_first_word(p, &l[c], false); if (r < 0) { int j; diff --git a/src/shared/util.h b/src/shared/util.h index 35584467c1..73bd9012fd 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -112,7 +112,7 @@ #define ANSI_HIGHLIGHT_OFF "\x1B[0m" #define ANSI_ERASE_TO_END_OF_LINE "\x1B[K" -size_t page_size(void); +size_t page_size(void) _pure_; #define PAGE_ALIGN(l) ALIGN_TO((l), page_size()) #define streq(a,b) (strcmp((a),(b)) == 0) @@ -245,6 +245,9 @@ static inline int safe_atoi64(const char *s, int64_t *ret_i) { return safe_atolli(s, (long long int*) ret_i); } +int safe_atou16(const char *s, uint16_t *ret); +int safe_atoi16(const char *s, int16_t *ret); + const char* split(const char **state, size_t *l, const char *separator, bool quoted); #define FOREACH_WORD(word, length, s, state) \ @@ -260,7 +263,6 @@ const char* split(const char **state, size_t *l, const char *separator, bool quo for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted))) pid_t get_parent_of_pid(pid_t pid, pid_t *ppid); -int get_starttime_of_pid(pid_t pid, unsigned long long *st); char *strappend(const char *s, const char *suffix); char *strnappend(const char *s, const char *suffix, size_t length); @@ -270,6 +272,7 @@ char **replace_env_argv(char **argv, char **env); int readlinkat_malloc(int fd, const char *p, char **ret); int readlink_malloc(const char *p, char **r); +int readlink_value(const char *p, char **ret); int readlink_and_make_absolute(const char *p, char **r); int readlink_and_canonicalize(const char *p, char **r); @@ -291,6 +294,9 @@ int get_process_exe(pid_t pid, char **name); int get_process_uid(pid_t pid, uid_t *uid); int get_process_gid(pid_t pid, gid_t *gid); int get_process_capeff(pid_t pid, char **capeff); +int get_process_cwd(pid_t pid, char **cwd); +int get_process_root(pid_t pid, char **root); +int get_process_environ(pid_t pid, char **environ); char hexchar(int x) _const_; int unhexchar(char c) _const_; @@ -321,6 +327,7 @@ int make_console_stdio(void); int dev_urandom(void *p, size_t n); void random_bytes(void *p, size_t n); +void initialize_srand(void); static inline uint64_t random_u64(void) { uint64_t u; @@ -418,7 +425,7 @@ int sigaction_many(const struct sigaction *sa, ...); int fopen_temporary(const char *path, FILE **_f, char **_temp_path); ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll); -ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll); +int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll); bool is_device_path(const char *path); @@ -446,6 +453,8 @@ int get_ctty(pid_t, dev_t *_devnr, char **r); int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid); +int is_fd_on_temporary_fs(int fd); + int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev); int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev); int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky); @@ -505,7 +514,7 @@ char *unquote(const char *s, const char *quotes); char *normalize_env_assignment(const char *s); int wait_for_terminate(pid_t pid, siginfo_t *status); -int wait_for_terminate_and_warn(const char *name, pid_t pid); +int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code); noreturn void freeze(void); @@ -581,8 +590,6 @@ static inline bool _pure_ in_charset(const char *s, const char* charset) { int block_get_whole_disk(dev_t d, dev_t *ret); -int file_is_priv_sticky(const char *p); - #define NULSTR_FOREACH(i, l) \ for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1) @@ -793,6 +800,15 @@ static inline void _reset_errno_(int *saved_errno) { #define PROTECT_ERRNO _cleanup_(_reset_errno_) __attribute__((unused)) int _saved_errno_ = errno +static inline int negative_errno(void) { + /* This helper should be used to shut up gcc if you know 'errno' is + * negative. Instead of "return -errno;", use "return negative_errno();" + * It will suppress bogus gcc warnings in case it assumes 'errno' might + * be 0 and thus the caller's error-handling might not be triggered. */ + assert_return(errno > 0, -EINVAL); + return -errno; +} + struct _umask_struct_ { mode_t mask; bool quit; @@ -829,6 +845,21 @@ static inline int log2i(int x) { return __SIZEOF_INT__ * 8 - __builtin_clz(x) - 1; } +static inline unsigned log2u(unsigned x) { + assert(x > 0); + + return sizeof(unsigned) * 8 - __builtin_clz(x) - 1; +} + +static inline unsigned log2u_round_up(unsigned x) { + assert(x > 0); + + if (x == 1) + return 0; + + return log2u(x - 1) + 1; +} + static inline bool logind_running(void) { return access("/run/systemd/seats/", F_OK) >= 0; } @@ -942,6 +973,7 @@ static inline void qsort_safe(void *base, size_t nmemb, size_t size, int proc_cmdline(char **ret); int parse_proc_cmdline(int (*parse_word)(const char *key, const char *value)); +int get_proc_cmdline_key(const char *parameter, char **value); int container_get_leader(const char *machine, pid_t *pid); @@ -993,9 +1025,16 @@ int take_password_lock(const char *root); int is_symlink(const char *path); int is_dir(const char *path, bool follow); -int unquote_first_word(const char **p, char **ret); +int unquote_first_word(const char **p, char **ret, bool relax); int unquote_many_words(const char **p, ...) _sentinel_; int free_and_strdup(char **p, const char *s); int sethostname_idempotent(const char *s); + +#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1) + +#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \ + for ((e) = (struct inotify_event*) (buffer); \ + (uint8_t*) (e) < (uint8_t*) (buffer) + (sz); \ + (e) = (struct inotify_event*) ((uint8_t*) (e) + sizeof(struct inotify_event) + (e)->len)) diff --git a/src/shared/virt.c b/src/shared/virt.c index f9c4e67c74..f10baab40b 100644 --- a/src/shared/virt.c +++ b/src/shared/virt.c @@ -293,8 +293,26 @@ int detect_container(const char **id) { r = read_one_line_file("/run/systemd/container", &m); if (r == -ENOENT) { - r = 0; - goto finish; + + /* Fallback for cases where PID 1 was not + * systemd (for example, cases where + * init=/bin/sh is used. */ + + r = getenv_for_pid(1, "container", &m); + if (r <= 0) { + + /* If that didn't work, give up, + * assume no container manager. + * + * Note: This means we still cannot + * detect containers if init=/bin/sh + * is passed but privileges dropped, + * as /proc/1/environ is only readable + * with privileges. */ + + r = 0; + goto finish; + } } if (r < 0) return r; diff --git a/src/shared/watchdog.c b/src/shared/watchdog.c index 7d188d98e8..2fe4eb81cf 100644 --- a/src/shared/watchdog.c +++ b/src/shared/watchdog.c @@ -44,36 +44,28 @@ static int update_timeout(void) { flags = WDIOS_DISABLECARD; r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags); - if (r < 0) { - log_warning("Failed to disable hardware watchdog: %m"); - return -errno; - } + if (r < 0) + return log_warning_errno(errno, "Failed to disable hardware watchdog: %m"); } else { int sec, flags; char buf[FORMAT_TIMESPAN_MAX]; sec = (int) ((watchdog_timeout + USEC_PER_SEC - 1) / USEC_PER_SEC); r = ioctl(watchdog_fd, WDIOC_SETTIMEOUT, &sec); - if (r < 0) { - log_warning("Failed to set timeout to %is: %m", sec); - return -errno; - } + if (r < 0) + return log_warning_errno(errno, "Failed to set timeout to %is: %m", sec); watchdog_timeout = (usec_t) sec * USEC_PER_SEC; log_info("Set hardware watchdog to %s.", format_timespan(buf, sizeof(buf), watchdog_timeout, 0)); flags = WDIOS_ENABLECARD; r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags); - if (r < 0) { - log_warning("Failed to enable hardware watchdog: %m"); - return -errno; - } + if (r < 0) + return log_warning_errno(errno, "Failed to enable hardware watchdog: %m"); r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0); - if (r < 0) { - log_warning("Failed to ping hardware watchdog: %m"); - return -errno; - } + if (r < 0) + return log_warning_errno(errno, "Failed to ping hardware watchdog: %m"); } return 0; @@ -127,10 +119,8 @@ int watchdog_ping(void) { } r = ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0); - if (r < 0) { - log_warning("Failed to ping hardware watchdog: %m"); - return -errno; - } + if (r < 0) + return log_warning_errno(errno, "Failed to ping hardware watchdog: %m"); return 0; } @@ -148,7 +138,7 @@ void watchdog_close(bool disarm) { flags = WDIOS_DISABLECARD; r = ioctl(watchdog_fd, WDIOC_SETOPTIONS, &flags); if (r < 0) - log_warning("Failed to disable hardware watchdog: %m"); + log_warning_errno(errno, "Failed to disable hardware watchdog: %m"); /* To be sure, use magic close logic, too */ for (;;) { @@ -158,7 +148,7 @@ void watchdog_close(bool disarm) { break; if (errno != EINTR) { - log_error("Failed to disarm watchdog timer: %m"); + log_error_errno(errno, "Failed to disarm watchdog timer: %m"); break; } } diff --git a/src/shutdownd/shutdownd.c b/src/shutdownd/shutdownd.c index 0f008a6100..826efbfeab 100644 --- a/src/shutdownd/shutdownd.c +++ b/src/shutdownd/shutdownd.c @@ -79,7 +79,7 @@ static int read_packet(int fd, union shutdown_buffer *_b) { if (errno == EAGAIN || errno == EINTR) return 0; - log_error("recvmsg(): %m"); + log_error_errno(errno, "recvmsg(): %m"); return -errno; } @@ -203,20 +203,16 @@ static int update_schedule_file(struct sd_shutdown_command *c) { assert(c); r = mkdir_safe_label("/run/systemd/shutdown", 0755, 0, 0); - if (r < 0) { - log_error("Failed to create shutdown subdirectory: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to create shutdown subdirectory: %m"); t = cescape(c->wall_message); if (!t) return log_oom(); r = fopen_temporary("/run/systemd/shutdown/scheduled", &f, &temp_path); - if (r < 0) { - log_error("Failed to save information about scheduled shutdowns: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to save information about scheduled shutdowns: %m"); fchmod(fileno(f), 0644); @@ -237,7 +233,7 @@ static int update_schedule_file(struct sd_shutdown_command *c) { fflush(f); if (ferror(f) || rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) { - log_error("Failed to write information about scheduled shutdowns: %m"); + log_error_errno(errno, "Failed to write information about scheduled shutdowns: %m"); r = -errno; unlink(temp_path); @@ -284,7 +280,7 @@ int main(int argc, char *argv[]) { n_fds = sd_listen_fds(true); if (n_fds < 0) { - log_error("Failed to read listening file descriptors from environment: %s", strerror(-r)); + log_error_errno(r, "Failed to read listening file descriptors from environment: %m"); return EXIT_FAILURE; } @@ -300,7 +296,7 @@ int main(int argc, char *argv[]) { pollfd[i].events = POLLIN; pollfd[i].fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC); if (pollfd[i].fd < 0) { - log_error("timerfd_create(): %m"); + log_error_errno(errno, "timerfd_create(): %m"); goto finish; } } @@ -321,7 +317,7 @@ int main(int argc, char *argv[]) { if (errno == EAGAIN || errno == EINTR) continue; - log_error("poll(): %m"); + log_error_errno(errno, "poll(): %m"); goto finish; } @@ -358,7 +354,7 @@ int main(int argc, char *argv[]) { warn_wall(n, &b.command); } if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) { - log_error("timerfd_settime(): %m"); + log_error_errno(errno, "timerfd_settime(): %m"); goto finish; } @@ -366,7 +362,7 @@ int main(int argc, char *argv[]) { zero(its); timespec_store(&its.it_value, when_nologin(b.command.usec)); if (timerfd_settime(pollfd[FD_NOLOGIN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) { - log_error("timerfd_settime(): %m"); + log_error_errno(errno, "timerfd_settime(): %m"); goto finish; } @@ -374,7 +370,7 @@ int main(int argc, char *argv[]) { zero(its); timespec_store(&its.it_value, b.command.usec); if (timerfd_settime(pollfd[FD_SHUTDOWN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) { - log_error("timerfd_settime(): %m"); + log_error_errno(errno, "timerfd_settime(): %m"); goto finish; } @@ -398,7 +394,7 @@ int main(int argc, char *argv[]) { /* Restart timer */ timespec_store(&its.it_value, when_wall(n, b.command.usec)); if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) { - log_error("timerfd_settime(): %m"); + log_error_errno(errno, "timerfd_settime(): %m"); goto finish; } } @@ -410,7 +406,7 @@ int main(int argc, char *argv[]) { e = write_string_file_atomic("/run/nologin", "System is going down."); if (e < 0) - log_error("Failed to create /run/nologin: %s", strerror(-e)); + log_error_errno(e, "Failed to create /run/nologin: %m"); else unlink_nologin = true; @@ -452,7 +448,7 @@ finish: (b.command.warn_wall ? NULL : "--no-wall"), NULL); - log_error("Failed to execute /sbin/shutdown: %m"); + log_error_errno(errno, "Failed to execute /sbin/shutdown: %m"); } sd_notify(false, diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c index ca00eea4ab..56b963a729 100644 --- a/src/sleep/sleep.c +++ b/src/sleep/sleep.c @@ -48,15 +48,14 @@ static int write_mode(char **modes) { if (k == 0) return 0; - log_debug("Failed to write '%s' to /sys/power/disk: %s", - *mode, strerror(-k)); + log_debug_errno(k, "Failed to write '%s' to /sys/power/disk: %m", + *mode); if (r == 0) r = k; } if (r < 0) - log_error("Failed to write mode to /sys/power/disk: %s", - strerror(-r)); + log_error_errno(r, "Failed to write mode to /sys/power/disk: %m"); return r; } @@ -71,51 +70,49 @@ static int write_state(FILE **f, char **states) { k = write_string_stream(*f, *state); if (k == 0) return 0; - log_debug("Failed to write '%s' to /sys/power/state: %s", - *state, strerror(-k)); + log_debug_errno(k, "Failed to write '%s' to /sys/power/state: %m", + *state); if (r == 0) r = k; fclose(*f); *f = fopen("/sys/power/state", "we"); - if (!*f) { - log_error("Failed to open /sys/power/state: %m"); - return -errno; - } + if (!*f) + return log_error_errno(errno, "Failed to open /sys/power/state: %m"); } return r; } static int execute(char **modes, char **states) { - char* arguments[4]; + + char *arguments[] = { + NULL, + (char*) "pre", + arg_verb, + NULL + }; + int r; _cleanup_fclose_ FILE *f = NULL; - const char* note = strappenda("SLEEP=", arg_verb); /* This file is opened first, so that if we hit an error, * we can abort before modifying any state. */ f = fopen("/sys/power/state", "we"); - if (!f) { - log_error("Failed to open /sys/power/state: %m"); - return -errno; - } + if (!f) + return log_error_errno(errno, "Failed to open /sys/power/state: %m"); /* Configure the hibernation mode */ r = write_mode(modes); if (r < 0) return r; - arguments[0] = NULL; - arguments[1] = (char*) "pre"; - arguments[2] = arg_verb; - arguments[3] = NULL; execute_directory(SYSTEM_SLEEP_PATH, NULL, DEFAULT_TIMEOUT_USEC, arguments); log_struct(LOG_INFO, - MESSAGE_ID(SD_MESSAGE_SLEEP_START), - "MESSAGE=Suspending system...", - note, + LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_START), + LOG_MESSAGE("Suspending system..."), + "SLEEP=%s", arg_verb, NULL); r = write_state(&f, states); @@ -123,9 +120,9 @@ static int execute(char **modes, char **states) { return r; log_struct(LOG_INFO, - MESSAGE_ID(SD_MESSAGE_SLEEP_STOP), - "MESSAGE=System resumed.", - note, + LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_STOP), + LOG_MESSAGE("System resumed."), + "SLEEP=%s", arg_verb, NULL); arguments[1] = (char*) "post"; diff --git a/src/socket-proxy/socket-proxyd.c b/src/socket-proxy/socket-proxyd.c index 3041903757..a3c3c87f11 100644 --- a/src/socket-proxy/socket-proxyd.c +++ b/src/socket-proxy/socket-proxyd.c @@ -120,18 +120,14 @@ static int connection_create_pipes(Connection *c, int buffer[2], size_t *sz) { return 0; r = pipe2(buffer, O_CLOEXEC|O_NONBLOCK); - if (r < 0) { - log_error("Failed to allocate pipe buffer: %m"); - return -errno; - } + if (r < 0) + return log_error_errno(errno, "Failed to allocate pipe buffer: %m"); (void) fcntl(buffer[0], F_SETPIPE_SZ, BUFFER_SIZE); r = fcntl(buffer[0], F_GETPIPE_SZ); - if (r < 0) { - log_error("Failed to get pipe buffer size: %m"); - return -errno; - } + if (r < 0) + return log_error_errno(errno, "Failed to get pipe buffer size: %m"); assert(r > 0); *sz = r; @@ -171,10 +167,8 @@ static int connection_shovel( } else if (z == 0 || errno == EPIPE || errno == ECONNRESET) { *from_source = sd_event_source_unref(*from_source); *from = safe_close(*from); - } else if (errno != EAGAIN && errno != EINTR) { - log_error("Failed to splice: %m"); - return -errno; - } + } else if (errno != EAGAIN && errno != EINTR) + return log_error_errno(errno, "Failed to splice: %m"); } if (*full > 0 && *to >= 0) { @@ -185,10 +179,8 @@ static int connection_shovel( } else if (z == 0 || errno == EPIPE || errno == ECONNRESET) { *to_source = sd_event_source_unref(*to_source); *to = safe_close(*to); - } else if (errno != EAGAIN && errno != EINTR) { - log_error("Failed to splice: %m"); - return -errno; - } + } else if (errno != EAGAIN && errno != EINTR) + return log_error_errno(errno, "Failed to splice: %m"); } } while (shoveled); @@ -265,10 +257,8 @@ static int connection_enable_event_sources(Connection *c) { else r = 0; - if (r < 0) { - log_error("Failed to set up server event source: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to set up server event source: %m"); if (c->client_event_source) r = sd_event_source_set_io_events(c->client_event_source, b); @@ -277,10 +267,8 @@ static int connection_enable_event_sources(Connection *c) { else r = 0; - if (r < 0) { - log_error("Failed to set up client event source: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to set up client event source: %m"); return 0; } @@ -321,12 +309,12 @@ static int connect_cb(sd_event_source *s, int fd, uint32_t revents, void *userda solen = sizeof(error); r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &solen); if (r < 0) { - log_error("Failed to issue SO_ERROR: %m"); + log_error_errno(errno, "Failed to issue SO_ERROR: %m"); goto fail; } if (error != 0) { - log_error("Failed to connect to remote host: %s", strerror(error)); + log_error_errno(error, "Failed to connect to remote host: %m"); goto fail; } @@ -348,7 +336,7 @@ static int connection_start(Connection *c, struct sockaddr *sa, socklen_t salen) c->client_fd = socket(sa->sa_family, SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); if (c->client_fd < 0) { - log_error("Failed to get remote socket: %m"); + log_error_errno(errno, "Failed to get remote socket: %m"); goto fail; } @@ -357,17 +345,17 @@ static int connection_start(Connection *c, struct sockaddr *sa, socklen_t salen) if (errno == EINPROGRESS) { r = sd_event_add_io(c->context->event, &c->client_event_source, c->client_fd, EPOLLOUT, connect_cb, c); if (r < 0) { - log_error("Failed to add connection socket: %s", strerror(-r)); + log_error_errno(r, "Failed to add connection socket: %m"); goto fail; } r = sd_event_source_set_enabled(c->client_event_source, SD_EVENT_ONESHOT); if (r < 0) { - log_error("Failed to enable oneshot event source: %s", strerror(-r)); + log_error_errno(r, "Failed to enable oneshot event source: %m"); goto fail; } } else { - log_error("Failed to connect to remote host: %m"); + log_error_errno(errno, "Failed to connect to remote host: %m"); goto fail; } } else { @@ -449,7 +437,7 @@ static int resolve_remote(Connection *c) { log_debug("Looking up address info for %s:%s", node, service); r = sd_resolve_getaddrinfo(c->context->resolve, &c->resolve_query, node, service, &hints, resolve_cb, c); if (r < 0) { - log_error("Failed to resolve remote host: %s", strerror(-r)); + log_error_errno(r, "Failed to resolve remote host: %m"); goto fail; } @@ -514,21 +502,21 @@ static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdat nfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); if (nfd < 0) { if (errno != -EAGAIN) - log_warning("Failed to accept() socket: %m"); + log_warning_errno(errno, "Failed to accept() socket: %m"); } else { getpeername_pretty(nfd, &peer); log_debug("New connection from %s", strna(peer)); r = add_connection_socket(context, nfd); if (r < 0) { - log_error("Failed to accept connection, ignoring: %s", strerror(-r)); + log_error_errno(r, "Failed to accept connection, ignoring: %m"); safe_close(fd); } } r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT); if (r < 0) { - log_error("Error while re-enabling listener with ONESHOT: %s", strerror(-r)); + log_error_errno(r, "Error while re-enabling listener with ONESHOT: %m"); sd_event_exit(context->event, r); return r; } @@ -550,30 +538,24 @@ static int add_listen_socket(Context *context, int fd) { } r = sd_is_socket(fd, 0, SOCK_STREAM, 1); - if (r < 0) { - log_error("Failed to determine socket type: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to determine socket type: %m"); if (r == 0) { log_error("Passed in socket is not a stream socket."); return -EINVAL; } r = fd_nonblock(fd, true); - if (r < 0) { - log_error("Failed to mark file descriptor non-blocking: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to mark file descriptor non-blocking: %m"); r = sd_event_add_io(context->event, &source, fd, EPOLLIN, accept_cb, context); - if (r < 0) { - log_error("Failed to add event source: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add event source: %m"); r = set_put(context->listen, source); if (r < 0) { - log_error("Failed to add source to set: %s", strerror(-r)); + log_error_errno(r, "Failed to add source to set: %m"); sd_event_source_unref(source); return r; } @@ -581,10 +563,8 @@ static int add_listen_socket(Context *context, int fd) { /* Set the watcher to oneshot in case other processes are also * watching to accept(). */ r = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT); - if (r < 0) { - log_error("Failed to enable oneshot mode: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to enable oneshot mode: %m"); return 0; } @@ -663,19 +643,19 @@ int main(int argc, char *argv[]) { r = sd_event_default(&context.event); if (r < 0) { - log_error("Failed to allocate event loop: %s", strerror(-r)); + log_error_errno(r, "Failed to allocate event loop: %m"); goto finish; } r = sd_resolve_default(&context.resolve); if (r < 0) { - log_error("Failed to allocate resolver: %s", strerror(-r)); + log_error_errno(r, "Failed to allocate resolver: %m"); goto finish; } r = sd_resolve_attach_event(context.resolve, context.event, 0); if (r < 0) { - log_error("Failed to attach resolver: %s", strerror(-r)); + log_error_errno(r, "Failed to attach resolver: %m"); goto finish; } @@ -700,7 +680,7 @@ int main(int argc, char *argv[]) { r = sd_event_loop(context.event); if (r < 0) { - log_error("Failed to run event loop: %s", strerror(-r)); + log_error_errno(r, "Failed to run event loop: %m"); goto finish; } diff --git a/src/sysctl/sysctl.c b/src/sysctl/sysctl.c index 809e59b71f..973e67e3c8 100644 --- a/src/sysctl/sysctl.c +++ b/src/sysctl/sysctl.c @@ -38,15 +38,7 @@ static char **arg_prefixes = NULL; -static const char conf_file_dirs[] = - "/etc/sysctl.d\0" - "/run/sysctl.d\0" - "/usr/local/lib/sysctl.d\0" - "/usr/lib/sysctl.d\0" -#ifdef HAVE_SPLIT_USR - "/lib/sysctl.d\0" -#endif - ; +static const char conf_file_dirs[] = CONF_DIRS_NULSTR("sysctl"); static char* normalize_sysctl(char *s) { char *n; @@ -142,8 +134,7 @@ static int parse_file(Hashmap *sysctl_options, const char *path, bool ignore_eno if (ignore_enoent && r == -ENOENT) return 0; - log_error("Failed to open file '%s', ignoring: %s", path, strerror(-r)); - return r; + return log_error_errno(r, "Failed to open file '%s', ignoring: %m", path); } log_debug("parse: %s", path); @@ -156,7 +147,7 @@ static int parse_file(Hashmap *sysctl_options, const char *path, bool ignore_eno if (feof(f)) break; - log_error("Failed to read file '%s', ignoring: %m", path); + log_error_errno(errno, "Failed to read file '%s', ignoring: %m", path); return -errno; } @@ -204,7 +195,7 @@ static int parse_file(Hashmap *sysctl_options, const char *path, bool ignore_eno k = hashmap_put(sysctl_options, property, new_value); if (k < 0) { - log_error("Failed to add sysctl variable %s to hashmap: %s", property, strerror(-k)); + log_error_errno(k, "Failed to add sysctl variable %s to hashmap: %m", property); free(property); free(new_value); return k; @@ -322,7 +313,7 @@ int main(int argc, char *argv[]) { r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs); if (r < 0) { - log_error("Failed to enumerate sysctl.d files: %s", strerror(-r)); + log_error_errno(r, "Failed to enumerate sysctl.d files: %m"); goto finish; } diff --git a/src/system-update-generator/system-update-generator.c b/src/system-update-generator/system-update-generator.c index 4f22c9c359..464ee22b20 100644 --- a/src/system-update-generator/system-update-generator.c +++ b/src/system-update-generator/system-update-generator.c @@ -41,15 +41,13 @@ static int generate_symlink(void) { if (errno == ENOENT) return 0; - log_error("Failed to check for system update: %m"); + log_error_errno(errno, "Failed to check for system update: %m"); return -EINVAL; } p = strappenda(arg_dest, "/default.target"); - if (symlink(SYSTEM_DATA_UNIT_PATH "/system-update.target", p) < 0) { - log_error("Failed to create symlink %s: %m", p); - return -errno; - } + if (symlink(SYSTEM_DATA_UNIT_PATH "/system-update.target", p) < 0) + return log_error_errno(errno, "Failed to create symlink %s: %m", p); return 0; } diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 28eaa6a847..b1441ad86a 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -67,11 +67,13 @@ #include "logs-show.h" #include "socket-util.h" #include "fileio.h" +#include "copy.h" #include "env-util.h" #include "bus-util.h" #include "bus-message.h" #include "bus-error.h" -#include "bus-errors.h" +#include "bus-common-errors.h" +#include "mkdir.h" static char **arg_types = NULL; static char **arg_states = NULL; @@ -301,21 +303,37 @@ static int compare_unit_info(const void *a, const void *b) { } static bool output_show_unit(const UnitInfo *u, char **patterns) { - const char *dot; - if (!strv_isempty(patterns)) { char **pattern; STRV_FOREACH(pattern, patterns) if (fnmatch(*pattern, u->id, FNM_NOESCAPE) == 0) - return true; + goto next; return false; } - return (!arg_types || ((dot = strrchr(u->id, '.')) && - strv_find(arg_types, dot+1))) && - (arg_all || !(streq(u->active_state, "inactive") - || u->following[0]) || u->job_id > 0); +next: + if (arg_types) { + const char *dot; + + dot = strrchr(u->id, '.'); + if (!dot) + return false; + + if (!strv_find(arg_types, dot+1)) + return false; + } + + if (arg_all) + return true; + + if (u->job_id > 0) + return true; + + if (streq(u->active_state, "inactive") || u->following[0]) + return false; + + return true; } static int output_units_list(const UnitInfo *unit_infos, unsigned c) { @@ -605,7 +623,7 @@ static int get_unit_list_recursive( r = sd_bus_open_system_container(&container, *i); if (r < 0) { - log_error("Failed to connect to container %s: %s", *i, strerror(-r)); + log_error_errno(r, "Failed to connect to container %s: %m", *i); continue; } @@ -1231,18 +1249,33 @@ static int compare_unit_file_list(const void *a, const void *b) { } static bool output_show_unit_file(const UnitFileList *u, char **patterns) { - const char *dot; - if (!strv_isempty(patterns)) { char **pattern; STRV_FOREACH(pattern, patterns) if (fnmatch(*pattern, basename(u->path), FNM_NOESCAPE) == 0) - return true; + goto next; return false; } - return !arg_types || ((dot = strrchr(u->path, '.')) && strv_find(arg_types, dot+1)); +next: + if (!strv_isempty(arg_types)) { + const char *dot; + + dot = strrchr(u->path, '.'); + if (!dot) + return false; + + if (!strv_find(arg_types, dot+1)) + return false; + } + + if (!strv_isempty(arg_states)) { + if (!strv_find(arg_states, unit_file_state_to_string(u->state))) + return false; + } + + return true; } static void output_unit_file_list(const UnitFileList *units, unsigned c) { @@ -1328,7 +1361,7 @@ static int list_unit_files(sd_bus *bus, char **args) { r = unit_file_get_list(arg_scope, arg_root, h); if (r < 0) { unit_file_list_free(h); - log_error("Failed to get unit file list: %s", strerror(-r)); + log_error_errno(r, "Failed to get unit file list: %m"); return r; } @@ -1448,11 +1481,13 @@ static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, cha "RequiresOverridable\0" "Requisite\0" "RequisiteOverridable\0" - "Wants\0", + "Wants\0" + "BindsTo\0", [DEPENDENCY_REVERSE] = "RequiredBy\0" "RequiredByOverridable\0" "WantedBy\0" - "PartOf\0", + "PartOf\0" + "BoundBy\0", [DEPENDENCY_AFTER] = "After\0", [DEPENDENCY_BEFORE] = "Before\0", }; @@ -1872,10 +1907,8 @@ static int get_default(sd_bus *bus, char **args) { if (!bus || avoid_bus()) { r = unit_file_get_default(arg_scope, arg_root, &_path); - if (r < 0) { - log_error("Failed to get default target: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get default target: %m"); path = _path; } else { @@ -1955,10 +1988,8 @@ static int set_default(sd_bus *bus, char **args) { if (!bus || avoid_bus()) { r = unit_file_set_default(arg_scope, arg_root, unit, true, &changes, &n_changes); - if (r < 0) { - log_error("Failed to set default target: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to set default target: %m"); if (!arg_quiet) dump_unit_file_changes(changes, n_changes); @@ -2166,10 +2197,8 @@ static int cancel_job(sd_bus *bus, char **args) { int q; q = safe_atou32(*name, &id); - if (q < 0) { - log_error("Failed to parse job id \"%s\": %s", *name, strerror(-q)); - return q; - } + if (q < 0) + return log_error_errno(q, "Failed to parse job id \"%s\": %m", *name); q = sd_bus_message_new_method_call( bus, @@ -2397,10 +2426,8 @@ static int wait_for_jobs(sd_bus *bus, Set *s) { while (!set_isempty(s)) { q = bus_process_wait(bus); - if (q < 0) { - log_error("Failed to wait for response: %s", strerror(-q)); - return q; - } + if (q < 0) + return log_error_errno(q, "Failed to wait for response: %m"); if (d.result) { q = check_wait_response(&d); @@ -2526,10 +2553,8 @@ static int check_triggering_units( STRV_FOREACH(i, triggered_by) { r = check_one_unit(bus, *i, "active\0reloading\0", true); - if (r < 0) { - log_error("Failed to check unit: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to check unit: %m"); if (r == 0) continue; @@ -2773,15 +2798,13 @@ static int start_unit(sd_bus *bus, char **args) { else { r = expand_names(bus, args + 1, suffix, &names); if (r < 0) - log_error("Failed to expand names: %s", strerror(-r)); + log_error_errno(r, "Failed to expand names: %m"); } if (!arg_no_block) { r = enable_wait_for_jobs(bus); - if (r < 0) { - log_error("Could not watch jobs: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Could not watch jobs: %m"); s = set_new(&string_hash_ops); if (!s) @@ -3040,10 +3063,8 @@ static int check_unit_generic(sd_bus *bus, int code, const char *good_states, ch assert(args); r = expand_names(bus, args, NULL, &names); - if (r < 0) { - log_error("Failed to expand names: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to expand names: %m"); STRV_FOREACH(name, names) { int state; @@ -3081,7 +3102,7 @@ static int kill_unit(sd_bus *bus, char **args) { r = expand_names(bus, args + 1, NULL, &names); if (r < 0) - log_error("Failed to expand names: %s", strerror(-r)); + log_error_errno(r, "Failed to expand names: %m"); STRV_FOREACH(name, names) { _cleanup_bus_message_unref_ sd_bus_message *m = NULL; @@ -3199,6 +3220,7 @@ typedef struct UnitStatusInfo { const char *active_state; const char *sub_state; const char *unit_file_state; + const char *unit_file_preset; const char *description; const char *following; @@ -3240,7 +3262,14 @@ typedef struct UnitStatusInfo { bool failed_condition_trigger; bool failed_condition_negate; const char *failed_condition; - const char *failed_condition_param; + const char *failed_condition_parameter; + + usec_t assert_timestamp; + bool assert_result; + bool failed_assert_trigger; + bool failed_assert_negate; + const char *failed_assert; + const char *failed_assert_parameter; /* Socket */ unsigned n_accepted; @@ -3315,7 +3344,10 @@ static void print_status_info( if (i->load_error) printf(" Loaded: %s%s%s (Reason: %s)\n", on, strna(i->load_state), off, i->load_error); - else if (path && i->unit_file_state) + else if (path && !isempty(i->unit_file_state) && !isempty(i->unit_file_preset)) + printf(" Loaded: %s%s%s (%s; %s; vendor preset: %s)\n", + on, strna(i->load_state), off, path, i->unit_file_state, i->unit_file_preset); + else if (path && !isempty(i->unit_file_state)) printf(" Loaded: %s%s%s (%s; %s)\n", on, strna(i->load_state), off, path, i->unit_file_state); else if (path) @@ -3384,7 +3416,8 @@ static void print_status_info( s1 = format_timestamp_relative(since1, sizeof(since1), i->condition_timestamp); s2 = format_timestamp(since2, sizeof(since2), i->condition_timestamp); - printf(" start condition failed at %s%s%s\n", + printf("Condition: start %scondition failed%s at %s%s%s\n", + ansi_highlight_yellow(), ansi_highlight_off(), s2, s1 ? "; " : "", s1 ? s1 : ""); if (i->failed_condition_trigger) printf(" none of the trigger conditions were met\n"); @@ -3392,7 +3425,23 @@ static void print_status_info( printf(" %s=%s%s was not met\n", i->failed_condition, i->failed_condition_negate ? "!" : "", - i->failed_condition_param); + i->failed_condition_parameter); + } + + if (!i->assert_result && i->assert_timestamp > 0) { + s1 = format_timestamp_relative(since1, sizeof(since1), i->assert_timestamp); + s2 = format_timestamp(since2, sizeof(since2), i->assert_timestamp); + + printf(" Assert: start %sassertion failed%s at %s%s%s\n", + ansi_highlight_red(), ansi_highlight_off(), + s2, s1 ? "; " : "", s1 ? s1 : ""); + if (i->failed_assert_trigger) + printf(" none of the trigger assertions were met\n"); + else if (i->failed_assert) + printf(" %s=%s%s was not met\n", + i->failed_assert, + i->failed_assert_negate ? "!" : "", + i->failed_assert_parameter); } if (i->sysfs_path) @@ -3623,6 +3672,8 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo * i->following = s; else if (streq(name, "UnitFileState")) i->unit_file_state = s; + else if (streq(name, "UnitFilePreset")) + i->unit_file_preset = s; else if (streq(name, "Result")) i->result = s; } @@ -3643,6 +3694,8 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo * i->need_daemon_reload = b; else if (streq(name, "ConditionResult")) i->condition_result = b; + else if (streq(name, "AssertResult")) + i->assert_result = b; break; } @@ -3712,6 +3765,8 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo * i->active_exit_timestamp = (usec_t) u; else if (streq(name, "ConditionTimestamp")) i->condition_timestamp = (usec_t) u; + else if (streq(name, "AssertTimestamp")) + i->assert_timestamp = (usec_t) u; break; } @@ -3804,7 +3859,32 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo * i->failed_condition = cond; i->failed_condition_trigger = trigger; i->failed_condition_negate = negate; - i->failed_condition_param = param; + i->failed_condition_parameter = param; + } + } + 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); + + } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Asserts")) { + const char *cond, *param; + int trigger, negate; + int32_t state; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sbbsi)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(m, "(sbbsi)", &cond, &trigger, &negate, ¶m, &state)) > 0) { + log_debug("%s %d %d %s %d", cond, trigger, negate, param, state); + if (state < 0 && (!trigger || !i->failed_assert)) { + i->failed_assert = cond; + i->failed_assert_trigger = trigger; + i->failed_assert_negate = negate; + i->failed_assert_parameter = param; } } if (r < 0) @@ -4351,10 +4431,8 @@ static int show_system_status(sd_bus *bus) { return log_oom(); r = bus_map_all_properties(bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", machine_info_property_map, &mi); - if (r < 0) { - log_error("Failed to read server status: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to read server status: %m"); if (streq_ptr(mi.state, "degraded")) { on = ansi_highlight_red(); @@ -4471,7 +4549,7 @@ static int show(sd_bus *bus, char **args) { r = expand_names(bus, patterns, NULL, &names); if (r < 0) - log_error("Failed to expand names: %s", strerror(-r)); + log_error_errno(r, "Failed to expand names: %m"); STRV_FOREACH(name, names) { _cleanup_free_ char *unit; @@ -4507,7 +4585,7 @@ static int cat(sd_bus *bus, char **args) { r = expand_names(bus, args + 1, NULL, &names); if (r < 0) - log_error("Failed to expand names: %s", strerror(-r)); + log_error_errno(r, "Failed to expand names: %m"); pager_open_if_enabled(); @@ -4563,9 +4641,9 @@ static int cat(sd_bus *bus, char **args) { ansi_highlight_off()); fflush(stdout); - r = sendfile_full(STDOUT_FILENO, fragment_path); + r = copy_file_fd(fragment_path, STDOUT_FILENO); if (r < 0) { - log_warning("Failed to cat %s: %s", fragment_path, strerror(-r)); + log_warning_errno(r, "Failed to cat %s: %m", fragment_path); continue; } } @@ -4578,9 +4656,9 @@ static int cat(sd_bus *bus, char **args) { ansi_highlight_off()); fflush(stdout); - r = sendfile_full(STDOUT_FILENO, *path); + r = copy_file_fd(*path, STDOUT_FILENO); if (r < 0) { - log_warning("Failed to cat %s: %s", *path, strerror(-r)); + log_warning_errno(r, "Failed to cat %s: %m", *path); continue; } } @@ -4720,7 +4798,7 @@ static int delete_snapshot(sd_bus *bus, char **args) { r = expand_names(bus, args + 1, ".snapshot", &names); if (r < 0) - log_error("Failed to expand names: %s", strerror(-r)); + log_error_errno(r, "Failed to expand names: %m"); STRV_FOREACH(name, names) { _cleanup_bus_message_unref_ sd_bus_message *m = NULL; @@ -4821,7 +4899,7 @@ static int reset_failed(sd_bus *bus, char **args) { r = expand_names(bus, args + 1, NULL, &names); if (r < 0) - log_error("Failed to expand names: %s", strerror(-r)); + log_error_errno(r, "Failed to expand names: %m"); STRV_FOREACH(name, names) { _cleanup_bus_message_unref_ sd_bus_message *m = NULL; @@ -4915,7 +4993,7 @@ static int switch_root(sd_bus *bus, char **args) { "init", &cmdline_init, NULL); if (r < 0) - log_debug("Failed to parse /proc/cmdline: %s", strerror(-r)); + log_debug_errno(r, "Failed to parse /proc/cmdline: %m"); init = cmdline_init; } @@ -5065,7 +5143,7 @@ static int enable_sysv_units(const char *verb, char **args) { int r = 0; #if defined(HAVE_SYSV_COMPAT) && defined(HAVE_CHKCONFIG) - unsigned f = 1, t = 1; + unsigned f = 0; _cleanup_lookup_paths_free_ LookupPaths paths = {}; if (arg_scope != UNIT_FILE_SYSTEM) @@ -5084,7 +5162,7 @@ static int enable_sysv_units(const char *verb, char **args) { return r; r = 0; - for (f = 0; args[f]; f++) { + while (args[f]) { const char *name; _cleanup_free_ char *p = NULL, *q = NULL, *l = NULL; bool found_native = false, found_sysv; @@ -5095,7 +5173,7 @@ static int enable_sysv_units(const char *verb, char **args) { pid_t pid; siginfo_t status; - name = args[f]; + name = args[f++]; if (!endswith(name, ".service")) continue; @@ -5127,9 +5205,6 @@ static int enable_sysv_units(const char *verb, char **args) { if (!found_sysv) continue; - /* Mark this entry, so that we don't try enabling it as native unit */ - args[f] = (char*) ""; - log_info("%s is not a native service, redirecting to /sbin/chkconfig.", name); if (!isempty(arg_root)) @@ -5148,10 +5223,9 @@ static int enable_sysv_units(const char *verb, char **args) { log_info("Executing %s", l); pid = fork(); - if (pid < 0) { - log_error("Failed to fork: %m"); - return -errno; - } else if (pid == 0) { + if (pid < 0) + return log_error_errno(errno, "Failed to fork: %m"); + else if (pid == 0) { /* Child */ execv(argv[0], (char**) argv); @@ -5160,7 +5234,7 @@ static int enable_sysv_units(const char *verb, char **args) { j = wait_for_terminate(pid, &status); if (j < 0) { - log_error("Failed to wait for child: %s", strerror(-r)); + log_error_errno(r, "Failed to wait for child: %m"); return j; } @@ -5179,19 +5253,12 @@ static int enable_sysv_units(const char *verb, char **args) { return -EINVAL; } else return -EPROTO; - } - - /* Drop all SysV units */ - for (f = 0, t = 0; args[f]; f++) { - - if (isempty(args[f])) - continue; - args[t++] = args[f]; + /* Remove this entry, so that we don't try enabling it as native unit */ + assert(f > 0 && streq(args[f-1], name)); + assert_se(strv_remove(args + f - 1, name)); } - args[t] = NULL; - #endif return r; } @@ -5274,7 +5341,7 @@ static int enable_unit(sd_bus *bus, char **args) { assert_not_reached("Unknown verb"); if (r < 0) { - log_error("Operation failed: %s", strerror(-r)); + log_error_errno(r, "Operation failed: %m"); goto finish; } @@ -5422,10 +5489,8 @@ static int add_dependency(sd_bus *bus, char **args) { r = unit_file_add_dependency(arg_scope, arg_runtime, arg_root, names, target, dep, arg_force, &changes, &n_changes); - if (r < 0) { - log_error("Can't add dependency: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Can't add dependency: %m"); if (!arg_quiet) dump_unit_file_changes(changes, n_changes); @@ -5486,7 +5551,7 @@ static int preset_all(sd_bus *bus, char **args) { r = unit_file_preset_all(arg_scope, arg_runtime, arg_root, arg_preset_mode, arg_force, &changes, &n_changes); if (r < 0) { - log_error("Operation failed: %s", strerror(-r)); + log_error_errno(r, "Operation failed: %m"); goto finish; } @@ -5568,14 +5633,13 @@ static int unit_is_enabled(sd_bus *bus, char **args) { UnitFileState state; state = unit_file_get_state(arg_scope, arg_root, *name); - if (state < 0) { - log_error("Failed to get unit file state for %s: %s", *name, strerror(-state)); - return state; - } + if (state < 0) + return log_error_errno(state, "Failed to get unit file state for %s: %m", *name); if (state == UNIT_FILE_ENABLED || state == UNIT_FILE_ENABLED_RUNTIME || - state == UNIT_FILE_STATIC) + state == UNIT_FILE_STATIC || + state == UNIT_FILE_INDIRECT) enabled = true; if (!arg_quiet) @@ -5605,9 +5669,7 @@ static int unit_is_enabled(sd_bus *bus, char **args) { if (r < 0) return bus_log_parse_error(r); - if (streq(s, "enabled") || - streq(s, "enabled-runtime") || - streq(s, "static")) + if (STR_IN_SET(s, "enabled", "enabled-runtime", "static", "indirect")) enabled = true; if (!arg_quiet) @@ -5642,6 +5704,519 @@ static int is_system_running(sd_bus *bus, char **args) { return streq(state, "running") ? EXIT_SUCCESS : EXIT_FAILURE; } +static int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **unit_path) { + char **p; + + assert(lp); + assert(unit_name); + assert(unit_path); + + STRV_FOREACH(p, lp->unit_path) { + char *path; + + path = path_join(arg_root, *p, unit_name); + if (!path) + return log_oom(); + + if (access(path, F_OK) == 0) { + *unit_path = path; + return 1; + } + + free(path); + } + + return 0; +} + +static int create_edit_temp_file(const char *new_path, const char *original_path, char **ret_tmp_fn) { + int r; + char *t; + + assert(new_path); + assert(original_path); + assert(ret_tmp_fn); + + t = tempfn_random(new_path); + if (!t) + return log_oom(); + + r = mkdir_parents(new_path, 0755); + if (r < 0) { + log_error_errno(r, "Failed to create directories for %s: %m", new_path); + free(t); + return r; + } + + r = copy_file(original_path, t, 0, 0644); + if (r == -ENOENT) { + r = touch(t); + if (r < 0) { + log_error_errno(r, "Failed to create temporary file %s: %m", t); + free(t); + return r; + } + } else if (r < 0) { + log_error_errno(r, "Failed to copy %s to %s: %m", original_path, t); + free(t); + return r; + } + + *ret_tmp_fn = t; + + return 0; +} + +static int get_drop_in_to_edit(const char *unit_name, const char *user_home, const char *user_runtime, char **ret_path) { + char *tmp_new_path; + char *tmp; + + assert(unit_name); + assert(ret_path); + + switch (arg_scope) { + case UNIT_FILE_SYSTEM: + tmp = strappenda(arg_runtime ? "/run/systemd/system/" : SYSTEM_CONFIG_UNIT_PATH "/", unit_name, ".d/override.conf"); + break; + case UNIT_FILE_GLOBAL: + tmp = strappenda(arg_runtime ? "/run/systemd/user/" : USER_CONFIG_UNIT_PATH "/", unit_name, ".d/override.conf"); + break; + case UNIT_FILE_USER: + assert(user_home); + assert(user_runtime); + + tmp = strappenda(arg_runtime ? user_runtime : user_home, "/", unit_name, ".d/override.conf"); + break; + default: + assert_not_reached("Invalid scope"); + } + + tmp_new_path = path_join(arg_root, tmp, NULL); + if (!tmp_new_path) + return log_oom(); + + *ret_path = tmp_new_path; + + return 0; +} + +static int unit_file_create_drop_in(const char *unit_name, const char *user_home, const char *user_runtime, char **ret_new_path, char **ret_tmp_path) { + char *tmp_new_path; + char *tmp_tmp_path; + int r; + + assert(unit_name); + assert(ret_new_path); + assert(ret_tmp_path); + + r = get_drop_in_to_edit(unit_name, user_home, user_runtime, &tmp_new_path); + if (r < 0) + return r; + + r = create_edit_temp_file(tmp_new_path, tmp_new_path, &tmp_tmp_path); + if (r < 0) { + free(tmp_new_path); + return r; + } + + *ret_new_path = tmp_new_path; + *ret_tmp_path = tmp_tmp_path; + + return 0; +} + +static bool unit_is_editable(const char *unit_name, const char *fragment_path, const char *user_home) { + bool editable = true; + const char *invalid_path; + + assert(unit_name); + + if (!arg_runtime) + return true; + + switch (arg_scope) { + case UNIT_FILE_SYSTEM: + if (path_startswith(fragment_path, "/etc/systemd/system")) { + editable = false; + invalid_path = "/etc/systemd/system"; + } else if (path_startswith(fragment_path, SYSTEM_CONFIG_UNIT_PATH)) { + editable = false; + invalid_path = SYSTEM_CONFIG_UNIT_PATH; + } + break; + case UNIT_FILE_GLOBAL: + if (path_startswith(fragment_path, "/etc/systemd/user")) { + editable = false; + invalid_path = "/etc/systemd/user"; + } else if (path_startswith(fragment_path, USER_CONFIG_UNIT_PATH)) { + editable = false; + invalid_path = USER_CONFIG_UNIT_PATH; + } + break; + case UNIT_FILE_USER: + assert(user_home); + + if (path_startswith(fragment_path, "/etc/systemd/user")) { + editable = false; + invalid_path = "/etc/systemd/user"; + } else if (path_startswith(fragment_path, USER_CONFIG_UNIT_PATH)) { + editable = false; + invalid_path = USER_CONFIG_UNIT_PATH; + } else if (path_startswith(fragment_path, user_home)) { + editable = false; + invalid_path = user_home; + } + break; + default: + assert_not_reached("Invalid scope"); + } + + if (!editable) + log_error("%s ignored: cannot temporarily edit units from %s", unit_name, invalid_path); + + return editable; +} + +static int get_copy_to_edit(const char *unit_name, const char *fragment_path, const char *user_home, const char *user_runtime, char **ret_path) { + char *tmp_new_path; + + assert(unit_name); + assert(ret_path); + + if (!unit_is_editable(unit_name, fragment_path, user_home)) + return -EINVAL; + + switch (arg_scope) { + case UNIT_FILE_SYSTEM: + tmp_new_path = path_join(arg_root, arg_runtime ? "/run/systemd/system/" : SYSTEM_CONFIG_UNIT_PATH, unit_name); + break; + case UNIT_FILE_GLOBAL: + tmp_new_path = path_join(arg_root, arg_runtime ? "/run/systemd/user/" : USER_CONFIG_UNIT_PATH, unit_name); + break; + case UNIT_FILE_USER: + assert(user_home); + assert(user_runtime); + + tmp_new_path = path_join(arg_root, arg_runtime ? user_runtime : user_home, unit_name); + break; + default: + assert_not_reached("Invalid scope"); + } + if (!tmp_new_path) + return log_oom(); + + *ret_path = tmp_new_path; + + return 0; +} + +static int unit_file_create_copy(const char *unit_name, + const char *fragment_path, + const char *user_home, + const char *user_runtime, + char **ret_new_path, + char **ret_tmp_path) { + char *tmp_new_path; + char *tmp_tmp_path; + int r; + + assert(fragment_path); + assert(unit_name); + assert(ret_new_path); + assert(ret_tmp_path); + + r = get_copy_to_edit(unit_name, fragment_path, user_home, user_runtime, &tmp_new_path); + if (r < 0) + return r; + + if (!path_equal(fragment_path, tmp_new_path) && access(tmp_new_path, F_OK) == 0) { + char response; + + r = ask_char(&response, "yn", "%s already exists, are you sure to overwrite it with %s? [(y)es, (n)o] ", tmp_new_path, fragment_path); + if (r < 0) { + free(tmp_new_path); + return r; + } + if (response != 'y') { + log_warning("%s ignored", unit_name); + free(tmp_new_path); + return -1; + } + } + + r = create_edit_temp_file(tmp_new_path, fragment_path, &tmp_tmp_path); + if (r < 0) { + log_error_errno(r, "Failed to create temporary file for %s: %m", tmp_new_path); + free(tmp_new_path); + return r; + } + + *ret_new_path = tmp_new_path; + *ret_tmp_path = tmp_tmp_path; + + return 0; +} + +static int run_editor(char **paths) { + pid_t pid; + int r; + + assert(paths); + + pid = fork(); + if (pid < 0) { + log_error_errno(errno, "Failed to fork: %m"); + return -errno; + } + + if (pid == 0) { + const char **args; + char **backup_editors = STRV_MAKE("nano", "vim", "vi"); + char *editor; + char **tmp_path, **original_path, **p; + unsigned i = 1; + size_t argc; + + argc = strv_length(paths)/2 + 1; + args = newa(const char*, argc + 1); + + args[0] = NULL; + STRV_FOREACH_PAIR(original_path, tmp_path, paths) { + args[i] = *tmp_path; + i++; + } + args[argc] = NULL; + + /* SYSTEMD_EDITOR takes precedence over EDITOR which takes precedence over VISUAL + * If neither SYSTEMD_EDITOR nor EDITOR nor VISUAL are present, + * we try to execute well known editors + */ + editor = getenv("SYSTEMD_EDITOR"); + if (!editor) + editor = getenv("EDITOR"); + if (!editor) + editor = getenv("VISUAL"); + + if (!isempty(editor)) { + args[0] = editor; + execvp(editor, (char* const*) args); + } + + STRV_FOREACH(p, backup_editors) { + args[0] = *p; + execvp(*p, (char* const*) args); + /* We do not fail if the editor doesn't exist + * because we want to try each one of them before + * failing. + */ + if (errno != ENOENT) { + log_error("Failed to execute %s: %m", editor); + _exit(EXIT_FAILURE); + } + } + + log_error("Cannot edit unit(s): No editor available. Please set either SYSTEMD_EDITOR or EDITOR or VISUAL environment variable"); + _exit(EXIT_FAILURE); + } + + r = wait_for_terminate_and_warn("editor", pid, true); + if (r < 0) + return log_error_errno(r, "Failed to wait for child: %m"); + + return r; +} + +static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) { + _cleanup_free_ char *user_home = NULL; + _cleanup_free_ char *user_runtime = NULL; + char **name; + int r; + + assert(names); + assert(paths); + + if (arg_scope == UNIT_FILE_USER) { + r = user_config_home(&user_home); + if (r < 0) + return log_oom(); + else if (r == 0) { + log_error("Cannot edit units for the user instance: home directory unknown"); + return -1; + } + + r = user_runtime_dir(&user_runtime); + if (r < 0) + return log_oom(); + else if (r == 0) { + log_error("Cannot edit units for the user instance: runtime directory unknown"); + return -1; + } + } + + if (!bus || avoid_bus()) { + _cleanup_lookup_paths_free_ LookupPaths lp = {}; + + /* If there is no bus, we try to find the units by testing each available directory + * according to the scope. + */ + r = lookup_paths_init(&lp, + arg_scope == UNIT_FILE_SYSTEM ? SYSTEMD_SYSTEM : SYSTEMD_USER, + arg_scope == UNIT_FILE_USER, + arg_root, + NULL, NULL, NULL); + if (r < 0) { + log_error_errno(r, "Failed get lookup paths: %m"); + return r; + } + + STRV_FOREACH(name, names) { + _cleanup_free_ char *path = NULL; + char *new_path, *tmp_path; + + r = unit_file_find_path(&lp, *name, &path); + if (r < 0) + return r; + if (r == 0) { + log_warning("%s ignored: not found", *name); + continue; + } + + if (arg_full) + r = unit_file_create_copy(*name, path, user_home, user_runtime, &new_path, &tmp_path); + else + r = unit_file_create_drop_in(*name, user_home, user_runtime, &new_path, &tmp_path); + + if (r < 0) + continue; + + r = strv_push(paths, new_path); + if (r < 0) + return log_oom(); + + r = strv_push(paths, tmp_path); + if (r < 0) + return log_oom(); + } + } else { + STRV_FOREACH(name, names) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char *fragment_path = NULL; + _cleanup_free_ char *unit = NULL; + char *new_path, *tmp_path; + + unit = unit_dbus_path_from_name(*name); + if (!unit) + return log_oom(); + + if (need_daemon_reload(bus, *name) > 0) { + log_warning("%s ignored: unit file changed on disk. Run 'systemctl%s daemon-reload'.", + *name, arg_scope == UNIT_FILE_SYSTEM ? "" : " --user"); + continue; + } + + r = sd_bus_get_property_string( + bus, + "org.freedesktop.systemd1", + unit, + "org.freedesktop.systemd1.Unit", + "FragmentPath", + &error, + &fragment_path); + if (r < 0) { + log_warning("Failed to get FragmentPath: %s", bus_error_message(&error, r)); + continue; + } + + if (isempty(fragment_path)) { + log_warning("%s ignored: not found", *name); + continue; + } + + if (arg_full) + r = unit_file_create_copy(*name, fragment_path, user_home, user_runtime, &new_path, &tmp_path); + else + r = unit_file_create_drop_in(*name, user_home, user_runtime, &new_path, &tmp_path); + if (r < 0) + continue; + + r = strv_push(paths, new_path); + if (r < 0) + return log_oom(); + + r = strv_push(paths, tmp_path); + if (r < 0) + return log_oom(); + } + } + + return 0; +} + +static int edit(sd_bus *bus, char **args) { + _cleanup_strv_free_ char **names = NULL; + _cleanup_strv_free_ char **paths = NULL; + char **original, **tmp; + int r; + + assert(args); + + if (!on_tty()) { + log_error("Cannot edit units if we are not on a tty"); + return -EINVAL; + } + + if (arg_transport != BUS_TRANSPORT_LOCAL) { + log_error("Cannot remotely edit units"); + return -EINVAL; + } + + r = expand_names(bus, args + 1, NULL, &names); + if (r < 0) + return log_error_errno(r, "Failed to expand names: %m"); + + if (!names) { + log_error("No unit name found by expanding names"); + return -ENOENT; + } + + r = find_paths_to_edit(bus, names, &paths); + if (r < 0) + return r; + + if (strv_isempty(paths)) { + log_error("Cannot find any units to edit"); + return -ENOENT; + } + + r = run_editor(paths); + if (r < 0) + goto end; + + STRV_FOREACH_PAIR(original, tmp, paths) { + /* If the temporary file is empty we ignore it. + * It's useful if the user wants to cancel its modification + */ + if (null_or_empty_path(*tmp)) { + log_warning("Edition of %s canceled: temporary file empty", *original); + continue; + } + r = rename(*tmp, *original); + if (r < 0) { + r = log_error_errno(errno, "Failed to rename %s to %s: %m", *tmp, *original); + goto end; + } + } + + if (!arg_no_reload && bus && !avoid_bus()) + r = daemon_reload(bus, args); + +end: + STRV_FOREACH_PAIR(original, tmp, paths) + unlink_noerrno(*tmp); + + return r; +} + static void systemctl_help(void) { pager_open_if_enabled(); @@ -5739,7 +6314,9 @@ static void systemctl_help(void) { " add-requires TARGET NAME... Add 'Requires' dependency for the target\n" " on specified one or more units\n" " get-default Get the name of the default target\n" - " set-default NAME Set the default target\n\n" + " set-default NAME Set the default target\n" + " edit NAME... Edit one or more unit files\n" + "\n" "Machine Commands:\n" " list-machines [PATTERN...] List local containers and host\n\n" "Job Commands:\n" @@ -6655,16 +7232,13 @@ static int talk_initctl(void) { if (errno == ENOENT) return 0; - log_error("Failed to open "INIT_FIFO": %m"); + log_error_errno(errno, "Failed to open "INIT_FIFO": %m"); return -errno; } - errno = 0; - r = loop_write(fd, &request, sizeof(request), false) != sizeof(request); - if (r) { - log_error("Failed to write to "INIT_FIFO": %m"); - return errno > 0 ? -errno : -EIO; - } + r = loop_write(fd, &request, sizeof(request), false); + if (r < 0) + return log_error_errno(r, "Failed to write to "INIT_FIFO": %m"); return 1; } @@ -6748,8 +7322,9 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) { { "get-default", EQUAL, 1, get_default, NOBUS }, { "set-property", MORE, 3, set_property }, { "is-system-running", EQUAL, 1, is_system_running }, - { "add-wants", MORE, 3, add_dependency, NOBUS }, - { "add-requires", MORE, 3, add_dependency, NOBUS }, + { "add-wants", MORE, 3, add_dependency, NOBUS }, + { "add-requires", MORE, 3, add_dependency, NOBUS }, + { "edit", MORE, 2, edit, NOBUS }, {} }, *verb = verbs; @@ -6811,7 +7386,7 @@ found: * enable/disable */ if (verb->bus == NOBUS) { if (!bus && !avoid_bus()) { - log_error("Failed to get D-Bus connection: %s", strerror(-bus_error)); + log_error_errno(bus_error, "Failed to get D-Bus connection: %m"); return -EIO; } @@ -6822,7 +7397,7 @@ found: } if ((verb->bus != FORCE || arg_force <= 0) && !bus) { - log_error("Failed to get D-Bus connection: %s", strerror(-bus_error)); + log_error_errno(bus_error, "Failed to get D-Bus connection: %m"); return -EIO; } } @@ -6886,10 +7461,8 @@ static int reload_with_fallback(sd_bus *bus) { /* Nothing else worked, so let's try signals */ assert(arg_action == ACTION_RELOAD || arg_action == ACTION_REEXEC); - if (kill(1, arg_action == ACTION_RELOAD ? SIGHUP : SIGTERM) < 0) { - log_error("kill() failed: %m"); - return -errno; - } + if (kill(1, arg_action == ACTION_RELOAD ? SIGHUP : SIGTERM) < 0) + return log_error_errno(errno, "kill() failed: %m"); return 0; } @@ -6917,8 +7490,13 @@ done: static int halt_now(enum action a) { -/* Make sure C-A-D is handled by the kernel from this - * point on... */ + /* The kernel will automaticall flush ATA disks and suchlike + * on reboot(), but the file systems need to be synce'd + * explicitly in advance. */ + sync(); + + /* Make sure C-A-D is handled by the kernel from this point + * on... */ reboot(RB_ENABLE_CAD); switch (a) { @@ -6995,7 +7573,7 @@ static int halt_main(sd_bus *bus) { m); if (r < 0) - log_warning("Failed to talk to shutdownd, proceeding with immediate shutdown: %s", strerror(-r)); + log_warning_errno(r, "Failed to talk to shutdownd, proceeding with immediate shutdown: %m"); else { char date[FORMAT_TIMESTAMP_MAX]; @@ -7014,7 +7592,7 @@ static int halt_main(sd_bus *bus) { else { r = utmp_put_shutdown(); if (r < 0) - log_warning("Failed to write utmp record: %s", strerror(-r)); + log_warning_errno(r, "Failed to write utmp record: %m"); } } @@ -7022,7 +7600,7 @@ static int halt_main(sd_bus *bus) { return 0; r = halt_now(arg_action); - log_error("Failed to reboot: %s", strerror(-r)); + log_error_errno(r, "Failed to reboot: %m"); return r; } @@ -7120,7 +7698,7 @@ int main(int argc, char*argv[]) { r = send_shutdownd(arg_when, SD_SHUTDOWN_NONE, false, !arg_no_wall, m); if (r < 0) - log_warning("Failed to talk to shutdownd, shutdown hasn't been cancelled: %s", strerror(-r)); + log_warning_errno(r, "Failed to talk to shutdownd, shutdown hasn't been cancelled: %m"); break; } diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h index 18acfc2ad7..c27537f862 100644 --- a/src/systemd/sd-bus.h +++ b/src/systemd/sd-bus.h @@ -46,35 +46,47 @@ typedef struct { int _need_free; } sd_bus_error; +typedef struct { + const char* name; + int code; +} sd_bus_error_map; + /* Flags */ enum { - SD_BUS_CREDS_PID = 1ULL << 0, - SD_BUS_CREDS_PID_STARTTIME = 1ULL << 1, - SD_BUS_CREDS_TID = 1ULL << 2, - SD_BUS_CREDS_UID = 1ULL << 3, - SD_BUS_CREDS_GID = 1ULL << 4, - SD_BUS_CREDS_COMM = 1ULL << 5, - SD_BUS_CREDS_TID_COMM = 1ULL << 6, - SD_BUS_CREDS_EXE = 1ULL << 7, - SD_BUS_CREDS_CMDLINE = 1ULL << 8, - SD_BUS_CREDS_CGROUP = 1ULL << 9, - SD_BUS_CREDS_UNIT = 1ULL << 10, - SD_BUS_CREDS_USER_UNIT = 1ULL << 11, - SD_BUS_CREDS_SLICE = 1ULL << 12, - SD_BUS_CREDS_SESSION = 1ULL << 13, - SD_BUS_CREDS_OWNER_UID = 1ULL << 14, - SD_BUS_CREDS_EFFECTIVE_CAPS = 1ULL << 15, - SD_BUS_CREDS_PERMITTED_CAPS = 1ULL << 16, - SD_BUS_CREDS_INHERITABLE_CAPS = 1ULL << 17, - SD_BUS_CREDS_BOUNDING_CAPS = 1ULL << 18, - SD_BUS_CREDS_SELINUX_CONTEXT = 1ULL << 19, - SD_BUS_CREDS_AUDIT_SESSION_ID = 1ULL << 20, - SD_BUS_CREDS_AUDIT_LOGIN_UID = 1ULL << 21, - SD_BUS_CREDS_UNIQUE_NAME = 1ULL << 22, - SD_BUS_CREDS_WELL_KNOWN_NAMES = 1ULL << 23, - SD_BUS_CREDS_CONNECTION_NAME = 1ULL << 24, - _SD_BUS_CREDS_ALL = (1ULL << 25) -1, + SD_BUS_CREDS_PID = 1ULL << 0, + SD_BUS_CREDS_TID = 1ULL << 1, + SD_BUS_CREDS_UID = 1ULL << 2, + SD_BUS_CREDS_EUID = 1ULL << 3, + SD_BUS_CREDS_SUID = 1ULL << 4, + SD_BUS_CREDS_FSUID = 1ULL << 5, + SD_BUS_CREDS_GID = 1ULL << 6, + SD_BUS_CREDS_EGID = 1ULL << 7, + SD_BUS_CREDS_SGID = 1ULL << 8, + SD_BUS_CREDS_FSGID = 1ULL << 9, + SD_BUS_CREDS_SUPPLEMENTARY_GIDS = 1ULL << 10, + SD_BUS_CREDS_COMM = 1ULL << 11, + SD_BUS_CREDS_TID_COMM = 1ULL << 12, + SD_BUS_CREDS_EXE = 1ULL << 13, + SD_BUS_CREDS_CMDLINE = 1ULL << 14, + SD_BUS_CREDS_CGROUP = 1ULL << 15, + SD_BUS_CREDS_UNIT = 1ULL << 16, + SD_BUS_CREDS_USER_UNIT = 1ULL << 17, + SD_BUS_CREDS_SLICE = 1ULL << 18, + SD_BUS_CREDS_SESSION = 1ULL << 19, + SD_BUS_CREDS_OWNER_UID = 1ULL << 20, + SD_BUS_CREDS_EFFECTIVE_CAPS = 1ULL << 21, + SD_BUS_CREDS_PERMITTED_CAPS = 1ULL << 22, + SD_BUS_CREDS_INHERITABLE_CAPS = 1ULL << 23, + SD_BUS_CREDS_BOUNDING_CAPS = 1ULL << 24, + SD_BUS_CREDS_SELINUX_CONTEXT = 1ULL << 25, + SD_BUS_CREDS_AUDIT_SESSION_ID = 1ULL << 26, + SD_BUS_CREDS_AUDIT_LOGIN_UID = 1ULL << 27, + SD_BUS_CREDS_UNIQUE_NAME = 1ULL << 28, + SD_BUS_CREDS_WELL_KNOWN_NAMES = 1ULL << 29, + SD_BUS_CREDS_DESCRIPTION = 1ULL << 30, + SD_BUS_CREDS_AUGMENT = 1ULL << 63, /* special flag, if on sd-bus will augment creds struct, in a potentially race-full way. */ + _SD_BUS_CREDS_ALL = (1ULL << 32) -1, }; enum { @@ -108,18 +120,29 @@ int sd_bus_open_system_remote(sd_bus **ret, const char *host); int sd_bus_open_system_container(sd_bus **ret, const char *machine); int sd_bus_new(sd_bus **ret); + int sd_bus_set_address(sd_bus *bus, const char *address); int sd_bus_set_fd(sd_bus *bus, int input_fd, int output_fd); int sd_bus_set_exec(sd_bus *bus, const char *path, char *const argv[]); +int sd_bus_get_address(sd_bus *bus, const char **address); int sd_bus_set_bus_client(sd_bus *bus, int b); -int sd_bus_set_server(sd_bus *bus, int b, sd_id128_t server_id); +int sd_bus_is_bus_client(sd_bus *bus); +int sd_bus_set_server(sd_bus *bus, int b, sd_id128_t bus_id); +int sd_bus_is_server(sd_bus *bus); int sd_bus_set_anonymous(sd_bus *bus, int b); +int sd_bus_is_anonymous(sd_bus *bus); int sd_bus_set_trusted(sd_bus *bus, int b); -int sd_bus_set_name(sd_bus *bus, const char *name); +int sd_bus_is_trusted(sd_bus *bus); int sd_bus_set_monitor(sd_bus *bus, int b); +int sd_bus_is_monitor(sd_bus *bus); +int sd_bus_set_description(sd_bus *bus, const char *description); +int sd_bus_get_description(sd_bus *bus, const char **description); int sd_bus_negotiate_fds(sd_bus *bus, int b); +int sd_bus_can_send(sd_bus *bus, char type); int sd_bus_negotiate_timestamp(sd_bus *bus, int b); -int sd_bus_negotiate_creds(sd_bus *bus, uint64_t creds_mask); +int sd_bus_negotiate_creds(sd_bus *bus, int b, uint64_t creds_mask); +int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *creds_mask); + int sd_bus_start(sd_bus *ret); int sd_bus_try_close(sd_bus *bus); @@ -129,11 +152,11 @@ sd_bus *sd_bus_ref(sd_bus *bus); sd_bus *sd_bus_unref(sd_bus *bus); int sd_bus_is_open(sd_bus *bus); -int sd_bus_can_send(sd_bus *bus, char type); -int sd_bus_get_server_id(sd_bus *bus, sd_id128_t *peer); -int sd_bus_get_owner_creds(sd_bus *bus, uint64_t creds_mask, sd_bus_creds **ret); -int sd_bus_get_name(sd_bus *bus, const char **name); + +int sd_bus_get_bus_id(sd_bus *bus, sd_id128_t *id); +int sd_bus_get_scope(sd_bus *bus, const char **scope); int sd_bus_get_tid(sd_bus *bus, pid_t *tid); +int sd_bus_get_owner_creds(sd_bus *bus, uint64_t creds_mask, sd_bus_creds **ret); int sd_bus_send(sd_bus *bus, sd_bus_message *m, uint64_t *cookie); int sd_bus_send_to(sd_bus *bus, sd_bus_message *m, const char *destination, uint64_t *cookie); @@ -147,6 +170,7 @@ int sd_bus_process(sd_bus *bus, sd_bus_message **r); int sd_bus_process_priority(sd_bus *bus, int64_t max_priority, sd_bus_message **r); int sd_bus_wait(sd_bus *bus, uint64_t timeout_usec); int sd_bus_flush(sd_bus *bus); + sd_bus_slot* sd_bus_get_current_slot(sd_bus *bus); sd_bus_message* sd_bus_get_current_message(sd_bus *bus); sd_bus_message_handler_t sd_bus_get_current_handler(sd_bus *bus); @@ -171,9 +195,10 @@ sd_bus_slot* sd_bus_slot_ref(sd_bus_slot *slot); sd_bus_slot* sd_bus_slot_unref(sd_bus_slot *slot); sd_bus* sd_bus_slot_get_bus(sd_bus_slot *slot); - void *sd_bus_slot_get_userdata(sd_bus_slot *slot); void *sd_bus_slot_set_userdata(sd_bus_slot *slot, void *userdata); +int sd_bus_slot_set_description(sd_bus_slot *slot, const char *description); +int sd_bus_slot_get_description(sd_bus_slot *slot, char **description); sd_bus_message* sd_bus_slot_get_current_message(sd_bus_slot *slot); sd_bus_message_handler_t sd_bus_slot_get_current_handler(sd_bus_slot *bus); @@ -220,6 +245,8 @@ sd_bus_creds *sd_bus_message_get_creds(sd_bus_message *m); /* do not unref the r int sd_bus_message_is_signal(sd_bus_message *m, const char *interface, const char *member); int sd_bus_message_is_method_call(sd_bus_message *m, const char *interface, const char *member); int sd_bus_message_is_method_error(sd_bus_message *m, const char *name); +int sd_bus_message_is_empty(sd_bus_message *m); +int sd_bus_message_has_signature(sd_bus_message *m, const char *signature); int sd_bus_message_set_expect_reply(sd_bus_message *m, int b); int sd_bus_message_set_auto_start(sd_bus_message *m, int b); @@ -233,10 +260,10 @@ int sd_bus_message_append_basic(sd_bus_message *m, char type, const void *p); int sd_bus_message_append_array(sd_bus_message *m, char type, const void *ptr, size_t size); int sd_bus_message_append_array_space(sd_bus_message *m, char type, size_t size, void **ptr); int sd_bus_message_append_array_iovec(sd_bus_message *m, char type, const struct iovec *iov, unsigned n); -int sd_bus_message_append_array_memfd(sd_bus_message *m, char type, int memfd); +int sd_bus_message_append_array_memfd(sd_bus_message *m, char type, int memfd, uint64_t offset, uint64_t size); int sd_bus_message_append_string_space(sd_bus_message *m, size_t size, char **s); int sd_bus_message_append_string_iovec(sd_bus_message *m, const struct iovec *iov, unsigned n); -int sd_bus_message_append_string_memfd(sd_bus_message *m, int memfd); +int sd_bus_message_append_string_memfd(sd_bus_message *m, int memfd, uint64_t offset, uint64_t size); int sd_bus_message_append_strv(sd_bus_message *m, char **l); int sd_bus_message_open_container(sd_bus_message *m, char type, const char *contents); int sd_bus_message_close_container(sd_bus_message *m); @@ -299,10 +326,16 @@ sd_bus_creds *sd_bus_creds_unref(sd_bus_creds *c); uint64_t sd_bus_creds_get_mask(const sd_bus_creds *c); int sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid); -int sd_bus_creds_get_pid_starttime(sd_bus_creds *c, uint64_t *usec); int sd_bus_creds_get_tid(sd_bus_creds *c, pid_t *tid); int sd_bus_creds_get_uid(sd_bus_creds *c, uid_t *uid); +int sd_bus_creds_get_euid(sd_bus_creds *c, uid_t *euid); +int sd_bus_creds_get_suid(sd_bus_creds *c, uid_t *suid); +int sd_bus_creds_get_fsuid(sd_bus_creds *c, uid_t *fsuid); int sd_bus_creds_get_gid(sd_bus_creds *c, gid_t *gid); +int sd_bus_creds_get_egid(sd_bus_creds *c, gid_t *egid); +int sd_bus_creds_get_sgid(sd_bus_creds *c, gid_t *sgid); +int sd_bus_creds_get_fsgid(sd_bus_creds *c, gid_t *fsgid); +int sd_bus_creds_get_supplementary_gids(sd_bus_creds *c, const gid_t **gids); int sd_bus_creds_get_comm(sd_bus_creds *c, const char **comm); int sd_bus_creds_get_tid_comm(sd_bus_creds *c, const char **comm); int sd_bus_creds_get_exe(sd_bus_creds *c, const char **exe); @@ -322,7 +355,7 @@ int sd_bus_creds_get_audit_session_id(sd_bus_creds *c, uint32_t *sessionid); int sd_bus_creds_get_audit_login_uid(sd_bus_creds *c, uid_t *loginuid); int sd_bus_creds_get_unique_name(sd_bus_creds *c, const char **name); int sd_bus_creds_get_well_known_names(sd_bus_creds *c, char ***names); -int sd_bus_creds_get_connection_name(sd_bus_creds *c, const char **name); +int sd_bus_creds_get_description(sd_bus_creds *c, const char **name); /* Error structures */ @@ -340,6 +373,19 @@ int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e); int sd_bus_error_is_set(const sd_bus_error *e); int sd_bus_error_has_name(const sd_bus_error *e, const char *name); +#define SD_BUS_ERROR_MAP(_name, _code) \ + { \ + .name = _name, \ + .code = _code, \ + } +#define SD_BUS_ERROR_MAP_END \ + { \ + .name = NULL, \ + .code = - 'x', \ + } + +int sd_bus_error_add_map(const sd_bus_error_map *map); + /* Auxiliary macros */ #define SD_BUS_MESSAGE_APPEND_ID128(x) 16, \ diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h index 7416f82193..951662e56c 100644 --- a/src/systemd/sd-dhcp-client.h +++ b/src/systemd/sd-dhcp-client.h @@ -51,6 +51,10 @@ int sd_dhcp_client_set_request_broadcast(sd_dhcp_client *client, int broadcast); int sd_dhcp_client_set_index(sd_dhcp_client *client, int interface_index); int sd_dhcp_client_set_mac(sd_dhcp_client *client, const uint8_t *addr, size_t addr_len, uint16_t arp_type); +int sd_dhcp_client_set_client_id(sd_dhcp_client *client, uint8_t type, + const uint8_t *data, size_t data_len); +int sd_dhcp_client_get_client_id(sd_dhcp_client *client, uint8_t *type, + const uint8_t **data, size_t *data_len); int sd_dhcp_client_set_mtu(sd_dhcp_client *client, uint32_t mtu); int sd_dhcp_client_set_hostname(sd_dhcp_client *client, const char *hostname); int sd_dhcp_client_set_vendor_class_identifier(sd_dhcp_client *client, const char *vci); diff --git a/src/systemd/sd-dhcp-lease.h b/src/systemd/sd-dhcp-lease.h index a3728a702f..4296b91d8a 100644 --- a/src/systemd/sd-dhcp-lease.h +++ b/src/systemd/sd-dhcp-lease.h @@ -45,5 +45,10 @@ int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname); int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname); int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path); int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, struct sd_dhcp_route **routesgn); +int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const uint8_t **client_id, + size_t *client_id_len); + +int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file); +int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file); #endif diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h index c7f168fe21..e9663c0c71 100644 --- a/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/sd-dhcp6-client.h @@ -33,6 +33,7 @@ enum { DHCP6_EVENT_RESEND_EXPIRE = 10, DHCP6_EVENT_RETRANS_MAX = 11, DHCP6_EVENT_IP_ACQUIRE = 12, + DHCP6_EVENT_INFORMATION_REQUEST = 13, }; typedef struct sd_dhcp6_client sd_dhcp6_client; @@ -47,6 +48,10 @@ int sd_dhcp6_client_set_mac(sd_dhcp6_client *client, const uint8_t *addr, size_t addr_len, uint16_t arp_type); int sd_dhcp6_client_set_duid(sd_dhcp6_client *client, uint16_t type, uint8_t *duid, size_t duid_len); +int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, + bool enabled); +int sd_dhcp6_client_get_information_request(sd_dhcp6_client *client, + bool *enabled); int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option); diff --git a/src/systemd/sd-event.h b/src/systemd/sd-event.h index 0dbdcdf2a5..25a10f99ab 100644 --- a/src/systemd/sd-event.h +++ b/src/systemd/sd-event.h @@ -109,8 +109,8 @@ sd_event *sd_event_source_get_event(sd_event_source *s); void* sd_event_source_get_userdata(sd_event_source *s); void* sd_event_source_set_userdata(sd_event_source *s, void *userdata); -int sd_event_source_set_name(sd_event_source *s, const char *name); -int sd_event_source_get_name(sd_event_source *s, const char **name); +int sd_event_source_set_description(sd_event_source *s, const char *description); +int sd_event_source_get_description(sd_event_source *s, const char **description); int sd_event_source_set_prepare(sd_event_source *s, sd_event_handler_t callback); int sd_event_source_get_pending(sd_event_source *s); int sd_event_source_get_priority(sd_event_source *s, int64_t *priority); diff --git a/src/systemd/sd-pppoe.h b/src/systemd/sd-pppoe.h new file mode 100644 index 0000000000..318d2f033b --- /dev/null +++ b/src/systemd/sd-pppoe.h @@ -0,0 +1,53 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foosdpppoefoo +#define foosdpppoefoo + +/*** + This file is part of systemd. + + Copyright (C) 2014 Tom Gundersen + + 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 <stdbool.h> +#include <net/ethernet.h> + +#include "sd-event.h" + +#include "sparse-endian.h" + +enum { + PPPOE_EVENT_RUNNING = 0, + PPPOE_EVENT_STOPPED = 1, +}; + +typedef struct sd_pppoe sd_pppoe; +typedef void (*sd_pppoe_cb_t)(sd_pppoe *ppp, int event, void *userdata); + +int sd_pppoe_detach_event(sd_pppoe *ppp); +int sd_pppoe_attach_event(sd_pppoe *ppp, sd_event *event, int priority); +int sd_pppoe_get_channel(sd_pppoe *ppp, int *channel); +int sd_pppoe_set_callback(sd_pppoe *ppp, sd_pppoe_cb_t cb, void *userdata); +int sd_pppoe_set_ifindex(sd_pppoe *ppp, int ifindex); +int sd_pppoe_set_ifname(sd_pppoe *ppp, const char *ifname); +int sd_pppoe_set_service_name(sd_pppoe *ppp, const char *service_name); +int sd_pppoe_start(sd_pppoe *ppp); +int sd_pppoe_stop(sd_pppoe *ppp); +sd_pppoe *sd_pppoe_ref(sd_pppoe *ppp); +sd_pppoe *sd_pppoe_unref(sd_pppoe *ppp); +int sd_pppoe_new (sd_pppoe **ret); + +#endif diff --git a/src/systemd/sd-rtnl.h b/src/systemd/sd-rtnl.h index 1e7eb811d1..b05f83ce41 100644 --- a/src/systemd/sd-rtnl.h +++ b/src/systemd/sd-rtnl.h @@ -26,6 +26,7 @@ #include <netinet/in.h> #include <netinet/ether.h> #include <linux/rtnetlink.h> +#include <linux/neighbour.h> #include "sd-event.h" #include "_sd-common.h" @@ -41,6 +42,7 @@ typedef int (*sd_rtnl_message_handler_t)(sd_rtnl *rtnl, sd_rtnl_message *m, void /* bus */ int sd_rtnl_open(sd_rtnl **nl, unsigned n_groups, ...); +int sd_rtnl_inc_rcvbuf(const sd_rtnl *const rtnl, const int size); sd_rtnl *sd_rtnl_ref(sd_rtnl *nl); sd_rtnl *sd_rtnl_unref(sd_rtnl *nl); @@ -73,11 +75,13 @@ int sd_rtnl_message_new_addr(sd_rtnl *rtnl, sd_rtnl_message **ret, uint16_t msg_ int family); int sd_rtnl_message_new_route(sd_rtnl *rtnl, sd_rtnl_message **ret, uint16_t nlmsg_type, int rtm_family, unsigned char rtm_protocol); +int sd_rtnl_message_new_neigh(sd_rtnl *rtnl, sd_rtnl_message **ret, uint16_t msg_type, int index, int nda_family); sd_rtnl_message *sd_rtnl_message_ref(sd_rtnl_message *m); sd_rtnl_message *sd_rtnl_message_unref(sd_rtnl_message *m); int sd_rtnl_message_request_dump(sd_rtnl_message *m, int dump); +int sd_rtnl_message_is_error(sd_rtnl_message *m); int sd_rtnl_message_get_errno(sd_rtnl_message *m); int sd_rtnl_message_get_type(sd_rtnl_message *m, uint16_t *type); int sd_rtnl_message_is_broadcast(sd_rtnl_message *m); @@ -93,12 +97,20 @@ int sd_rtnl_message_addr_get_ifindex(sd_rtnl_message *m, int *ifindex); int sd_rtnl_message_link_set_flags(sd_rtnl_message *m, unsigned flags, unsigned change); int sd_rtnl_message_link_set_type(sd_rtnl_message *m, unsigned type); +int sd_rtnl_message_link_set_family(sd_rtnl_message *m, unsigned family); int sd_rtnl_message_link_get_ifindex(sd_rtnl_message *m, int *ifindex); int sd_rtnl_message_link_get_flags(sd_rtnl_message *m, unsigned *flags); int sd_rtnl_message_link_get_type(sd_rtnl_message *m, unsigned *type); int sd_rtnl_message_route_set_dst_prefixlen(sd_rtnl_message *m, unsigned char prefixlen); +int sd_rtnl_message_route_set_src_prefixlen(sd_rtnl_message *m, unsigned char prefixlen); int sd_rtnl_message_route_set_scope(sd_rtnl_message *m, unsigned char scope); +int sd_rtnl_message_route_get_family(sd_rtnl_message *m, int *family); +int sd_rtnl_message_route_get_dst_prefixlen(sd_rtnl_message *m, unsigned char *dst_len); +int sd_rtnl_message_route_get_src_prefixlen(sd_rtnl_message *m, unsigned char *src_len); + +int sd_rtnl_message_neigh_get_family(sd_rtnl_message *m, int *family); +int sd_rtnl_message_neigh_get_ifindex(sd_rtnl_message *m, int *family); int sd_rtnl_message_append_string(sd_rtnl_message *m, unsigned short type, const char *data); int sd_rtnl_message_append_u8(sd_rtnl_message *m, unsigned short type, uint8_t data); diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index 9b9be96a0a..647eb577a6 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -67,15 +67,7 @@ typedef struct Item { static char *arg_root = NULL; -static const char conf_file_dirs[] = - "/etc/sysusers.d\0" - "/run/sysusers.d\0" - "/usr/local/lib/sysusers.d\0" - "/usr/lib/sysusers.d\0" -#ifdef HAVE_SPLIT_USR - "/lib/sysusers.d\0" -#endif - ; +static const char conf_file_dirs[] = CONF_DIRS_NULSTR("sysusers"); static Hashmap *users = NULL, *groups = NULL; static Hashmap *todo_uids = NULL, *todo_gids = NULL; @@ -84,7 +76,7 @@ static Hashmap *members = NULL; static Hashmap *database_uid = NULL, *database_user = NULL; static Hashmap *database_gid = NULL, *database_group = NULL; -static uid_t search_uid = (uid_t) -1; +static uid_t search_uid = UID_INVALID; static UidRange *uid_range = NULL; static unsigned n_uid_range = 0; @@ -234,14 +226,15 @@ static int make_backup(const char *target, const char *x) { /* Copy over the access mask */ if (fchmod(fileno(dst), st.st_mode & 07777) < 0) - log_warning("Failed to change mode on %s: %m", backup); + log_warning_errno(errno, "Failed to change mode on %s: %m", backup); if (fchown(fileno(dst), st.st_uid, st.st_gid)< 0) - log_warning("Failed to change ownership of %s: %m", backup); + log_warning_errno(errno, "Failed to change ownership of %s: %m", backup); ts[0] = st.st_atim; ts[1] = st.st_mtim; - futimens(fileno(dst), ts); + if (futimens(fileno(dst), ts) < 0) + log_warning_errno(errno, "Failed to fix access and modification time of %s: %m", backup); if (rename(temp, backup) < 0) goto fail; @@ -353,6 +346,21 @@ static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) { return 0; } +static int sync_rights(FILE *from, FILE *to) { + struct stat st; + + if (fstat(fileno(from), &st) < 0) + return -errno; + + if (fchmod(fileno(to), st.st_mode & 07777) < 0) + return -errno; + + if (fchown(fileno(to), st.st_uid, st.st_gid) < 0) + return -errno; + + return 0; +} + static int write_files(void) { _cleanup_fclose_ FILE *passwd = NULL, *group = NULL, *shadow = NULL, *gshadow = NULL; @@ -372,15 +380,14 @@ static int write_files(void) { if (r < 0) goto finish; - if (fchmod(fileno(group), 0644) < 0) { - r = -errno; - goto finish; - } - original = fopen(group_path, "re"); if (original) { struct group *gr; + r = sync_rights(original, group); + if (r < 0) + goto finish; + errno = 0; while ((gr = fgetgrent(original))) { /* Safety checks against name and GID @@ -418,6 +425,9 @@ static int write_files(void) { } else if (errno != ENOENT) { r = -errno; goto finish; + } else if (fchmod(fileno(group), 0644) < 0) { + r = -errno; + goto finish; } HASHMAP_FOREACH(i, todo_gids, iterator) { @@ -449,15 +459,14 @@ static int write_files(void) { if (r < 0) goto finish; - if (fchmod(fileno(gshadow), 0000) < 0) { - r = -errno; - goto finish; - } - original = fopen(gshadow_path, "re"); if (original) { struct sgrp *sg; + r = sync_rights(original, gshadow); + if (r < 0) + goto finish; + errno = 0; while ((sg = fgetsgent(original))) { @@ -483,6 +492,9 @@ static int write_files(void) { } else if (errno != ENOENT) { r = -errno; goto finish; + } else if (fchmod(fileno(gshadow), 0000) < 0) { + r = -errno; + goto finish; } HASHMAP_FOREACH(i, todo_gids, iterator) { @@ -513,15 +525,14 @@ static int write_files(void) { if (r < 0) goto finish; - if (fchmod(fileno(passwd), 0644) < 0) { - r = -errno; - goto finish; - } - original = fopen(passwd_path, "re"); if (original) { struct passwd *pw; + r = sync_rights(original, passwd); + if (r < 0) + goto finish; + errno = 0; while ((pw = fgetpwent(original))) { @@ -552,6 +563,9 @@ static int write_files(void) { } else if (errno != ENOENT) { r = -errno; goto finish; + } else if (fchmod(fileno(passwd), 0644) < 0) { + r = -errno; + goto finish; } HASHMAP_FOREACH(i, todo_uids, iterator) { @@ -596,15 +610,14 @@ static int write_files(void) { if (r < 0) goto finish; - if (fchmod(fileno(shadow), 0000) < 0) { - r = -errno; - goto finish; - } - original = fopen(shadow_path, "re"); if (original) { struct spwd *sp; + r = sync_rights(original, shadow); + if (r < 0) + goto finish; + errno = 0; while ((sp = fgetspent(original))) { @@ -629,6 +642,9 @@ static int write_files(void) { } else if (errno != ENOENT) { r = -errno; goto finish; + } else if (fchmod(fileno(shadow), 0000) < 0) { + r = -errno; + goto finish; } lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY); @@ -882,10 +898,8 @@ static int add_user(Item *i) { i->description = strdup(p->pw_gecos); return 0; } - if (!IN_SET(errno, 0, ENOENT)) { - log_error("Failed to check if user %s already exists: %m", i->name); - return -errno; - } + if (!IN_SET(errno, 0, ENOENT)) + return log_error_errno(errno, "Failed to check if user %s already exists: %m", i->name); /* And shadow too, just to be sure */ errno = 0; @@ -894,19 +908,15 @@ static int add_user(Item *i) { log_error("User %s already exists in shadow database, but not in user database.", i->name); return -EBADMSG; } - if (!IN_SET(errno, 0, ENOENT)) { - log_error("Failed to check if user %s already exists in shadow database: %m", i->name); - return -errno; - } + if (!IN_SET(errno, 0, ENOENT)) + return log_error_errno(errno, "Failed to check if user %s already exists in shadow database: %m", i->name); } /* Try to use the suggested numeric uid */ if (i->uid_set) { r = uid_is_ok(i->uid, i->name); - if (r < 0) { - log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid); if (r == 0) { log_debug("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name); i->uid_set = false; @@ -923,10 +933,9 @@ static int add_user(Item *i) { log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name); else { r = uid_is_ok(c, i->name); - if (r < 0) { - log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r)); - return r; - } else if (r > 0) { + if (r < 0) + return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid); + else if (r > 0) { i->uid = c; i->uid_set = true; } else @@ -938,10 +947,8 @@ static int add_user(Item *i) { /* Otherwise try to reuse the group ID */ if (!i->uid_set && i->gid_set) { r = uid_is_ok((uid_t) i->gid, i->name); - if (r < 0) { - log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid); if (r > 0) { i->uid = (uid_t) i->gid; i->uid_set = true; @@ -958,10 +965,9 @@ static int add_user(Item *i) { } r = uid_is_ok(search_uid, i->name); - if (r < 0) { - log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r)); - return r; - } else if (r > 0) + if (r < 0) + return log_error_errno(r, "Failed to verify uid " UID_FMT ": %m", i->uid); + else if (r > 0) break; } @@ -1046,19 +1052,15 @@ static int add_group(Item *i) { i->gid_set = true; return 0; } - if (!IN_SET(errno, 0, ENOENT)) { - log_error("Failed to check if group %s already exists: %m", i->name); - return -errno; - } + if (!IN_SET(errno, 0, ENOENT)) + return log_error_errno(errno, "Failed to check if group %s already exists: %m", i->name); } /* Try to use the suggested numeric gid */ if (i->gid_set) { r = gid_is_ok(i->gid); - if (r < 0) { - log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid); if (r == 0) { log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name); i->gid_set = false; @@ -1068,10 +1070,8 @@ static int add_group(Item *i) { /* Try to reuse the numeric uid, if there's one */ if (!i->gid_set && i->uid_set) { r = gid_is_ok((gid_t) i->uid); - if (r < 0) { - log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid); if (r > 0) { i->gid = (gid_t) i->uid; i->gid_set = true; @@ -1088,10 +1088,9 @@ static int add_group(Item *i) { log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name); else { r = gid_is_ok(c); - if (r < 0) { - log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r)); - return r; - } else if (r > 0) { + if (r < 0) + return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid); + else if (r > 0) { i->gid = c; i->gid_set = true; } else @@ -1111,10 +1110,9 @@ static int add_group(Item *i) { } r = gid_is_ok(search_uid); - if (r < 0) { - log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r)); - return r; - } else if (r > 0) + if (r < 0) + return log_error_errno(r, "Failed to verify gid " GID_FMT ": %m", i->gid); + else if (r > 0) break; } @@ -1705,8 +1703,7 @@ static int read_config_file(const char *fn, bool ignore_enoent) { if (ignore_enoent && r == -ENOENT) return 0; - log_error("Failed to open '%s', ignoring: %s", fn, strerror(-r)); - return r; + return log_error_errno(r, "Failed to open '%s', ignoring: %m", fn); } f = rf; @@ -1728,7 +1725,7 @@ static int read_config_file(const char *fn, bool ignore_enoent) { } if (ferror(f)) { - log_error("Failed to read from file %s: %m", fn); + log_error_errno(errno, "Failed to read from file %s: %m", fn); if (r == 0) r = -EIO; } @@ -1837,7 +1834,7 @@ int main(int argc, char *argv[]) { r = mac_selinux_init(NULL); if (r < 0) { - log_error("SELinux setup failed: %s", strerror(-r)); + log_error_errno(r, "SELinux setup failed: %m"); goto finish; } @@ -1855,7 +1852,7 @@ int main(int argc, char *argv[]) { r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs); if (r < 0) { - log_error("Failed to enumerate sysusers.d files: %s", strerror(-r)); + log_error_errno(r, "Failed to enumerate sysusers.d files: %m"); goto finish; } @@ -1881,19 +1878,19 @@ int main(int argc, char *argv[]) { lock = take_password_lock(arg_root); if (lock < 0) { - log_error("Failed to take lock: %s", strerror(-lock)); + log_error_errno(lock, "Failed to take lock: %m"); goto finish; } r = load_user_database(); if (r < 0) { - log_error("Failed to load user database: %s", strerror(-r)); + log_error_errno(r, "Failed to load user database: %m"); goto finish; } r = load_group_database(); if (r < 0) { - log_error("Failed to read group database: %s", strerror(-r)); + log_error_errno(r, "Failed to read group database: %m"); goto finish; } @@ -1905,7 +1902,7 @@ int main(int argc, char *argv[]) { r = write_files(); if (r < 0) - log_error("Failed to write files: %s", strerror(-r)); + log_error_errno(r, "Failed to write files: %m"); finish: while ((i = hashmap_steal_first(groups))) diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c index 6e4cdd694e..45c8b4ea0f 100644 --- a/src/sysv-generator/sysv-generator.c +++ b/src/sysv-generator/sysv-generator.c @@ -143,14 +143,13 @@ static int generate_unit_file(SysvStub *s) { return log_oom(); f = fopen(unit, "wxe"); - if (!f) { - log_error("Failed to create unit file %s: %m", unit); - return -errno; - } + if (!f) + return log_error_errno(errno, "Failed to create unit file %s: %m", unit); fprintf(f, "# Automatically generated by systemd-sysv-generator\n\n" "[Unit]\n" + "Documentation=man:systemd-sysv-generator(8)\n" "SourcePath=%s\n" "Description=%s\n", s->path, s->description); @@ -175,9 +174,6 @@ static int generate_unit_file(SysvStub *s) { "RemainAfterExit=%s\n", yes_no(!s->pid_file)); - if (s->sysv_start_priority > 0) - fprintf(f, "SysVStartPriority=%d\n", s->sysv_start_priority); - if (s->pid_file) fprintf(f, "PIDFile=%s\n", s->pid_file); @@ -192,7 +188,7 @@ static int generate_unit_file(SysvStub *s) { STRV_FOREACH(p, s->wanted_by) { r = add_symlink(s->name, *p); if (r < 0) - log_error_unit(s->name, "Failed to create 'Wants' symlink to %s: %s", *p, strerror(-r)); + log_unit_error_errno(s->name, r, "Failed to create 'Wants' symlink to %s: %m", *p); } return 0; @@ -320,7 +316,7 @@ static int load_sysv(SysvStub *s) { if (feof(f)) break; - log_error_unit(s->name, + log_unit_error(s->name, "Failed to read configuration file '%s': %m", s->path); return -errno; @@ -395,7 +391,7 @@ static int load_sysv(SysvStub *s) { fn = strstrip(t+8); if (!path_is_absolute(fn)) { - log_error_unit(s->name, + log_unit_error(s->name, "[%s:%u] PID file not absolute. Ignoring.", s->path, line); continue; @@ -489,12 +485,12 @@ static int load_sysv(SysvStub *s) { } if (r < 0) - log_error_unit(s->name, + log_unit_error(s->name, "[%s:%u] Failed to add LSB Provides name %s, ignoring: %s", s->path, line, m, strerror(-r)); } if (!isempty(state_)) - log_error_unit(s->name, + log_unit_error(s->name, "[%s:%u] Trailing garbage in Provides, ignoring.", s->path, line); @@ -517,7 +513,7 @@ static int load_sysv(SysvStub *s) { r = sysv_translate_facility(n, basename(s->path), &m); if (r < 0) { - log_error_unit(s->name, + log_unit_error(s->name, "[%s:%u] Failed to translate LSB dependency %s, ignoring: %s", s->path, line, n, strerror(-r)); continue; @@ -551,12 +547,12 @@ static int load_sysv(SysvStub *s) { } if (r < 0) - log_error_unit(s->name, + log_unit_error(s->name, "[%s:%u] Failed to add dependency on %s, ignoring: %s", s->path, line, m, strerror(-r)); } if (!isempty(state_)) - log_error_unit(s->name, + log_unit_error(s->name, "[%s:%u] Trailing garbage in %*s, ignoring.", s->path, line, (int)(strchr(t, ':') - t), t); @@ -698,7 +694,7 @@ static int enumerate_sysv(LookupPaths lp, Hashmap *all_services) { d = opendir(*path); if (!d) { if (errno != ENOENT) - log_warning("opendir(%s) failed: %m", *path); + log_warning_errno(errno, "opendir(%s) failed: %m", *path); continue; } @@ -773,7 +769,7 @@ static int set_dependencies_from_rcnd(LookupPaths lp, Hashmap *all_services) { d = opendir(path); if (!d) { if (errno != ENOENT) - log_warning("opendir(%s) failed: %m", path); + log_warning_errno(errno, "opendir(%s) failed: %m", path); continue; } diff --git a/src/test/test-architecture.c b/src/test/test-architecture.c index 24217ad369..30bdec45e5 100644 --- a/src/test/test-architecture.c +++ b/src/test/test-architecture.c @@ -25,9 +25,8 @@ #include "log.h" int main(int argc, char *argv[]) { - Architecture a; - int v; const char *id = NULL; + int a, v; v = detect_virtualization(&id); if (v == -EPERM || v == -EACCES) diff --git a/src/test/test-boot-timestamps.c b/src/test/test-boot-timestamps.c index 4ede318e38..06d93af533 100644 --- a/src/test/test-boot-timestamps.c +++ b/src/test/test-boot-timestamps.c @@ -37,7 +37,7 @@ static int test_acpi_fpdt(void) { r = acpi_get_boot_usec(&loader_start, &loader_exit); if (r < 0) { if (r != -ENOENT) - log_error("Failed to read ACPI FPDT: %s", strerror(-r)); + log_error_errno(r, "Failed to read ACPI FPDT: %m"); return r; } @@ -60,7 +60,7 @@ static int test_efi_loader(void) { r = efi_loader_get_boot_usec(&loader_start, &loader_exit); if (r < 0) { if (r != -ENOENT) - log_error("Failed to read EFI loader data: %s", strerror(-r)); + log_error_errno(r, "Failed to read EFI loader data: %m"); return r; } @@ -84,7 +84,7 @@ int main(int argc, char* argv[]) { r = boot_timestamps(NULL, &fw, &l); if (r < 0) { - log_error("Failed to read variables: %s", strerror(-r)); + log_error_errno(r, "Failed to read variables: %m"); return 1; } diff --git a/src/test/test-cap-list.c b/src/test/test-cap-list.c new file mode 100644 index 0000000000..dfa9a063c2 --- /dev/null +++ b/src/test/test-cap-list.c @@ -0,0 +1,47 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2014 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 "log.h" +#include "cap-list.h" +#include "capability.h" + +int main(int argc, char *argv[]) { + int i; + + assert_se(!capability_to_name(-1)); + assert_se(!capability_to_name(cap_last_cap()+1)); + + for (i = 0; i <= (int) cap_last_cap(); i++) { + const char *n; + + assert_se(n = capability_to_name(i)); + assert_se(capability_from_name(n) == i); + printf("%s = %i\n", n, i); + } + + assert_se(capability_from_name("asdfbsd") == -EINVAL); + assert_se(capability_from_name("CAP_AUDIT_READ") == CAP_AUDIT_READ); + assert_se(capability_from_name("0") == 0); + assert_se(capability_from_name("15") == 15); + assert_se(capability_from_name("-1") == -EINVAL); + + return 0; +} diff --git a/src/test/test-capability.c b/src/test/test-capability.c index a362fc6c57..43769923b0 100644 --- a/src/test/test-capability.c +++ b/src/test/test-capability.c @@ -70,7 +70,7 @@ static int setup_tests(void) { nobody = getpwnam("nobody"); if (!nobody) { - log_error("Could not find nobody user: %m"); + log_error_errno(errno, "Could not find nobody user: %m"); return -EXIT_TEST_SKIP; } test_uid = nobody->pw_uid; diff --git a/src/test/test-cgroup-util.c b/src/test/test-cgroup-util.c index 68c526ae82..58eb744277 100644 --- a/src/test/test-cgroup-util.c +++ b/src/test/test-cgroup-util.c @@ -141,7 +141,7 @@ static void test_proc(void) { FOREACH_DIRENT(de, d, break) { _cleanup_free_ char *path = NULL, *path_shifted = NULL, *session = NULL, *unit = NULL, *user_unit = NULL, *machine = NULL, *slice = NULL; pid_t pid; - uid_t uid = (uid_t) -1; + uid_t uid = UID_INVALID; if (de->d_type != DT_DIR && de->d_type != DT_UNKNOWN) diff --git a/src/test/test-condition-util.c b/src/test/test-condition-util.c deleted file mode 100644 index 35ee9167bf..0000000000 --- a/src/test/test-condition-util.c +++ /dev/null @@ -1,107 +0,0 @@ -/*** - This file is part of systemd - - Copyright 2014 Ronny Chevalier - - 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 "condition-util.h" -#include "macro.h" -#include "util.h" -#include "log.h" -#include "architecture.h" -#include "systemd/sd-id128.h" - -static void test_condition_test_ac_power(void) { - Condition *condition; - - condition = condition_new(CONDITION_AC_POWER, "true", false, false); - assert_se(condition_test_ac_power(condition) == on_ac_power()); - condition_free(condition); - - condition = condition_new(CONDITION_AC_POWER, "false", false, false); - assert_se(condition_test_ac_power(condition) != on_ac_power()); - condition_free(condition); - - condition = condition_new(CONDITION_AC_POWER, "false", false, true); - assert_se(condition_test_ac_power(condition) == on_ac_power()); - condition_free(condition); -} - -static void test_condition_test_host(void) { - Condition *condition; - sd_id128_t id; - int r; - char sid[SD_ID128_STRING_MAX]; - _cleanup_free_ char *hostname = NULL; - - r = sd_id128_get_machine(&id); - assert_se(r >= 0); - assert_se(sd_id128_to_string(id, sid)); - - condition = condition_new(CONDITION_HOST, sid, false, false); - assert_se(condition_test_host(condition)); - condition_free(condition); - - condition = condition_new(CONDITION_HOST, "garbage value jjjjjjjjjjjjjj", false, false); - assert_se(!condition_test_host(condition)); - condition_free(condition); - - condition = condition_new(CONDITION_HOST, sid, false, true); - assert_se(!condition_test_host(condition)); - condition_free(condition); - - hostname = gethostname_malloc(); - assert_se(hostname); - - condition = condition_new(CONDITION_HOST, hostname, false, false); - assert_se(condition_test_host(condition)); - condition_free(condition); -} - -static void test_condition_test_architecture(void) { - Condition *condition; - Architecture a; - const char *sa; - - a = uname_architecture(); - assert_se(a >= 0); - - sa = architecture_to_string(a); - assert_se(sa); - - condition = condition_new(CONDITION_ARCHITECTURE, sa, false, false); - assert_se(condition_test_architecture(condition)); - condition_free(condition); - - condition = condition_new(CONDITION_ARCHITECTURE, "garbage value", false, false); - assert_se(!condition_test_architecture(condition)); - condition_free(condition); - - condition = condition_new(CONDITION_ARCHITECTURE, sa, false, true); - assert_se(!condition_test_architecture(condition)); - condition_free(condition); -} - -int main(int argc, char *argv[]) { - log_parse_environment(); - log_open(); - - test_condition_test_ac_power(); - test_condition_test_host(); - test_condition_test_architecture(); - - return 0; -} diff --git a/src/test/test-condition.c b/src/test/test-condition.c new file mode 100644 index 0000000000..349c6470c3 --- /dev/null +++ b/src/test/test-condition.c @@ -0,0 +1,194 @@ +/*** + This file is part of systemd + + Copyright 2014 Ronny Chevalier + + 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 "condition.h" +#include "macro.h" +#include "util.h" +#include "log.h" +#include "architecture.h" +#include "sd-id128.h" + +static void test_condition_test_path_exists(void) { + Condition *condition; + + condition = condition_new(CONDITION_PATH_EXISTS, "/bin/sh", false, false); + assert_se(condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_PATH_EXISTS, "/bin/s?", false, false); + assert_se(!condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_PATH_EXISTS_GLOB, "/bin/s?", false, false); + assert_se(condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_PATH_EXISTS_GLOB, "/bin/s?", false, true); + assert_se(!condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_PATH_EXISTS, "/thiscertainlywontexist", false, false); + assert_se(!condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_PATH_EXISTS, "/thiscertainlywontexist", false, true); + assert_se(condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_PATH_IS_DIRECTORY, "/bin", false, false); + assert_se(condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_DIRECTORY_NOT_EMPTY, "/bin", false, false); + assert_se(condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_FILE_NOT_EMPTY, "/bin/sh", false, false); + assert_se(condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_FILE_IS_EXECUTABLE, "/bin/sh", false, false); + assert_se(condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_FILE_IS_EXECUTABLE, "/etc/passwd", false, false); + assert_se(!condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/proc", false, false); + assert_se(condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/", false, false); + assert_se(condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/bin", false, false); + assert_se(!condition_test(condition)); + condition_free(condition); +} + +static void test_condition_test_ac_power(void) { + Condition *condition; + + condition = condition_new(CONDITION_AC_POWER, "true", false, false); + assert_se(condition_test(condition) == on_ac_power()); + condition_free(condition); + + condition = condition_new(CONDITION_AC_POWER, "false", false, false); + assert_se(condition_test(condition) != on_ac_power()); + condition_free(condition); + + condition = condition_new(CONDITION_AC_POWER, "false", false, true); + assert_se(condition_test(condition) == on_ac_power()); + condition_free(condition); +} + +static void test_condition_test_host(void) { + Condition *condition; + sd_id128_t id; + int r; + char sid[SD_ID128_STRING_MAX]; + _cleanup_free_ char *hostname = NULL; + + r = sd_id128_get_machine(&id); + assert_se(r >= 0); + assert_se(sd_id128_to_string(id, sid)); + + condition = condition_new(CONDITION_HOST, sid, false, false); + assert_se(condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_HOST, "garbage value jjjjjjjjjjjjjj", false, false); + assert_se(!condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_HOST, sid, false, true); + assert_se(!condition_test(condition)); + condition_free(condition); + + hostname = gethostname_malloc(); + assert_se(hostname); + + condition = condition_new(CONDITION_HOST, hostname, false, false); + assert_se(condition_test(condition)); + condition_free(condition); +} + +static void test_condition_test_architecture(void) { + Condition *condition; + const char *sa; + int a; + + a = uname_architecture(); + assert_se(a >= 0); + + sa = architecture_to_string(a); + assert_se(sa); + + condition = condition_new(CONDITION_ARCHITECTURE, sa, false, false); + assert_se(condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_ARCHITECTURE, "garbage value", false, false); + assert_se(condition_test(condition) < 0); + condition_free(condition); + + condition = condition_new(CONDITION_ARCHITECTURE, sa, false, true); + assert_se(!condition_test(condition)); + condition_free(condition); +} + +static void test_condition_test_kernel_command_line(void) { + Condition *condition; + + condition = condition_new(CONDITION_KERNEL_COMMAND_LINE, "thisreallyshouldntbeonthekernelcommandline", false, false); + assert_se(!condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_KERNEL_COMMAND_LINE, "andthis=neither", false, false); + assert_se(!condition_test(condition)); + condition_free(condition); +} + +static void test_condition_test_null(void) { + Condition *condition; + + condition = condition_new(CONDITION_NULL, NULL, false, false); + assert_se(condition_test(condition)); + condition_free(condition); + + condition = condition_new(CONDITION_NULL, NULL, false, true); + assert_se(!condition_test(condition)); + condition_free(condition); +} + +int main(int argc, char *argv[]) { + log_parse_environment(); + log_open(); + + test_condition_test_path_exists(); + test_condition_test_ac_power(); + test_condition_test_host(); + test_condition_test_architecture(); + test_condition_test_kernel_command_line(); + test_condition_test_null(); + + return 0; +} diff --git a/src/test/test-conf-files.c b/src/test/test-conf-files.c index e801c5989e..71cfc022dd 100644 --- a/src/test/test-conf-files.c +++ b/src/test/test-conf-files.c @@ -36,7 +36,7 @@ static void setup_test_dir(char *tmp_dir, const char *files, ...) { va_start(ap, files); while (files != NULL) { _cleanup_free_ char *path = strappend(tmp_dir, files); - assert_se(touch_file(path, true, (usec_t) -1, (uid_t) -1, (gid_t) -1, 0) == 0); + assert_se(touch_file(path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0) == 0); files = va_arg(ap, const char *); } va_end(ap); diff --git a/src/test/test-copy.c b/src/test/test-copy.c new file mode 100644 index 0000000000..d2cad08cb6 --- /dev/null +++ b/src/test/test-copy.c @@ -0,0 +1,141 @@ +/*** + This file is part of systemd + + Copyright 2014 Ronny Chevalier + + 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 <unistd.h> + +#include "copy.h" +#include "path-util.h" +#include "fileio.h" +#include "mkdir.h" +#include "strv.h" +#include "macro.h" +#include "util.h" + +static void test_copy_file(void) { + _cleanup_free_ char *buf = NULL; + char fn[] = "/tmp/test-copy_file.XXXXXX"; + char fn_copy[] = "/tmp/test-copy_file.XXXXXX"; + size_t sz = 0; + int fd; + + fd = mkostemp_safe(fn, O_RDWR|O_CLOEXEC); + assert_se(fd >= 0); + close(fd); + + fd = mkostemp_safe(fn_copy, O_RDWR|O_CLOEXEC); + assert_se(fd >= 0); + close(fd); + + assert_se(write_string_file(fn, "foo bar bar bar foo") == 0); + + assert_se(copy_file(fn, fn_copy, 0, 0644) == 0); + + assert_se(read_full_file(fn_copy, &buf, &sz) == 0); + assert_se(streq(buf, "foo bar bar bar foo\n")); + assert_se(sz == 20); + + unlink(fn); + unlink(fn_copy); +} + +static void test_copy_file_fd(void) { + char in_fn[] = "/tmp/test-copy-file-fd-XXXXXX"; + char out_fn[] = "/tmp/test-copy-file-fd-XXXXXX"; + _cleanup_close_ int in_fd = -1, out_fd = -1; + char text[] = "boohoo\nfoo\n\tbar\n"; + char buf[64] = {0}; + + in_fd = mkostemp_safe(in_fn, O_RDWR); + assert_se(in_fd >= 0); + out_fd = mkostemp_safe(out_fn, O_RDWR); + assert_se(out_fd >= 0); + + assert_se(write_string_file(in_fn, text) == 0); + assert_se(copy_file_fd("/a/file/which/does/not/exist/i/guess", out_fd) < 0); + assert_se(copy_file_fd(in_fn, out_fd) >= 0); + assert_se(lseek(out_fd, SEEK_SET, 0) == 0); + + assert_se(read(out_fd, buf, sizeof(buf)) == sizeof(text) - 1); + assert_se(streq(buf, text)); + + unlink(in_fn); + unlink(out_fn); +} + +static void test_copy_tree(void) { + char original_dir[] = "/tmp/test-copy_tree/"; + char copy_dir[] = "/tmp/test-copy_tree-copy/"; + char **files = STRV_MAKE("file", "dir1/file", "dir1/dir2/file", "dir1/dir2/dir3/dir4/dir5/file"); + char **links = STRV_MAKE("link", "file", + "link2", "dir1/file"); + char **p, **link; + + rm_rf_dangerous(copy_dir, false, true, false); + rm_rf_dangerous(original_dir, false, true, false); + + STRV_FOREACH(p, files) { + char *f = strappenda(original_dir, *p); + + assert_se(mkdir_parents(f, 0755) >= 0); + assert_se(write_string_file(f, "file") == 0); + } + + STRV_FOREACH_PAIR(link, p, links) { + char *f = strappenda(original_dir, *p); + char *l = strappenda(original_dir, *link); + + assert_se(mkdir_parents(l, 0755) >= 0); + assert_se(symlink(f, l) == 0); + } + + assert_se(copy_tree(original_dir, copy_dir, true) == 0); + + STRV_FOREACH(p, files) { + _cleanup_free_ char *buf = NULL; + size_t sz = 0; + char *f = strappenda(copy_dir, *p); + + assert_se(access(f, F_OK) == 0); + assert_se(read_full_file(f, &buf, &sz) == 0); + assert_se(streq(buf, "file\n")); + } + + STRV_FOREACH_PAIR(link, p, links) { + _cleanup_free_ char *target = NULL; + char *f = strappenda(original_dir, *p); + char *l = strappenda(copy_dir, *link); + + assert_se(readlink_and_canonicalize(l, &target) == 0); + assert_se(path_equal(f, target)); + } + + assert_se(copy_tree(original_dir, copy_dir, false) < 0); + assert_se(copy_tree("/tmp/inexistent/foo/bar/fsdoi", copy_dir, false) < 0); + + rm_rf_dangerous(copy_dir, false, true, false); + rm_rf_dangerous(original_dir, false, true, false); +} + +int main(int argc, char *argv[]) { + test_copy_file(); + test_copy_file_fd(); + test_copy_tree(); + + return 0; +} diff --git a/src/test/test-engine.c b/src/test/test-engine.c index 6acd394c67..456999ca40 100644 --- a/src/test/test-engine.c +++ b/src/test/test-engine.c @@ -66,7 +66,7 @@ int main(int argc, char *argv[]) { manager_dump_units(m, stdout, "\t"); printf("Test2: (Cyclic Order, Unfixable)\n"); - assert_se(manager_add_job(m, JOB_START, d, JOB_REPLACE, false, NULL, &j) == -ENOEXEC); + assert_se(manager_add_job(m, JOB_START, d, JOB_REPLACE, false, NULL, &j) == -EDEADLOCK); manager_dump_jobs(m, stdout, "\t"); printf("Test3: (Cyclic Order, Fixable, Garbage Collector)\n"); @@ -82,14 +82,14 @@ int main(int argc, char *argv[]) { manager_dump_units(m, stdout, "\t"); printf("Test5: (Colliding transaction, fail)\n"); - assert_se(manager_add_job(m, JOB_START, g, JOB_FAIL, false, NULL, &j) == -EEXIST); + assert_se(manager_add_job(m, JOB_START, g, JOB_FAIL, false, NULL, &j) == -EDEADLOCK); printf("Test6: (Colliding transaction, replace)\n"); assert_se(manager_add_job(m, JOB_START, g, JOB_REPLACE, false, NULL, &j) == 0); manager_dump_jobs(m, stdout, "\t"); printf("Test7: (Unmergeable job type, fail)\n"); - assert_se(manager_add_job(m, JOB_STOP, g, JOB_FAIL, false, NULL, &j) == -EEXIST); + assert_se(manager_add_job(m, JOB_STOP, g, JOB_FAIL, false, NULL, &j) == -EDEADLOCK); printf("Test8: (Mergeable job type, fail)\n"); assert_se(manager_add_job(m, JOB_RESTART, g, JOB_FAIL, false, NULL, &j) == 0); diff --git a/src/test/test-execute.c b/src/test/test-execute.c new file mode 100644 index 0000000000..60466f0d3f --- /dev/null +++ b/src/test/test-execute.c @@ -0,0 +1,178 @@ +/*** + This file is part of systemd. + + Copyright 2014 Ronny Chevalier + + 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 <stdio.h> + +#include "unit.h" +#include "manager.h" +#include "util.h" +#include "macro.h" +#include "strv.h" +#include "mkdir.h" + +typedef void (*test_function_t)(Manager *m); + +static void check(Manager *m, Unit *unit, int status_expected, int code_expected) { + Service *service = NULL; + usec_t ts; + usec_t timeout = 2 * USEC_PER_SEC; + + assert_se(m); + assert_se(unit); + + service = SERVICE(unit); + printf("%s\n", unit->id); + exec_context_dump(&service->exec_context, stdout, "\t"); + ts = now(CLOCK_MONOTONIC); + while (service->state != SERVICE_DEAD && service->state != SERVICE_FAILED) { + int r; + usec_t n; + + r = sd_event_run(m->event, 100 * USEC_PER_MSEC); + assert_se(r >= 0); + + n = now(CLOCK_MONOTONIC); + if (ts + timeout < n) { + log_error("Test timeout when testing %s", unit->id); + exit(EXIT_FAILURE); + } + } + exec_status_dump(&service->main_exec_status, stdout, "\t"); + assert_se(service->main_exec_status.status == status_expected); + assert_se(service->main_exec_status.code == code_expected); +} + +static void test(Manager *m, const char *unit_name, int status_expected, int code_expected) { + Unit *unit; + + assert_se(unit_name); + + assert_se(manager_load_unit(m, unit_name, NULL, NULL, &unit) >= 0); + assert_se(UNIT_VTABLE(unit)->start(unit) >= 0); + check(m, unit, status_expected, code_expected); +} + +static void test_exec_workingdirectory(Manager *m) { + assert_se(mkdir_p("/tmp/test-exec_workingdirectory", 0755) >= 0); + + test(m, "exec-workingdirectory.service", 0, CLD_EXITED); + + rm_rf_dangerous("/tmp/test-exec_workingdirectory", false, true, false); +} + +static void test_exec_personality(Manager *m) { + test(m, "exec-personality-x86.service", 0, CLD_EXITED); + +#if defined(__x86_64__) + test(m, "exec-personality-x86-64.service", 0, CLD_EXITED); +#endif +} + +static void test_exec_ignoresigpipe(Manager *m) { + test(m, "exec-ignoresigpipe-yes.service", 0, CLD_EXITED); + test(m, "exec-ignoresigpipe-no.service", SIGPIPE, CLD_KILLED); +} + +static void test_exec_privatetmp(Manager *m) { + assert_se(touch("/tmp/test-exec_privatetmp") >= 0); + + test(m, "exec-privatetmp-yes.service", 0, CLD_EXITED); + test(m, "exec-privatetmp-no.service", 0, CLD_EXITED); + + unlink("/tmp/test-exec_privatetmp"); +} + +static void test_exec_privatedevices(Manager *m) { + test(m, "exec-privatedevices-yes.service", 0, CLD_EXITED); + test(m, "exec-privatedevices-no.service", 0, CLD_EXITED); +} + +static void test_exec_systemcallfilter(Manager *m) { +#ifdef HAVE_SECCOMP + test(m, "exec-systemcallfilter-not-failing.service", 0, CLD_EXITED); + test(m, "exec-systemcallfilter-not-failing2.service", 0, CLD_EXITED); + test(m, "exec-systemcallfilter-failing.service", SIGSYS, CLD_KILLED); + test(m, "exec-systemcallfilter-failing2.service", SIGSYS, CLD_KILLED); +#endif +} + +static void test_exec_systemcallerrornumber(Manager *m) { +#ifdef HAVE_SECCOMP + test(m, "exec-systemcallerrornumber.service", 1, CLD_EXITED); +#endif +} + +static void test_exec_user(Manager *m) { + test(m, "exec-user.service", 0, CLD_EXITED); +} + +static void test_exec_group(Manager *m) { + test(m, "exec-group.service", 0, CLD_EXITED); +} + +static void test_exec_environment(Manager *m) { + test(m, "exec-environment.service", 0, CLD_EXITED); + test(m, "exec-environment-multiple.service", 0, CLD_EXITED); + test(m, "exec-environment-empty.service", 0, CLD_EXITED); +} + +int main(int argc, char *argv[]) { + test_function_t tests[] = { + test_exec_workingdirectory, + test_exec_personality, + test_exec_ignoresigpipe, + test_exec_privatetmp, + test_exec_privatedevices, + test_exec_systemcallfilter, + test_exec_systemcallerrornumber, + test_exec_user, + test_exec_group, + test_exec_environment, + NULL, + }; + test_function_t *test = NULL; + Manager *m = NULL; + int r; + + log_parse_environment(); + log_open(); + + /* It is needed otherwise cgroup creation fails */ + if (getuid() != 0) { + printf("Skipping test: not root\n"); + return EXIT_TEST_SKIP; + } + + assert_se(set_unit_path(TEST_DIR ":") >= 0); + + r = manager_new(SYSTEMD_USER, true, &m); + if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT)) { + printf("Skipping test: manager_new: %s", strerror(-r)); + return EXIT_TEST_SKIP; + } + assert_se(r >= 0); + assert_se(manager_startup(m, NULL, NULL) >= 0); + + for (test = tests; test && *test; test++) + (*test)(m); + + manager_free(m); + + return 0; +} diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c index 7e7b4ac45d..cdf1973ea5 100644 --- a/src/test/test-fileio.c +++ b/src/test/test-fileio.c @@ -331,28 +331,65 @@ static void test_write_string_file(void) { unlink(fn); } -static void test_sendfile_full(void) { - char in_fn[] = "/tmp/test-sendfile_full-XXXXXX"; - char out_fn[] = "/tmp/test-sendfile_full-XXXXXX"; - _cleanup_close_ int in_fd, out_fd; - char text[] = "boohoo\nfoo\n\tbar\n"; +static void test_write_string_file_no_create(void) { + char fn[] = "/tmp/test-write_string_file_no_create-XXXXXX"; + _cleanup_close_ int fd; char buf[64] = {0}; - in_fd = mkostemp_safe(in_fn, O_RDWR); - assert_se(in_fd >= 0); - out_fd = mkostemp_safe(out_fn, O_RDWR); - assert_se(out_fd >= 0); + fd = mkostemp_safe(fn, O_RDWR); + assert_se(fd >= 0); + + assert_se(write_string_file_no_create("/a/file/which/does/not/exists/i/guess", "boohoo") < 0); + assert_se(write_string_file_no_create(fn, "boohoo") == 0); + + assert_se(read(fd, buf, sizeof(buf)) == strlen("boohoo\n")); + assert_se(streq(buf, "boohoo\n")); + + unlink(fn); +} + +static void test_load_env_file_pairs(void) { + char fn[] = "/tmp/test-load_env_file_pairs-XXXXXX"; + int fd; + int r; + _cleanup_fclose_ FILE *f = NULL; + _cleanup_strv_free_ char **l = NULL; + char **k, **v; + + fd = mkostemp_safe(fn, O_RDWR); + assert_se(fd >= 0); + + r = write_string_file(fn, + "NAME=\"Arch Linux\"\n" + "ID=arch\n" + "PRETTY_NAME=\"Arch Linux\"\n" + "ANSI_COLOR=\"0;36\"\n" + "HOME_URL=\"https://www.archlinux.org/\"\n" + "SUPPORT_URL=\"https://bbs.archlinux.org/\"\n" + "BUG_REPORT_URL=\"https://bugs.archlinux.org/\"\n" + ); + assert_se(r == 0); + + f = fdopen(fd, "r"); + assert_se(f); - assert_se(write_string_file(in_fn, text) == 0); - assert_se(sendfile_full(out_fd, "/a/file/which/does/not/exist/i/guess") < 0); - assert_se(sendfile_full(out_fd, in_fn) == sizeof(text) - 1); - assert_se(lseek(out_fd, SEEK_SET, 0) == 0); + r = load_env_file_pairs(f, fn, NULL, &l); + assert_se(r >= 0); - assert_se(read(out_fd, buf, sizeof(buf)) == sizeof(text) - 1); - assert_se(streq(buf, text)); + assert_se(strv_length(l) == 14); + STRV_FOREACH_PAIR(k, v, l) { + assert_se(STR_IN_SET(*k, "NAME", "ID", "PRETTY_NAME", "ANSI_COLOR", "HOME_URL", "SUPPORT_URL", "BUG_REPORT_URL")); + printf("%s=%s\n", *k, *v); + if (streq(*k, "NAME")) assert_se(streq(*v, "Arch Linux")); + if (streq(*k, "ID")) assert_se(streq(*v, "arch")); + if (streq(*k, "PRETTY_NAME")) assert_se(streq(*v, "Arch Linux")); + if (streq(*k, "ANSI_COLOR")) assert_se(streq(*v, "0;36")); + if (streq(*k, "HOME_URL")) assert_se(streq(*v, "https://www.archlinux.org/")); + if (streq(*k, "SUPPORT_URL")) assert_se(streq(*v, "https://bbs.archlinux.org/")); + if (streq(*k, "BUG_REPORT_URL")) assert_se(streq(*v, "https://bugs.archlinux.org/")); + } - unlink(in_fn); - unlink(out_fn); + unlink(fn); } int main(int argc, char *argv[]) { @@ -366,7 +403,8 @@ int main(int argc, char *argv[]) { test_capeff(); test_write_string_stream(); test_write_string_file(); - test_sendfile_full(); + test_write_string_file_no_create(); + test_load_env_file_pairs(); return 0; } diff --git a/src/test/test-hashmap-plain.c b/src/test/test-hashmap-plain.c index ce686b650c..6f0910aae7 100644 --- a/src/test/test-hashmap-plain.c +++ b/src/test/test-hashmap-plain.c @@ -194,8 +194,8 @@ static void test_hashmap_move(void) { hashmap_put(m, "key 3", val3); hashmap_put(m, "key 4", val4); - assert(hashmap_move(n, NULL) == 0); - assert(hashmap_move(n, m) == 0); + assert_se(hashmap_move(n, NULL) == 0); + assert_se(hashmap_move(n, m) == 0); assert_se(hashmap_size(m) == 1); r = hashmap_get(m, "key 1"); @@ -246,7 +246,7 @@ static void test_hashmap_put(void) { int valid_hashmap_put; void *val1 = (void*) "val 1"; - hashmap_ensure_allocated(&m, &string_hash_ops); + assert_se(hashmap_ensure_allocated(&m, &string_hash_ops) >= 0); assert_se(m); valid_hashmap_put = hashmap_put(m, "key 1", val1); @@ -380,6 +380,7 @@ static void test_hashmap_remove_and_replace(void) { void *key2 = UINT_TO_PTR(2); void *key3 = UINT_TO_PTR(3); void *r; + int i, j; m = hashmap_new(&trivial_hash_ops); assert_se(m); @@ -407,6 +408,25 @@ static void test_hashmap_remove_and_replace(void) { r = hashmap_get(m, key2); assert_se(r == key2); assert_se(!hashmap_get(m, key3)); + + /* Repeat this test several times to increase the chance of hitting + * the less likely case in hashmap_remove_and_replace where it + * compensates for the backward shift. */ + for (i = 0; i < 20; i++) { + hashmap_clear(m); + + for (j = 1; j < 7; j++) + hashmap_put(m, UINT_TO_PTR(10*i + j), UINT_TO_PTR(10*i + j)); + valid = hashmap_remove_and_replace(m, UINT_TO_PTR(10*i + 1), + UINT_TO_PTR(10*i + 2), + UINT_TO_PTR(10*i + 2)); + assert_se(valid == 0); + assert_se(!hashmap_get(m, UINT_TO_PTR(10*i + 1))); + for (j = 2; j < 7; j++) { + r = hashmap_get(m, UINT_TO_PTR(10*i + j)); + assert_se(r == UINT_TO_PTR(10*i + j)); + } + } } static void test_hashmap_ensure_allocated(void) { @@ -699,9 +719,9 @@ static void test_hashmap_many(void) { for (i = 1; i < tests[j].n_entries*3; i++) assert_se(hashmap_contains(h, UINT_TO_PTR(i)) == (i % 3 == 1)); - log_info("%u <= %u * 0.75 = %g", hashmap_size(h), hashmap_buckets(h), hashmap_buckets(h) * 0.75); + log_info("%u <= %u * 0.8 = %g", hashmap_size(h), hashmap_buckets(h), hashmap_buckets(h) * 0.8); - assert_se(hashmap_size(h) <= hashmap_buckets(h) * 0.75); + assert_se(hashmap_size(h) <= hashmap_buckets(h) * 0.8); assert_se(hashmap_size(h) == tests[j].n_entries); while (!hashmap_isempty(h)) { diff --git a/src/test/test-hostname.c b/src/test/test-hostname.c index ad4f285619..86efa1a3f7 100644 --- a/src/test/test-hostname.c +++ b/src/test/test-hostname.c @@ -32,7 +32,7 @@ int main(int argc, char* argv[]) { r = hostname_setup(); if (r < 0) - fprintf(stderr, "hostname: %s\n", strerror(-r)); + log_error_errno(r, "hostname: %m"); return 0; } diff --git a/src/test/test-libudev.c b/src/test/test-libudev.c index a51814df95..293c151d42 100644 --- a/src/test/test-libudev.c +++ b/src/test/test-libudev.c @@ -34,14 +34,6 @@ #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) -_printf_(6,0) -static void log_fn(struct udev *udev, - int priority, const char *file, int line, const char *fn, - const char *format, va_list args) { - printf("test-libudev: %s %s:%d ", fn, file, line); - vprintf(format, args); -} - static void print_device(struct udev_device *device) { const char *str; dev_t devnum; @@ -431,9 +423,6 @@ int main(int argc, char *argv[]) { printf("no context\n"); return 1; } - udev_set_log_fn(udev, log_fn); - printf("set log: %p\n", log_fn); - while ((c = getopt_long(argc, argv, "p:s:dhV", options, NULL)) >= 0) switch (c) { @@ -447,8 +436,8 @@ int main(int argc, char *argv[]) { break; case 'd': - if (udev_get_log_priority(udev) < LOG_INFO) - udev_set_log_priority(udev, LOG_INFO); + if (log_get_max_level() < LOG_INFO) + log_set_max_level(LOG_INFO); break; case 'h': diff --git a/src/test/test-locale-util.c b/src/test/test-locale-util.c new file mode 100644 index 0000000000..1398a3a0b9 --- /dev/null +++ b/src/test/test-locale-util.c @@ -0,0 +1,59 @@ +/*** + This file is part of systemd + + Copyright 2014 Ronny Chevalier + + 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 <unistd.h> + +#include "locale-util.h" +#include "strv.h" +#include "macro.h" + +static void test_get_locales(void) { + _cleanup_strv_free_ char **locales = NULL; + char **p; + int r; + + r = get_locales(&locales); + assert_se(r >= 0); + assert_se(locales); + + STRV_FOREACH(p, locales) { + puts(*p); + assert_se(locale_is_valid(*p)); + } +} + +static void test_locale_is_valid(void) { + assert_se(locale_is_valid("en_EN.utf8")); + assert_se(locale_is_valid("fr_FR.utf8")); + assert_se(locale_is_valid("fr_FR@euro")); + assert_se(locale_is_valid("fi_FI")); + assert_se(locale_is_valid("POSIX")); + assert_se(locale_is_valid("C")); + + assert_se(!locale_is_valid("")); + assert_se(!locale_is_valid("/usr/bin/foo")); + assert_se(!locale_is_valid("\x01gar\x02 bage\x03")); +} + +int main(int argc, char *argv[]) { + test_get_locales(); + test_locale_is_valid(); + + return 0; +} diff --git a/src/test/test-ns.c b/src/test/test-ns.c index 7714e49ad9..7cd7b77153 100644 --- a/src/test/test-ns.c +++ b/src/test/test-ns.c @@ -65,12 +65,12 @@ int main(int argc, char *argv[]) { PROTECT_SYSTEM_NO, 0); if (r < 0) { - log_error("Failed to setup namespace: %s", strerror(-r)); + log_error_errno(r, "Failed to setup namespace: %m"); return 1; } execl("/bin/sh", "/bin/sh", NULL); - log_error("execl(): %m"); + log_error_errno(errno, "execl(): %m"); return 1; } diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c index 63d64b28b0..58b456a291 100644 --- a/src/test/test-path-util.c +++ b/src/test/test-path-util.c @@ -85,29 +85,32 @@ static void test_path(void) { } } -static void test_find_binary(const char *self) { +static void test_find_binary(const char *self, bool local) { char *p; - assert_se(find_binary("/bin/sh", &p) == 0); + assert_se(find_binary("/bin/sh", local, &p) == 0); puts(p); assert_se(streq(p, "/bin/sh")); free(p); - assert_se(find_binary(self, &p) == 0); + assert_se(find_binary(self, local, &p) == 0); puts(p); assert_se(endswith(p, "/test-path-util")); assert_se(path_is_absolute(p)); free(p); - assert_se(find_binary("sh", &p) == 0); + assert_se(find_binary("sh", local, &p) == 0); puts(p); assert_se(endswith(p, "/sh")); assert_se(path_is_absolute(p)); free(p); - assert_se(find_binary("xxxx-xxxx", &p) == -ENOENT); + assert_se(find_binary("xxxx-xxxx", local, &p) == -ENOENT); - assert_se(find_binary("/some/dir/xxxx-xxxx", &p) == -ENOENT); + assert_se(find_binary("/some/dir/xxxx-xxxx", local, &p) == + (local ? -ENOENT : 0)); + if (!local) + free(p); } static void test_prefixes(void) { @@ -242,13 +245,35 @@ static void test_strv_resolve(void) { assert_se(rm_rf_dangerous(tmp_dir, false, true, false) == 0); } +static void test_path_startswith(void) { + assert_se(path_startswith("/foo/bar/barfoo/", "/foo")); + assert_se(path_startswith("/foo/bar/barfoo/", "/foo/")); + assert_se(path_startswith("/foo/bar/barfoo/", "/")); + assert_se(path_startswith("/foo/bar/barfoo/", "////")); + assert_se(path_startswith("/foo/bar/barfoo/", "/foo//bar/////barfoo///")); + assert_se(path_startswith("/foo/bar/barfoo/", "/foo/bar/barfoo////")); + assert_se(path_startswith("/foo/bar/barfoo/", "/foo/bar///barfoo/")); + assert_se(path_startswith("/foo/bar/barfoo/", "/foo////bar/barfoo/")); + assert_se(path_startswith("/foo/bar/barfoo/", "////foo/bar/barfoo/")); + assert_se(path_startswith("/foo/bar/barfoo/", "/foo/bar/barfoo")); + + assert_se(!path_startswith("/foo/bar/barfoo/", "/foo/bar/barfooa/")); + assert_se(!path_startswith("/foo/bar/barfoo/", "/foo/bar/barfooa")); + assert_se(!path_startswith("/foo/bar/barfoo/", "")); + assert_se(!path_startswith("/foo/bar/barfoo/", "/bar/foo")); + assert_se(!path_startswith("/foo/bar/barfoo/", "/f/b/b/")); +} + int main(int argc, char **argv) { test_path(); - test_find_binary(argv[0]); + test_find_binary(argv[0], true); + test_find_binary(argv[0], false); test_prefixes(); test_path_join(); test_fsck_exists(); test_make_relative(); test_strv_resolve(); + test_path_startswith(); + return 0; } diff --git a/src/test/test-path.c b/src/test/test-path.c new file mode 100644 index 0000000000..18fcb575e6 --- /dev/null +++ b/src/test/test-path.c @@ -0,0 +1,271 @@ +/*** + This file is part of systemd. + + Copyright 2014 Ronny Chevalier + + 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 <stdio.h> +#include <stdbool.h> + +#include "unit.h" +#include "manager.h" +#include "util.h" +#include "macro.h" +#include "strv.h" +#include "mkdir.h" + +typedef void (*test_function_t)(Manager *m); + +static int setup_test(Manager **m) { + char **tests_path = STRV_MAKE("exists", "existsglobFOOBAR", "changed", "modified", "unit", + "directorynotempty", "makedirectory"); + char **test_path; + Manager *tmp; + int r; + + assert_se(m); + + r = manager_new(SYSTEMD_USER, true, &tmp); + if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT)) { + printf("Skipping test: manager_new: %s", strerror(-r)); + return -EXIT_TEST_SKIP; + } + assert_se(r >= 0); + assert_se(manager_startup(tmp, NULL, NULL) >= 0); + + STRV_FOREACH(test_path, tests_path) { + rm_rf_dangerous(strappenda("/tmp/test-path_", *test_path), false, true, false); + } + + *m = tmp; + + return 0; +} + +static void shutdown_test(Manager *m) { + assert_se(m); + + manager_free(m); +} + +static void check_stop_unlink(Manager *m, Unit *unit, const char *test_path, const char *service_name) { + _cleanup_free_ char *tmp = NULL; + Unit *service_unit = NULL; + Service *service = NULL; + usec_t ts; + usec_t timeout = 2 * USEC_PER_SEC; + + assert_se(m); + assert_se(unit); + assert_se(test_path); + + if (!service_name) { + assert_se(tmp = strreplace(unit->id, ".path", ".service")); + service_unit = manager_get_unit(m, tmp); + } else + service_unit = manager_get_unit(m, service_name); + assert_se(service_unit); + service = SERVICE(service_unit); + + ts = now(CLOCK_MONOTONIC); + /* We proces events until the service related to the path has been successfully started */ + while(service->result != SERVICE_SUCCESS || service->state != SERVICE_START) { + usec_t n; + int r; + + r = sd_event_run(m->event, 100 * USEC_PER_MSEC); + assert_se(r >= 0); + + printf("%s: state = %s; result = %s \n", + service_unit->id, + service_state_to_string(service->state), + service_result_to_string(service->result)); + + + /* But we timeout if the service has not been started in the allocated time */ + n = now(CLOCK_MONOTONIC); + if (ts + timeout < n) { + log_error("Test timeout when testing %s", unit->id); + exit(EXIT_FAILURE); + } + } + + assert_se(UNIT_VTABLE(unit)->stop(unit) >= 0); + rm_rf_dangerous(test_path, false, true, false); +} + +static void test_path_exists(Manager *m) { + const char *test_path = "/tmp/test-path_exists"; + Unit *unit = NULL; + + assert_se(m); + + assert_se(manager_load_unit(m, "path-exists.path", NULL, NULL, &unit) >= 0); + assert_se(UNIT_VTABLE(unit)->start(unit) >= 0); + + assert_se(touch(test_path) >= 0); + + check_stop_unlink(m, unit, test_path, NULL); +} + +static void test_path_existsglob(Manager *m) { + const char *test_path = "/tmp/test-path_existsglobFOOBAR"; + Unit *unit = NULL; + + assert_se(m); + assert_se(manager_load_unit(m, "path-existsglob.path", NULL, NULL, &unit) >= 0); + assert_se(UNIT_VTABLE(unit)->start(unit) >= 0); + + assert_se(touch(test_path) >= 0); + + check_stop_unlink(m, unit, test_path, NULL); +} + +static void test_path_changed(Manager *m) { + const char *test_path = "/tmp/test-path_changed"; + FILE *f; + Unit *unit = NULL; + + assert_se(m); + + assert_se(touch(test_path) >= 0); + + assert_se(manager_load_unit(m, "path-changed.path", NULL, NULL, &unit) >= 0); + assert_se(UNIT_VTABLE(unit)->start(unit) >= 0); + + f = fopen(test_path, "w"); + assert_se(f); + fclose(f); + + check_stop_unlink(m, unit, test_path, NULL); +} + +static void test_path_modified(Manager *m) { + _cleanup_fclose_ FILE *f = NULL; + const char *test_path = "/tmp/test-path_modified"; + Unit *unit = NULL; + + assert_se(m); + + assert_se(touch(test_path) >= 0); + + assert_se(manager_load_unit(m, "path-modified.path", NULL, NULL, &unit) >= 0); + assert_se(UNIT_VTABLE(unit)->start(unit) >= 0); + + f = fopen(test_path, "w"); + assert_se(f); + fputs("test", f); + + check_stop_unlink(m, unit, test_path, NULL); +} + +static void test_path_unit(Manager *m) { + const char *test_path = "/tmp/test-path_unit"; + Unit *unit = NULL; + + assert_se(m); + + assert_se(manager_load_unit(m, "path-unit.path", NULL, NULL, &unit) >= 0); + assert_se(UNIT_VTABLE(unit)->start(unit) >= 0); + + assert_se(touch(test_path) >= 0); + + check_stop_unlink(m, unit, test_path, "path-mycustomunit.service"); +} + +static void test_path_directorynotempty(Manager *m) { + const char *test_path = "/tmp/test-path_directorynotempty/"; + Unit *unit = NULL; + + assert_se(m); + + assert_se(access(test_path, F_OK) < 0); + + assert_se(manager_load_unit(m, "path-directorynotempty.path", NULL, NULL, &unit) >= 0); + assert_se(UNIT_VTABLE(unit)->start(unit) >= 0); + + /* MakeDirectory default to no */ + assert_se(access(test_path, F_OK) < 0); + + assert_se(mkdir_p(test_path, 0755) >= 0); + assert_se(touch(strappenda(test_path, "test_file")) >= 0); + + check_stop_unlink(m, unit, test_path, NULL); +} + +static void test_path_makedirectory_directorymode(Manager *m) { + const char *test_path = "/tmp/test-path_makedirectory/"; + Unit *unit = NULL; + struct stat s; + + assert_se(m); + + assert_se(access(test_path, F_OK) < 0); + + assert_se(manager_load_unit(m, "path-makedirectory.path", NULL, NULL, &unit) >= 0); + assert_se(UNIT_VTABLE(unit)->start(unit) >= 0); + + /* Check if the directory has been created */ + assert_se(access(test_path, F_OK) >= 0); + + /* Check the mode we specified with DirectoryMode=0744 */ + assert_se(stat(test_path, &s) >= 0); + assert_se((s.st_mode & S_IRWXU) == 0700); + assert_se((s.st_mode & S_IRWXG) == 0040); + assert_se((s.st_mode & S_IRWXO) == 0004); + + assert_se(UNIT_VTABLE(unit)->stop(unit) >= 0); + rm_rf_dangerous(test_path, false, true, false); +} + +int main(int argc, char *argv[]) { + test_function_t tests[] = { + test_path_exists, + test_path_existsglob, + test_path_changed, + test_path_modified, + test_path_unit, + test_path_directorynotempty, + test_path_makedirectory_directorymode, + NULL, + }; + test_function_t *test = NULL; + Manager *m = NULL; + + log_parse_environment(); + log_open(); + + /* It is needed otherwise cgroup creation fails */ + if (getuid() != 0) + return EXIT_TEST_SKIP; + + assert_se(set_unit_path(TEST_DIR ":") >= 0); + + for (test = tests; test && *test; test++) { + int r; + + /* We create a clean environment for each test */ + r = setup_test(&m); + if (r < 0) + return -r; + + (*test)(m); + + shutdown_test(m); + } + + return 0; +} diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c index c2c728bcde..6fb4a40944 100644 --- a/src/test/test-socket-util.c +++ b/src/test/test-socket-util.c @@ -258,7 +258,7 @@ static void test_nameinfo_pretty(void) { _cleanup_close_ int sfd = -1, cfd = -1; r = getnameinfo_pretty(STDIN_FILENO, &stdin_name); - log_info("No connection remote: %s", strerror(-r)); + log_info_errno(r, "No connection remote: %m"); assert_se(r < 0); diff --git a/src/test/test-strv.c b/src/test/test-strv.c index bbfe306d7d..674c1b53fc 100644 --- a/src/test/test-strv.c +++ b/src/test/test-strv.c @@ -113,6 +113,22 @@ static void test_strv_find_prefix(void) { assert_se(!strv_find_prefix((char **)input_table_multiple, "onee")); } +static void test_strv_find_startswith(void) { + char *r; + + r = strv_find_startswith((char **)input_table_multiple, "o"); + assert_se(r && streq(r, "ne")); + + r = strv_find_startswith((char **)input_table_multiple, "one"); + assert_se(r && streq(r, "")); + + r = strv_find_startswith((char **)input_table_multiple, ""); + assert_se(r && streq(r, "one")); + + assert_se(!strv_find_startswith((char **)input_table_multiple, "xxx")); + assert_se(!strv_find_startswith((char **)input_table_multiple, "onee")); +} + static void test_strv_join(void) { _cleanup_free_ char *p = NULL, *q = NULL, *r = NULL, *s = NULL, *t = NULL; @@ -149,7 +165,7 @@ static void test_strv_quote_unquote(const char* const *split, const char *quoted assert_se(p); assert_se(streq(p, quoted)); - r = strv_split_quoted(&s, quoted); + r = strv_split_quoted(&s, quoted, false); assert_se(r == 0); assert_se(s); STRV_FOREACH(t, s) { @@ -166,7 +182,7 @@ static void test_strv_unquote(const char *quoted, const char **list) { char **t; int r; - r = strv_split_quoted(&s, quoted); + r = strv_split_quoted(&s, quoted, false); assert_se(r == 0); assert_se(s); j = strv_join(s, " | "); @@ -183,7 +199,7 @@ static void test_invalid_unquote(const char *quoted) { char **s = NULL; int r; - r = strv_split_quoted(&s, quoted); + r = strv_split_quoted(&s, quoted, false); assert_se(s == NULL); assert_se(r == -EINVAL); } @@ -416,6 +432,44 @@ static void test_strv_from_stdarg_alloca(void) { test_strv_from_stdarg_alloca_one(STRV_MAKE_EMPTY, NULL); } +static void test_strv_push_prepend(void) { + _cleanup_strv_free_ char **a = NULL; + + a = strv_new("foo", "bar", "three", NULL); + + assert_se(strv_push_prepend(&a, strdup("first")) >= 0); + assert_se(streq(a[0], "first")); + assert_se(streq(a[1], "foo")); + assert_se(streq(a[2], "bar")); + assert_se(streq(a[3], "three")); + assert_se(!a[4]); + + assert_se(strv_consume_prepend(&a, strdup("first2")) >= 0); + assert_se(streq(a[0], "first2")); + assert_se(streq(a[1], "first")); + assert_se(streq(a[2], "foo")); + assert_se(streq(a[3], "bar")); + assert_se(streq(a[4], "three")); + assert_se(!a[5]); +} + +static void test_strv_push(void) { + _cleanup_strv_free_ char **a = NULL; + char *i, *j; + + assert_se(i = strdup("foo")); + assert_se(strv_push(&a, i) >= 0); + + assert_se(i = strdup("a")); + assert_se(j = strdup("b")); + assert_se(strv_push_pair(&a, i, j) >= 0); + + assert_se(streq_ptr(a[0], "foo")); + assert_se(streq_ptr(a[1], "a")); + assert_se(streq_ptr(a[2], "b")); + assert_se(streq_ptr(a[3], NULL)); +} + int main(int argc, char *argv[]) { test_specifier_printf(); test_strv_foreach(); @@ -423,6 +477,7 @@ int main(int argc, char *argv[]) { test_strv_foreach_pair(); test_strv_find(); test_strv_find_prefix(); + test_strv_find_startswith(); test_strv_join(); test_strv_quote_unquote(input_table_multiple, "\"one\" \"two\" \"three\""); @@ -444,12 +499,12 @@ int main(int argc, char *argv[]) { test_strv_unquote(" \"x'\" ", (const char*[]) { "x'", NULL }); test_strv_unquote("a '--b=c \"d e\"'", (const char*[]) { "a", "--b=c \"d e\"", NULL }); - test_invalid_unquote("a --b='c \"d e\"'"); - test_invalid_unquote("a --b='c \"d e\" '"); + test_invalid_unquote("a --b='c \"d e\"''"); + test_invalid_unquote("a --b='c \"d e\" '\""); test_invalid_unquote("a --b='c \"d e\"garbage"); test_invalid_unquote("'"); test_invalid_unquote("\""); - test_invalid_unquote("'x'y"); + test_invalid_unquote("'x'y'g"); test_strv_split(); test_strv_split_newlines(); @@ -462,6 +517,8 @@ int main(int argc, char *argv[]) { test_strv_extend(); test_strv_extendf(); test_strv_from_stdarg_alloca(); + test_strv_push_prepend(); + test_strv_push(); return 0; } diff --git a/src/test/test-tables.c b/src/test/test-tables.c index 907958e461..97d5609adf 100644 --- a/src/test/test-tables.c +++ b/src/test/test-tables.c @@ -48,6 +48,7 @@ #include "link-config.h" #include "bus-policy.h" #include "journald-server.h" +#include "locale-util.h" #include "test-tables.h" @@ -60,6 +61,8 @@ int main(int argc, char **argv) { test_table(busname_state, BUSNAME_STATE); test_table(cgroup_device_policy, CGROUP_DEVICE_POLICY); test_table(condition_type, CONDITION_TYPE); + test_table(assert_type, CONDITION_TYPE); + test_table(condition_result, CONDITION_RESULT); test_table(device_state, DEVICE_STATE); test_table(exec_input, EXEC_INPUT); test_table(exec_output, EXEC_OUTPUT); @@ -116,6 +119,7 @@ int main(int argc, char **argv) { test_table(unit_file_state, UNIT_FILE_STATE); test_table(unit_load_state, UNIT_LOAD_STATE); test_table(unit_type, UNIT_TYPE); + test_table(locale_variable, VARIABLE_LC); test_table_sparse(object_compressed, OBJECT_COMPRESSED); diff --git a/src/test/test-udev.c b/src/test/test-udev.c index 8a51473419..f2283ec7a7 100644 --- a/src/test/test-udev.c +++ b/src/test/test-udev.c @@ -35,10 +35,6 @@ #include "udev.h" #include "udev-util.h" -void udev_main_log(struct udev *udev, int priority, - const char *file, int line, const char *fn, - const char *format, va_list args) {} - static int fake_filesystems(void) { static const struct fakefs { const char *src; @@ -153,8 +149,14 @@ int main(int argc, char *argv[]) { } } - udev_event_execute_rules(event, 3 * USEC_PER_SEC, USEC_PER_SEC, rules, &sigmask_orig); - udev_event_execute_run(event, 3 * USEC_PER_SEC, USEC_PER_SEC, NULL); + udev_event_execute_rules(event, + 3 * USEC_PER_SEC, USEC_PER_SEC, + NULL, + rules, + &sigmask_orig); + udev_event_execute_run(event, + 3 * USEC_PER_SEC, USEC_PER_SEC, + NULL); out: if (event != NULL && event->fd_signal >= 0) close(event->fd_signal); diff --git a/src/test/test-uid-range.c b/src/test/test-uid-range.c index 06b4d43426..bc5baa2fcb 100644 --- a/src/test/test-uid-range.c +++ b/src/test/test-uid-range.c @@ -39,7 +39,7 @@ int main(int argc, char *argv[]) { assert_se(uid_range_contains(p, n, 999)); assert_se(!uid_range_contains(p, n, 1000)); - search = (uid_t) -1; + search = UID_INVALID; assert_se(uid_range_next_lower(p, n, &search)); assert_se(search == 999); assert_se(uid_range_next_lower(p, n, &search)); diff --git a/src/test/test-unaligned.c b/src/test/test-unaligned.c new file mode 100644 index 0000000000..1754d06b2d --- /dev/null +++ b/src/test/test-unaligned.c @@ -0,0 +1,93 @@ +/*** + This file is part of systemd + + Copyright 2014 Tom Gundersen + + 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 "unaligned.h" +#include "sparse-endian.h" +#include "util.h" + +static uint8_t data[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, +}; + +int main(int argc, const char *argv[]) { + uint8_t scratch[16]; + + assert_se(unaligned_read_be16(&data[0]) == 0x0001); + assert_se(unaligned_read_be16(&data[1]) == 0x0102); + + assert_se(unaligned_read_be32(&data[0]) == 0x00010203); + assert_se(unaligned_read_be32(&data[1]) == 0x01020304); + assert_se(unaligned_read_be32(&data[2]) == 0x02030405); + assert_se(unaligned_read_be32(&data[3]) == 0x03040506); + + assert_se(unaligned_read_be64(&data[0]) == 0x0001020304050607); + assert_se(unaligned_read_be64(&data[1]) == 0x0102030405060708); + assert_se(unaligned_read_be64(&data[2]) == 0x0203040506070809); + assert_se(unaligned_read_be64(&data[3]) == 0x030405060708090a); + assert_se(unaligned_read_be64(&data[4]) == 0x0405060708090a0b); + assert_se(unaligned_read_be64(&data[5]) == 0x05060708090a0b0c); + assert_se(unaligned_read_be64(&data[6]) == 0x060708090a0b0c0d); + assert_se(unaligned_read_be64(&data[7]) == 0x0708090a0b0c0d0e); + + zero(scratch); + unaligned_write_be16(&scratch[0], 0x0001); + assert_se(memcmp(&scratch[0], &data[0], sizeof(uint16_t)) == 0); + zero(scratch); + unaligned_write_be16(&scratch[1], 0x0102); + assert_se(memcmp(&scratch[1], &data[1], sizeof(uint16_t)) == 0); + + zero(scratch); + unaligned_write_be32(&scratch[0], 0x00010203); + assert_se(memcmp(&scratch[0], &data[0], sizeof(uint32_t)) == 0); + zero(scratch); + unaligned_write_be32(&scratch[1], 0x01020304); + assert_se(memcmp(&scratch[1], &data[1], sizeof(uint32_t)) == 0); + zero(scratch); + unaligned_write_be32(&scratch[2], 0x02030405); + assert_se(memcmp(&scratch[2], &data[2], sizeof(uint32_t)) == 0); + zero(scratch); + unaligned_write_be32(&scratch[3], 0x03040506); + assert_se(memcmp(&scratch[3], &data[3], sizeof(uint32_t)) == 0); + + zero(scratch); + unaligned_write_be64(&scratch[0], 0x0001020304050607); + assert_se(memcmp(&scratch[0], &data[0], sizeof(uint64_t)) == 0); + zero(scratch); + unaligned_write_be64(&scratch[1], 0x0102030405060708); + assert_se(memcmp(&scratch[1], &data[1], sizeof(uint64_t)) == 0); + zero(scratch); + unaligned_write_be64(&scratch[2], 0x0203040506070809); + assert_se(memcmp(&scratch[2], &data[2], sizeof(uint64_t)) == 0); + zero(scratch); + unaligned_write_be64(&scratch[3], 0x030405060708090a); + assert_se(memcmp(&scratch[3], &data[3], sizeof(uint64_t)) == 0); + zero(scratch); + unaligned_write_be64(&scratch[4], 0x0405060708090a0b); + assert_se(memcmp(&scratch[4], &data[4], sizeof(uint64_t)) == 0); + zero(scratch); + unaligned_write_be64(&scratch[5], 0x05060708090a0b0c); + assert_se(memcmp(&scratch[5], &data[5], sizeof(uint64_t)) == 0); + zero(scratch); + unaligned_write_be64(&scratch[6], 0x060708090a0b0c0d); + assert_se(memcmp(&scratch[6], &data[6], sizeof(uint64_t)) == 0); + zero(scratch); + unaligned_write_be64(&scratch[7], 0x0708090a0b0c0d0e); + assert_se(memcmp(&scratch[7], &data[7], sizeof(uint64_t)) == 0); +} diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c index 03b3e25939..f31a1bbc9b 100644 --- a/src/test/test-unit-file.c +++ b/src/test/test-unit-file.c @@ -222,6 +222,9 @@ static void test_config_parse_exec(void) { "MODULE_0=coretemp\n" \ "MODULE_1=f71882fg" +#define env_file_5 \ + "a=\n" \ + "b=" static void test_load_env_file_1(void) { _cleanup_strv_free_ char **data = NULL; @@ -300,6 +303,24 @@ static void test_load_env_file_4(void) { unlink(name); } +static void test_load_env_file_5(void) { + _cleanup_strv_free_ char **data = NULL; + int r; + + char name[] = "/tmp/test-load-env-file.XXXXXX"; + _cleanup_close_ int fd; + + fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC); + assert_se(fd >= 0); + assert_se(write(fd, env_file_5, sizeof(env_file_5)) == sizeof(env_file_5)); + + r = load_env_file(NULL, name, NULL, &data); + assert_se(r == 0); + assert_se(streq(data[0], "a=")); + assert_se(streq(data[1], "b=")); + assert_se(data[2] == NULL); + unlink(name); +} static void test_install_printf(void) { char name[] = "name.service", @@ -387,6 +408,7 @@ int main(int argc, char *argv[]) { test_load_env_file_2(); test_load_env_file_3(); test_load_env_file_4(); + test_load_env_file_5(); TEST_REQ_RUNNING_SYSTEMD(test_install_printf()); return r; diff --git a/src/test/test-unit-name.c b/src/test/test-unit-name.c index 256e820c22..ab6c488cff 100644 --- a/src/test/test-unit-name.c +++ b/src/test/test-unit-name.c @@ -135,7 +135,7 @@ static int test_unit_printf(void) { #define expect(unit, pattern, expected) \ { \ char *e; \ - _cleanup_free_ char *t; \ + _cleanup_free_ char *t = NULL; \ assert_se(unit_full_printf(unit, pattern, &t) >= 0); \ printf("result: %s\nexpect: %s\n", t, expected); \ if ((e = endswith(expected, "*"))) \ diff --git a/src/test/test-utf8.c b/src/test/test-utf8.c index b7d988f22d..3399f2ba9c 100644 --- a/src/test/test-utf8.c +++ b/src/test/test-utf8.c @@ -47,7 +47,6 @@ static void test_utf8_encoded_valid_unichar(void) { assert_se(utf8_encoded_valid_unichar("a") == 1); assert_se(utf8_encoded_valid_unichar("\341\204") < 0); assert_se(utf8_encoded_valid_unichar("\341\204\341\204") < 0); - } static void test_utf8_escaping(void) { @@ -66,12 +65,41 @@ static void test_utf8_escaping(void) { assert_se(utf8_is_valid(p3)); } +static void test_utf8_escaping_printable(void) { + _cleanup_free_ char *p1, *p2, *p3, *p4, *p5, *p6; + + p1 = utf8_escape_non_printable("goo goo goo"); + puts(p1); + assert_se(utf8_is_valid(p1)); + + p2 = utf8_escape_non_printable("\341\204\341\204"); + puts(p2); + assert_se(utf8_is_valid(p2)); + + p3 = utf8_escape_non_printable("\341\204"); + puts(p3); + assert_se(utf8_is_valid(p3)); + + p4 = utf8_escape_non_printable("ąę\n가너도루\n1234\n\341\204\341\204\n\001 \019\20\a"); + puts(p4); + assert_se(utf8_is_valid(p4)); + + p5 = utf8_escape_non_printable("\001 \019\20\a"); + puts(p5); + assert_se(utf8_is_valid(p5)); + + p6 = utf8_escape_non_printable("\xef\xbf\x30\x13"); + puts(p6); + assert_se(utf8_is_valid(p6)); +} + int main(int argc, char *argv[]) { test_utf8_is_valid(); test_utf8_is_printable(); test_ascii_is_valid(); test_utf8_encoded_valid_unichar(); test_utf8_escaping(); + test_utf8_escaping_printable(); return 0; } diff --git a/src/test/test-util.c b/src/test/test-util.c index de6a2a0d89..fe54586eee 100644 --- a/src/test/test-util.c +++ b/src/test/test-util.c @@ -35,6 +35,7 @@ #include "def.h" #include "fileio.h" #include "conf-parser.h" +#include "virt.h" static void test_streq_ptr(void) { assert_se(streq_ptr(NULL, NULL)); @@ -490,13 +491,14 @@ static void test_u64log2(void) { static void test_get_process_comm(void) { struct stat st; - _cleanup_free_ char *a = NULL, *c = NULL, *d = NULL, *f = NULL, *i = NULL; - unsigned long long b; + _cleanup_free_ char *a = NULL, *c = NULL, *d = NULL, *f = NULL, *i = NULL, *cwd = NULL, *root = NULL; + _cleanup_free_ char *env = NULL; pid_t e; uid_t u; gid_t g; dev_t h; int r; + pid_t me; if (stat("/proc/1/comm", &st) == 0) { assert_se(get_process_comm(1, &a) >= 0); @@ -505,9 +507,6 @@ static void test_get_process_comm(void) { log_warning("/proc/1/comm does not exist."); } - assert_se(get_starttime_of_pid(1, &b) >= 0); - log_info("pid1 starttime: '%llu'", b); - assert_se(get_process_cmdline(1, 0, true, &c) >= 0); log_info("pid1 cmdline: '%s'", c); @@ -532,7 +531,22 @@ static void test_get_process_comm(void) { log_info("pid1 gid: "GID_FMT, g); assert_se(g == 0); - assert_se(get_ctty_devnr(1, &h) == -ENOENT); + me = getpid(); + + r = get_process_cwd(me, &cwd); + assert_se(r >= 0 || r == -EACCES); + log_info("pid1 cwd: '%s'", cwd); + + r = get_process_root(me, &root); + assert_se(r >= 0 || r == -EACCES); + log_info("pid1 root: '%s'", root); + + r = get_process_environ(me, &env); + assert_se(r >= 0 || r == -EACCES); + log_info("self strlen(environ): '%zd'", strlen(env)); + + if (!detect_container(NULL)) + assert_se(get_ctty_devnr(1, &h) == -ENOENT); getenv_for_pid(1, "PATH", &i); log_info("pid1 $PATH: '%s'", strna(i)); @@ -1173,51 +1187,61 @@ static void test_unquote_first_word(void) { char *t; p = original = "foobar waldo"; - assert_se(unquote_first_word(&p, &t) > 0); + assert_se(unquote_first_word(&p, &t, false) > 0); assert_se(streq(t, "foobar")); free(t); assert_se(p == original + 7); - assert_se(unquote_first_word(&p, &t) > 0); + assert_se(unquote_first_word(&p, &t, false) > 0); assert_se(streq(t, "waldo")); free(t); assert_se(p == original + 12); - assert_se(unquote_first_word(&p, &t) == 0); + assert_se(unquote_first_word(&p, &t, false) == 0); assert_se(!t); assert_se(p == original + 12); p = original = "\"foobar\" \'waldo\'"; - assert_se(unquote_first_word(&p, &t) > 0); + assert_se(unquote_first_word(&p, &t, false) > 0); assert_se(streq(t, "foobar")); free(t); assert_se(p == original + 9); - assert_se(unquote_first_word(&p, &t) > 0); + assert_se(unquote_first_word(&p, &t, false) > 0); assert_se(streq(t, "waldo")); free(t); assert_se(p == original + 16); - assert_se(unquote_first_word(&p, &t) == 0); + assert_se(unquote_first_word(&p, &t, false) == 0); assert_se(!t); assert_se(p == original + 16); p = original = "\""; - assert_se(unquote_first_word(&p, &t) == -EINVAL); + assert_se(unquote_first_word(&p, &t, false) == -EINVAL); assert_se(p == original + 1); p = original = "\'"; - assert_se(unquote_first_word(&p, &t) == -EINVAL); + assert_se(unquote_first_word(&p, &t, false) == -EINVAL); assert_se(p == original + 1); + p = original = "\'fooo"; + assert_se(unquote_first_word(&p, &t, false) == -EINVAL); + assert_se(p == original + 5); + + p = original = "\'fooo"; + assert_se(unquote_first_word(&p, &t, true) > 0); + assert_se(streq(t, "fooo")); + free(t); + assert_se(p == original + 5); + p = original = "yay\'foo\'bar"; - assert_se(unquote_first_word(&p, &t) > 0); + assert_se(unquote_first_word(&p, &t, false) > 0); assert_se(streq(t, "yayfoobar")); free(t); assert_se(p == original + 11); p = original = " foobar "; - assert_se(unquote_first_word(&p, &t) > 0); + assert_se(unquote_first_word(&p, &t, false) > 0); assert_se(streq(t, "foobar")); free(t); assert_se(p == original + 12); @@ -1277,6 +1301,17 @@ static void test_unquote_many_words(void) { free(a); } +static int parse_item(const char *key, const char *value) { + assert_se(key); + + log_info("kernel cmdline option <%s> = <%s>", key, strna(value)); + return 0; +} + +static void test_parse_proc_cmdline(void) { + assert_se(parse_proc_cmdline(parse_item) >= 0); +} + int main(int argc, char *argv[]) { log_parse_environment(); log_open(); @@ -1348,6 +1383,7 @@ int main(int argc, char *argv[]) { test_execute_directory(); test_unquote_first_word(); test_unquote_many_words(); + test_parse_proc_cmdline(); return 0; } diff --git a/src/test/test-watchdog.c b/src/test/test-watchdog.c index ccb1854708..f6bb045c3c 100644 --- a/src/test/test-watchdog.c +++ b/src/test/test-watchdog.c @@ -35,13 +35,13 @@ int main(int argc, char *argv[]) { r = watchdog_set_timeout(&t); if (r < 0) - log_warning("Failed to open watchdog: %s", strerror(-r)); + log_warning_errno(r, "Failed to open watchdog: %m"); for (i = 0; i < 5; i++) { log_info("Pinging..."); r = watchdog_ping(); if (r < 0) - log_warning("Failed to ping watchdog: %s", strerror(-r)); + log_warning_errno(r, "Failed to ping watchdog: %m"); usleep(t/2); } diff --git a/src/timedate/timedatectl.c b/src/timedate/timedatectl.c index 0ba32d31c4..49196ca793 100644 --- a/src/timedate/timedatectl.c +++ b/src/timedate/timedatectl.c @@ -232,7 +232,7 @@ static int show_status(sd_bus *bus, char **args, unsigned n) { map, &info); if (r < 0) { - log_error("Failed to query server: %s", strerror(-r)); + log_error_errno(r, "Failed to query server: %m"); goto fail; } @@ -363,10 +363,8 @@ static int list_timezones(sd_bus *bus, char **args, unsigned n) { assert(n == 1); r = get_timezones(&zones); - if (r < 0) { - log_error("Failed to read list of time zones: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to read list of time zones: %m"); pager_open_if_enabled(); strv_print(zones); @@ -558,7 +556,7 @@ int main(int argc, char *argv[]) { r = bus_open_transport(arg_transport, arg_host, false, &bus); if (r < 0) { - log_error("Failed to create bus connection: %s", strerror(-r)); + log_error_errno(r, "Failed to create bus connection: %m"); goto finish; } diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c index 8880812b49..bf567a1624 100644 --- a/src/timedate/timedated.c +++ b/src/timedate/timedated.c @@ -38,12 +38,18 @@ #include "fileio-label.h" #include "label.h" #include "bus-util.h" -#include "bus-errors.h" +#include "bus-error.h" +#include "bus-common-errors.h" #include "event-util.h" #define NULL_ADJTIME_UTC "0.0 0 0\n0\nUTC\n" #define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n" +static BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map timedated_errors[] = { + SD_BUS_ERROR_MAP("org.freedesktop.timedate1.NoNTPSupport", ENOTSUP), + SD_BUS_ERROR_MAP_END +}; + typedef struct Context { char *zone; bool local_rtc; @@ -70,7 +76,7 @@ static int context_read_data(Context *c) { if (r == -EINVAL) log_warning("/etc/localtime should be a symbolic link to a time zone data file in /usr/share/zoneinfo/."); else - log_warning("Failed to get target of /etc/localtime: %s", strerror(-r)); + log_warning_errno(r, "Failed to get target of /etc/localtime: %m"); } else { const char *e; @@ -254,10 +260,8 @@ static int context_start_ntp(Context *c, sd_bus *bus, sd_bus_error *error) { if (r < 0) { if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_NOT_FOUND) || sd_bus_error_has_name(error, "org.freedesktop.systemd1.LoadFailed") || - sd_bus_error_has_name(error, "org.freedesktop.systemd1.NoSuchUnit")) { - sd_bus_error_set_const(error, "org.freedesktop.timedate1.NoNTPSupport", "NTP not supported."); - return -ENOTSUP; - } + sd_bus_error_has_name(error, "org.freedesktop.systemd1.NoSuchUnit")) + return sd_bus_error_set_const(error, "org.freedesktop.timedate1.NoNTPSupport", "NTP not supported."); return r; } @@ -298,10 +302,8 @@ static int context_enable_ntp(Context*c, sd_bus *bus, sd_bus_error *error) { false); if (r < 0) { - if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_NOT_FOUND)) { - sd_bus_error_set_const(error, "org.freedesktop.timedate1.NoNTPSupport", "NTP not supported."); - return -ENOTSUP; - } + if (sd_bus_error_has_name(error, SD_BUS_ERROR_FILE_NOT_FOUND)) + return sd_bus_error_set_const(error, "org.freedesktop.timedate1.NoNTPSupport", "NTP not supported."); return r; } @@ -411,7 +413,7 @@ static int method_set_timezone(sd_bus *bus, sd_bus_message *m, void *userdata, s /* 1. Write new configuration file */ r = context_write_data_timezone(c); if (r < 0) { - log_error("Failed to set time zone: %s", strerror(-r)); + log_error_errno(r, "Failed to set time zone: %m"); return sd_bus_error_set_errnof(error, r, "Failed to set time zone: %s", strerror(-r)); } @@ -429,9 +431,9 @@ static int method_set_timezone(sd_bus *bus, sd_bus_message *m, void *userdata, s } log_struct(LOG_INFO, - MESSAGE_ID(SD_MESSAGE_TIMEZONE_CHANGE), + LOG_MESSAGE_ID(SD_MESSAGE_TIMEZONE_CHANGE), "TIMEZONE=%s", c->zone, - "MESSAGE=Changed time zone to '%s'.", c->zone, + LOG_MESSAGE("Changed time zone to '%s'.", c->zone), NULL); sd_bus_emit_properties_changed(bus, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "Timezone", NULL); @@ -467,7 +469,7 @@ static int method_set_local_rtc(sd_bus *bus, sd_bus_message *m, void *userdata, /* 1. Write new configuration file */ r = context_write_data_local_rtc(c); if (r < 0) { - log_error("Failed to set RTC to local/UTC: %s", strerror(-r)); + log_error_errno(r, "Failed to set RTC to local/UTC: %m"); return sd_bus_error_set_errnof(error, r, "Failed to set RTC to local/UTC: %s", strerror(-r)); } @@ -569,7 +571,7 @@ static int method_set_time(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bu /* Set system clock */ if (clock_settime(CLOCK_REALTIME, &ts) < 0) { - log_error("Failed to set local time: %m"); + log_error_errno(errno, "Failed to set local time: %m"); return sd_bus_error_set_errnof(error, errno, "Failed to set local time: %m"); } @@ -581,9 +583,9 @@ static int method_set_time(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bu clock_set_hwclock(tm); log_struct(LOG_INFO, - MESSAGE_ID(SD_MESSAGE_TIME_CHANGE), + LOG_MESSAGE_ID(SD_MESSAGE_TIME_CHANGE), "REALTIME="USEC_FMT, timespec_load(&ts), - "MESSAGE=Changed local time to %s", ctime(&ts.tv_sec), + LOG_MESSAGE("Changed local time to %s", ctime(&ts.tv_sec)), NULL); return sd_bus_reply_method_return(m, NULL); @@ -649,28 +651,20 @@ static int connect_bus(Context *c, sd_event *event, sd_bus **_bus) { assert(_bus); r = sd_bus_default_system(&bus); - if (r < 0) { - log_error("Failed to get system bus connection: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to get system bus connection: %m"); r = sd_bus_add_object_vtable(bus, NULL, "/org/freedesktop/timedate1", "org.freedesktop.timedate1", timedate_vtable, c); - if (r < 0) { - log_error("Failed to register object: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to register object: %m"); r = sd_bus_request_name(bus, "org.freedesktop.timedate1", 0); - if (r < 0) { - log_error("Failed to register name: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to register name: %m"); r = sd_bus_attach_event(bus, event, 0); - if (r < 0) { - log_error("Failed to attach bus to event loop: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to attach bus to event loop: %m"); *_bus = bus; bus = NULL; @@ -698,7 +692,7 @@ int main(int argc, char *argv[]) { r = sd_event_default(&event); if (r < 0) { - log_error("Failed to allocate event loop: %s", strerror(-r)); + log_error_errno(r, "Failed to allocate event loop: %m"); goto finish; } @@ -710,19 +704,19 @@ int main(int argc, char *argv[]) { r = context_read_data(&context); if (r < 0) { - log_error("Failed to read time zone data: %s", strerror(-r)); + log_error_errno(r, "Failed to read time zone data: %m"); goto finish; } r = context_read_ntp(&context, bus); if (r < 0) { - log_error("Failed to determine whether NTP is enabled: %s", strerror(-r)); + log_error_errno(r, "Failed to determine whether NTP is enabled: %m"); goto finish; } r = bus_event_loop_with_idle(event, bus, "org.freedesktop.timedate1", DEFAULT_EXIT_USEC, NULL, NULL); if (r < 0) { - log_error("Failed to run event loop: %s", strerror(-r)); + log_error_errno(r, "Failed to run event loop: %m"); goto finish; } diff --git a/src/timesync/timesyncd-conf.c b/src/timesync/timesyncd-conf.c index 4c2dcdb62b..be1f4bb151 100644 --- a/src/timesync/timesyncd-conf.c +++ b/src/timesync/timesyncd-conf.c @@ -97,8 +97,9 @@ int config_parse_servers( int manager_parse_config_file(Manager *m) { assert(m); - return config_parse(NULL, "/etc/systemd/timesyncd.conf", NULL, - "Time\0", - config_item_perf_lookup, timesyncd_gperf_lookup, - false, false, true, m); + return config_parse_many("/etc/systemd/timesyncd.conf", + CONF_DIRS_NULSTR("systemd/timesyncd.conf"), + "Time\0", + config_item_perf_lookup, timesyncd_gperf_lookup, + false, m); } diff --git a/src/timesync/timesyncd-manager.c b/src/timesync/timesyncd-manager.c index 3ae01eb8fc..ef5854d07e 100644 --- a/src/timesync/timesyncd-manager.c +++ b/src/timesync/timesyncd-manager.c @@ -132,6 +132,8 @@ struct ntp_msg { static int manager_arm_timer(Manager *m, usec_t next); static int manager_clock_watch_setup(Manager *m); +static int manager_listen_setup(Manager *m); +static void manager_listen_stop(Manager *m); static double ntp_ts_short_to_d(const struct ntp_ts_short *ts) { return be16toh(ts->sec) + (be16toh(ts->frac) / 65536.0); @@ -184,6 +186,10 @@ static int manager_send_request(Manager *m) { m->event_timeout = sd_event_source_unref(m->event_timeout); + r = manager_listen_setup(m); + if (r < 0) + return log_warning_errno(r, "Failed to setup connection socket: %m"); + /* * Set transmit timestamp, remember it; the server will send that back * as the origin timestamp and we have an indication that this is the @@ -204,7 +210,7 @@ static int manager_send_request(Manager *m) { m->pending = true; log_debug("Sent NTP request to %s (%s).", strna(pretty), m->current_server_name->string); } else { - log_debug("Sending NTP request to %s (%s) failed: %m", strna(pretty), m->current_server_name->string); + log_debug_errno(errno, "Sending NTP request to %s (%s) failed: %m", strna(pretty), m->current_server_name->string); return manager_connect(m); } @@ -216,10 +222,8 @@ static int manager_send_request(Manager *m) { m->retry_interval = NTP_POLL_INTERVAL_MIN_SEC * USEC_PER_SEC; r = manager_arm_timer(m, m->retry_interval); - if (r < 0) { - log_error("Failed to rearm timer: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to rearm timer: %m"); m->missed_replies++; if (m->missed_replies > NTP_MAX_MISSED_REPLIES) { @@ -229,10 +233,8 @@ static int manager_send_request(Manager *m) { clock_boottime_or_monotonic(), now(clock_boottime_or_monotonic()) + TIMEOUT_USEC, 0, manager_timeout, m); - if (r < 0) { - log_error("Failed to arm timeout timer: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to arm timeout timer: %m"); } return 0; @@ -250,7 +252,6 @@ static int manager_arm_timer(Manager *m, usec_t next) { int r; assert(m); - assert(m->event_receive); if (next == 0) { m->event_timer = sd_event_source_unref(m->event_timer); @@ -309,21 +310,15 @@ static int manager_clock_watch_setup(Manager *m) { safe_close(m->clock_watch_fd); m->clock_watch_fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC); - if (m->clock_watch_fd < 0) { - log_error("Failed to create timerfd: %m"); - return -errno; - } + if (m->clock_watch_fd < 0) + return log_error_errno(errno, "Failed to create timerfd: %m"); - if (timerfd_settime(m->clock_watch_fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0) { - log_error("Failed to set up timerfd: %m"); - return -errno; - } + if (timerfd_settime(m->clock_watch_fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0) + return log_error_errno(errno, "Failed to set up timerfd: %m"); r = sd_event_add_io(m->event, &m->event_clock_watch, m->clock_watch_fd, EPOLLIN, manager_clock_watch, m); - if (r < 0) { - log_error("Failed to create clock watch event source: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to create clock watch event source: %m"); return 0; } @@ -345,7 +340,7 @@ static int manager_adjust_clock(Manager *m, double offset, int leap_sec) { tmx.constant = log2i(m->poll_interval_usec / USEC_PER_SEC) - 4; tmx.maxerror = 0; tmx.esterror = 0; - log_debug(" adjust (slew): %+.3f sec\n", offset); + log_debug(" adjust (slew): %+.3f sec", offset); } else { tmx.modes = ADJ_STATUS | ADJ_NANO | ADJ_SETOFFSET; @@ -360,7 +355,7 @@ static int manager_adjust_clock(Manager *m, double offset, int leap_sec) { } m->jumped = true; - log_debug(" adjust (jump): %+.3f sec\n", offset); + log_debug(" adjust (jump): %+.3f sec", offset); } /* @@ -610,6 +605,9 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re m->pending = false; m->retry_interval = 0; + /* Stop listening */ + manager_listen_stop(m); + /* announce leap seconds */ if (NTP_FIELD_LEAP(ntpmsg.field) & NTP_LEAP_PLUSSEC) leap_sec = 1; @@ -678,7 +676,7 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re m->sync = true; r = manager_adjust_clock(m, offset, leap_sec); if (r < 0) - log_error("Failed to call clock_adjtime(): %m"); + log_error_errno(errno, "Failed to call clock_adjtime(): %m"); } log_info("interval/delta/delay/jitter/drift " USEC_FMT "s/%+.3fs/%.3fs/%.3fs/%+ippm%s", @@ -686,10 +684,8 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re spike ? " (ignored)" : ""); r = manager_arm_timer(m, m->poll_interval_usec); - if (r < 0) { - log_error("Failed to rearm timer: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to rearm timer: %m"); return 0; } @@ -702,7 +698,9 @@ static int manager_listen_setup(Manager *m) { assert(m); - assert(m->server_socket < 0); + if (m->server_socket >= 0) + return 0; + assert(!m->event_receive); assert(m->current_server_address); @@ -725,6 +723,13 @@ static int manager_listen_setup(Manager *m) { return sd_event_add_io(m->event, &m->event_receive, m->server_socket, EPOLLIN, manager_receive_response, m); } +static void manager_listen_stop(Manager *m) { + assert(m); + + m->event_receive = sd_event_source_unref(m->event_receive); + m->server_socket = safe_close(m->server_socket); +} + static int manager_begin(Manager *m) { _cleanup_free_ char *pretty = NULL; int r; @@ -741,12 +746,6 @@ static int manager_begin(Manager *m) { log_info("Using NTP server %s (%s).", strna(pretty), m->current_server_name->string); sd_notifyf(false, "STATUS=Using Time Server %s (%s).", strna(pretty), m->current_server_name->string); - r = manager_listen_setup(m); - if (r < 0) { - log_warning("Failed to setup connection socket: %s", strerror(-r)); - return r; - } - r = manager_clock_watch_setup(m); if (r < 0) return r; @@ -820,10 +819,8 @@ static int manager_resolve_handler(sd_resolve_query *q, int ret, const struct ad } r = server_address_new(m->current_server_name, &a, (const union sockaddr_union*) ai->ai_addr, ai->ai_addrlen); - if (r < 0) { - log_error("Failed to add server address: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to add server address: %m"); server_address_pretty(a, &pretty); log_debug("Resolved address %s for %s.", pretty, m->current_server_name->string); @@ -861,10 +858,8 @@ int manager_connect(Manager *m) { log_debug("Slowing down attempts to contact servers."); r = sd_event_add_time(m->event, &m->event_retry, clock_boottime_or_monotonic(), now(clock_boottime_or_monotonic()) + RETRY_USEC, 0, manager_retry_connect, m); - if (r < 0) { - log_error("Failed to create retry timer: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to create retry timer: %m"); return 0; } @@ -917,10 +912,8 @@ int manager_connect(Manager *m) { if (restart && !m->exhausted_servers && m->poll_interval_usec) { log_debug("Waiting after exhausting servers."); r = sd_event_add_time(m->event, &m->event_retry, clock_boottime_or_monotonic(), now(clock_boottime_or_monotonic()) + m->poll_interval_usec, 0, manager_retry_connect, m); - if (r < 0) { - log_error("Failed to create retry timer: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to create retry timer: %m"); m->exhausted_servers = true; @@ -946,10 +939,8 @@ int manager_connect(Manager *m) { log_debug("Resolving %s...", m->current_server_name->string); r = sd_resolve_getaddrinfo(m->resolve, &m->resolve_query, m->current_server_name->string, "123", &hints, manager_resolve_handler, m); - if (r < 0) { - log_error("Failed to create resolver: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to create resolver: %m"); return 1; } @@ -968,8 +959,7 @@ void manager_disconnect(Manager *m) { m->event_timer = sd_event_source_unref(m->event_timer); - m->event_receive = sd_event_source_unref(m->event_receive); - m->server_socket = safe_close(m->server_socket); + manager_listen_stop(m); m->event_clock_watch = sd_event_source_unref(m->event_clock_watch); m->clock_watch_fd = safe_close(m->clock_watch_fd); diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c index ee3bc99ae0..f7e089fc0c 100644 --- a/src/timesync/timesyncd.c +++ b/src/timesync/timesyncd.c @@ -73,7 +73,7 @@ static int load_clock_timestamp(uid_t uid, gid_t gid) { format_timestamp(date, sizeof(date), min)); if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, min)) < 0) - log_error("Failed to restore system clock: %m"); + log_error_errno(errno, "Failed to restore system clock: %m"); } return 0; @@ -101,7 +101,7 @@ int main(int argc, char *argv[]) { r = get_user_creds(&user, &uid, &gid, NULL, NULL); if (r < 0) { - log_error("Cannot resolve user name %s: %s", user, strerror(-r)); + log_error_errno(r, "Cannot resolve user name %s: %m", user); goto finish; } @@ -117,7 +117,7 @@ int main(int argc, char *argv[]) { r = manager_new(&m); if (r < 0) { - log_error("Failed to allocate manager: %s", strerror(-r)); + log_error_errno(r, "Failed to allocate manager: %m"); goto finish; } @@ -129,7 +129,7 @@ int main(int argc, char *argv[]) { r = manager_parse_config_file(m); if (r < 0) - log_warning("Failed to parse configuration file: %s", strerror(-r)); + log_warning_errno(r, "Failed to parse configuration file: %m"); log_debug("systemd-timesyncd running as pid %lu", (unsigned long) getpid()); sd_notify(false, @@ -144,7 +144,7 @@ int main(int argc, char *argv[]) { r = sd_event_loop(m->event); if (r < 0) { - log_error("Failed to run event loop: %s", strerror(-r)); + log_error_errno(r, "Failed to run event loop: %m"); goto finish; } diff --git a/src/timesync/timesyncd.conf.in b/src/timesync/timesyncd.conf.in index 674a51dbd7..fc3c6c49cf 100644 --- a/src/timesync/timesyncd.conf.in +++ b/src/timesync/timesyncd.conf.in @@ -5,6 +5,9 @@ # the Free Software Foundation; either version 2.1 of the License, or # (at your option) any later version. # +# You can override the directives in this file by creating files in +# /etc/systemd/timesyncd.conf.d/*.conf. +# # See timesyncd.conf(5) for details [Time] diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index 1e4675f87e..d40bd96f1b 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -39,6 +39,7 @@ #include <glob.h> #include <fnmatch.h> #include <sys/capability.h> +#include <sys/xattr.h> #include "log.h" #include "util.h" @@ -71,6 +72,7 @@ typedef enum ItemType { CREATE_CHAR_DEVICE = 'c', CREATE_BLOCK_DEVICE = 'b', COPY_FILES = 'C', + SET_XATTR = 't', /* These ones take globs */ WRITE_FILE = 'w', @@ -88,6 +90,7 @@ typedef struct Item { char *path; char *argument; + char **xattrs; uid_t uid; gid_t gid; mode_t mode; @@ -117,15 +120,7 @@ static char **arg_include_prefixes = NULL; static char **arg_exclude_prefixes = NULL; static char *arg_root = NULL; -static const char conf_file_dirs[] = - "/etc/tmpfiles.d\0" - "/run/tmpfiles.d\0" - "/usr/local/lib/tmpfiles.d\0" - "/usr/lib/tmpfiles.d\0" -#ifdef HAVE_SPLIT_USR - "/lib/tmpfiles.d\0" -#endif - ; +static const char conf_file_dirs[] = CONF_DIRS_NULSTR("tmpfiles"); #define MAX_DEPTH 256 @@ -299,9 +294,9 @@ static int dir_cleanup( /* FUSE, NFS mounts, SELinux might return EACCES */ if (errno == EACCES) - log_debug("stat(%s/%s) failed: %m", p, dent->d_name); + log_debug_errno(errno, "stat(%s/%s) failed: %m", p, dent->d_name); else - log_error("stat(%s/%s) failed: %m", p, dent->d_name); + log_error_errno(errno, "stat(%s/%s) failed: %m", p, dent->d_name); r = -errno; continue; } @@ -349,7 +344,7 @@ static int dir_cleanup( sub_dir = xopendirat(dirfd(d), dent->d_name, O_NOFOLLOW|O_NOATIME); if (!sub_dir) { if (errno != ENOENT) { - log_error("opendir(%s/%s) failed: %m", p, dent->d_name); + log_error_errno(errno, "opendir(%s/%s) failed: %m", p, dent->d_name); r = -errno; } @@ -382,7 +377,7 @@ static int dir_cleanup( if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) { if (errno != ENOENT && errno != ENOTEMPTY) { - log_error("rmdir(%s): %m", sub_path); + log_error_errno(errno, "rmdir(%s): %m", sub_path); r = -errno; } } @@ -430,7 +425,7 @@ static int dir_cleanup( if (unlinkat(dirfd(d), dent->d_name, 0) < 0) { if (errno != ENOENT) { - log_error("unlink(%s): %m", sub_path); + log_error_errno(errno, "unlink(%s): %m", sub_path); r = -errno; } } @@ -446,7 +441,7 @@ finish: times[1] = ds->st_mtim; if (futimens(dirfd(d), times) < 0) - log_error("utimensat(%s): %m", p); + log_error_errno(errno, "utimensat(%s): %m", p); } return r; @@ -477,26 +472,85 @@ static int item_set_perms(Item *i, const char *path) { } if (!st_valid || m != (st.st_mode & 07777)) { - if (chmod(path, m) < 0) { - log_error("chmod(%s) failed: %m", path); - return -errno; - } + if (chmod(path, m) < 0) + return log_error_errno(errno, "chmod(%s) failed: %m", path); } } if ((!st_valid || (i->uid != st.st_uid || i->gid != st.st_gid)) && (i->uid_set || i->gid_set)) if (chown(path, - i->uid_set ? i->uid : (uid_t) -1, - i->gid_set ? i->gid : (gid_t) -1) < 0) { + i->uid_set ? i->uid : UID_INVALID, + i->gid_set ? i->gid : GID_INVALID) < 0) { - log_error("chown(%s) failed: %m", path); + log_error_errno(errno, "chown(%s) failed: %m", path); return -errno; } return label_fix(path, false, false); } +static int get_xattrs_from_arg(Item *i) { + char *xattr; + const char *p; + int r; + + assert(i); + + if (!i->argument) { + log_error("%s: Argument can't be empty!", i->path); + return -EBADMSG; + } + p = i->argument; + + while ((r = unquote_first_word(&p, &xattr, false)) > 0) { + _cleanup_free_ char *tmp = NULL, *name = NULL, *value = NULL; + r = split_pair(xattr, "=", &name, &value); + if (r < 0) { + log_warning("Illegal xattr found: \"%s\" - ignoring.", xattr); + free(xattr); + continue; + } + free(xattr); + if (streq(name, "") || streq(value, "")) { + log_warning("Malformed xattr found: \"%s=%s\" - ignoring.", name, value); + continue; + } + tmp = unquote(value, "\""); + if (!tmp) + return log_oom(); + free(value); + value = cunescape(tmp); + if (!value) + return log_oom(); + if (strv_consume_pair(&i->xattrs, name, value) < 0) + return log_oom(); + name = value = NULL; + } + + return r; +} + +static int item_set_xattrs(Item *i, const char *path) { + char **name, **value; + + assert(i); + assert(path); + + if (strv_isempty(i->xattrs)) + return 0; + + STRV_FOREACH_PAIR(name, value, i->xattrs) { + int n; + n = strlen(*value); + if (lsetxattr(path, *name, *value, n, 0) < 0) { + log_error("Setting extended attribute %s=%s on %s failed: %m", *name, *value, path); + return -errno; + } + } + return 0; +} + static int write_one_file(Item *i, const char *path) { _cleanup_close_ int fd = -1; int flags, r = 0; @@ -518,7 +572,7 @@ static int write_one_file(Item *i, const char *path) { if (i->type == WRITE_FILE && errno == ENOENT) return 0; - log_error("Failed to create file %s: %m", path); + log_error_errno(errno, "Failed to create file %s: %m", path); return -errno; } @@ -542,10 +596,8 @@ static int write_one_file(Item *i, const char *path) { fd = safe_close(fd); - if (stat(path, &st) < 0) { - log_error("stat(%s) failed: %m", path); - return -errno; - } + if (stat(path, &st) < 0) + return log_error_errno(errno, "stat(%s) failed: %m", path); if (!S_ISREG(st.st_mode)) { log_error("%s is not a file.", path); @@ -556,6 +608,10 @@ static int write_one_file(Item *i, const char *path) { if (r < 0) return r; + r = item_set_xattrs(i, i->path); + if (r < 0) + return r; + return 0; } @@ -636,7 +692,7 @@ static int glob_item(Item *i, int (*action)(Item *, const char *)) { if (errno == 0) errno = EIO; - log_error("glob(%s) failed: %m", i->path); + log_error_errno(errno, "glob(%s) failed: %m", i->path); return -errno; } @@ -675,20 +731,14 @@ static int create_item(Item *i) { if (r < 0) { struct stat a, b; - if (r != -EEXIST) { - log_error("Failed to copy files to %s: %s", i->path, strerror(-r)); - return -r; - } + if (r != -EEXIST) + return log_error_errno(r, "Failed to copy files to %s: %m", i->path); - if (stat(i->argument, &a) < 0) { - log_error("stat(%s) failed: %m", i->argument); - return -errno; - } + if (stat(i->argument, &a) < 0) + return log_error_errno(errno, "stat(%s) failed: %m", i->argument); - if (stat(i->path, &b) < 0) { - log_error("stat(%s) failed: %m", i->path); - return -errno; - } + if (stat(i->path, &b) < 0) + return log_error_errno(errno, "stat(%s) failed: %m", i->path); if ((a.st_mode ^ b.st_mode) & S_IFMT) { log_debug("Can't copy to %s, file exists already and is of different type", i->path); @@ -718,15 +768,11 @@ static int create_item(Item *i) { } if (r < 0) { - if (r != -EEXIST) { - log_error("Failed to create directory %s: %s", i->path, strerror(-r)); - return r; - } + if (r != -EEXIST) + return log_error_errno(r, "Failed to create directory %s: %m", i->path); - if (stat(i->path, &st) < 0) { - log_error("stat(%s) failed: %m", i->path); - return -errno; - } + if (stat(i->path, &st) < 0) + return log_error_errno(errno, "stat(%s) failed: %m", i->path); if (!S_ISDIR(st.st_mode)) { log_debug("%s already exists and is not a directory.", i->path); @@ -738,6 +784,10 @@ static int create_item(Item *i) { if (r < 0) return r; + r = item_set_xattrs(i, i->path); + if (r < 0) + return r; + break; case CREATE_FIFO: @@ -749,15 +799,11 @@ static int create_item(Item *i) { } if (r < 0) { - if (errno != EEXIST) { - log_error("Failed to create fifo %s: %m", i->path); - return -errno; - } + if (errno != EEXIST) + return log_error_errno(errno, "Failed to create fifo %s: %m", i->path); - if (stat(i->path, &st) < 0) { - log_error("stat(%s) failed: %m", i->path); - return -errno; - } + if (stat(i->path, &st) < 0) + return log_error_errno(errno, "stat(%s) failed: %m", i->path); if (!S_ISFIFO(st.st_mode)) { @@ -769,10 +815,8 @@ static int create_item(Item *i) { mac_selinux_create_file_clear(); } - if (r < 0) { - log_error("Failed to create fifo %s: %s", i->path, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to create fifo %s: %m", i->path); } else { log_debug("%s is not a fifo.", i->path); return 0; @@ -784,6 +828,10 @@ static int create_item(Item *i) { if (r < 0) return r; + r = item_set_xattrs(i, i->path); + if (r < 0) + return r; + break; case CREATE_SYMLINK: @@ -795,10 +843,8 @@ static int create_item(Item *i) { if (r < 0) { _cleanup_free_ char *x = NULL; - if (errno != EEXIST) { - log_error("symlink(%s, %s) failed: %m", i->argument, i->path); - return -errno; - } + if (errno != EEXIST) + return log_error_errno(errno, "symlink(%s, %s) failed: %m", i->argument, i->path); r = readlink_malloc(i->path, &x); if (r < 0 || !streq(i->argument, x)) { @@ -808,10 +854,8 @@ static int create_item(Item *i) { r = symlink_atomic(i->argument, i->path); mac_selinux_create_file_clear(); - if (r < 0) { - log_error("symlink(%s, %s) failed: %s", i->argument, i->path, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "symlink(%s, %s) failed: %m", i->argument, i->path); } else { log_debug("%s is not a symlink or does not point to the correct path.", i->path); return 0; @@ -819,6 +863,10 @@ static int create_item(Item *i) { } } + r = item_set_xattrs(i, i->path); + if (r < 0) + return r; + break; case CREATE_BLOCK_DEVICE: @@ -850,15 +898,11 @@ static int create_item(Item *i) { return 0; } - if (errno != EEXIST) { - log_error("Failed to create device node %s: %m", i->path); - return -errno; - } + if (errno != EEXIST) + return log_error_errno(errno, "Failed to create device node %s: %m", i->path); - if (stat(i->path, &st) < 0) { - log_error("stat(%s) failed: %m", i->path); - return -errno; - } + if (stat(i->path, &st) < 0) + return log_error_errno(errno, "stat(%s) failed: %m", i->path); if ((st.st_mode & S_IFMT) != file_type) { @@ -870,10 +914,8 @@ static int create_item(Item *i) { mac_selinux_create_file_clear(); } - if (r < 0) { - log_error("Failed to create device node %s: %s", i->path, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to create device node %s: %m", i->path); } else { log_debug("%s is not a device node.", i->path); return 0; @@ -885,6 +927,10 @@ static int create_item(Item *i) { if (r < 0) return r; + r = item_set_xattrs(i, i->path); + if (r < 0) + return r; + break; } @@ -901,7 +947,12 @@ static int create_item(Item *i) { r = glob_item(i, item_set_perms_recursive); if (r < 0) return r; + break; + case SET_XATTR: + r = item_set_xattrs(i, i->path); + if (r < 0) + return r; break; } @@ -931,13 +982,12 @@ static int remove_item_instance(Item *i, const char *instance) { case RECURSIVE_RELABEL_PATH: case WRITE_FILE: case COPY_FILES: + case SET_XATTR: break; case REMOVE_PATH: - if (remove(instance) < 0 && errno != ENOENT) { - log_error("remove(%s): %m", instance); - return -errno; - } + if (remove(instance) < 0 && errno != ENOENT) + return log_error_errno(errno, "remove(%s): %m", instance); break; @@ -946,10 +996,8 @@ static int remove_item_instance(Item *i, const char *instance) { /* FIXME: we probably should use dir_cleanup() here * instead of rm_rf() so that 'x' is honoured. */ r = rm_rf_dangerous(instance, false, i->type == RECURSIVE_REMOVE_PATH, false); - if (r < 0 && r != -ENOENT) { - log_error("rm_rf(%s): %s", instance, strerror(-r)); - return r; - } + if (r < 0 && r != -ENOENT) + return log_error_errno(r, "rm_rf(%s): %m", instance); break; } @@ -978,6 +1026,7 @@ static int remove_item(Item *i) { case RECURSIVE_RELABEL_PATH: case WRITE_FILE: case COPY_FILES: + case SET_XATTR: break; case REMOVE_PATH: @@ -1013,24 +1062,20 @@ static int clean_item_instance(Item *i, const char* instance) { if (errno == ENOENT || errno == ENOTDIR) return 0; - log_error("Failed to open directory %s: %m", i->path); + log_error_errno(errno, "Failed to open directory %s: %m", i->path); return -errno; } - if (fstat(dirfd(d), &s) < 0) { - log_error("stat(%s) failed: %m", i->path); - return -errno; - } + if (fstat(dirfd(d), &s) < 0) + return log_error_errno(errno, "stat(%s) failed: %m", i->path); if (!S_ISDIR(s.st_mode)) { log_error("%s is not a directory.", i->path); return -ENOTDIR; } - if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0) { - log_error("stat(%s/..) failed: %m", i->path); - return -errno; - } + if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0) + return log_error_errno(errno, "stat(%s/..) failed: %m", i->path); mountpoint = s.st_dev != ps.st_dev || (s.st_dev == ps.st_dev && s.st_ino == ps.st_ino); @@ -1105,6 +1150,7 @@ static void item_free(Item *i) { free(i->path); free(i->argument); + strv_free(i->xattrs); free(i); } @@ -1303,6 +1349,16 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { break; } + case SET_XATTR: + if (!i->argument) { + log_error("[%s:%u] Set extended attribute requires argument.", fname, line); + return -EBADMSG; + } + r = get_xattrs_from_arg(i); + if (r < 0) + return r; + break; + default: log_error("[%s:%u] Unknown command type '%c'.", fname, line, type); return -EBADMSG; @@ -1396,18 +1452,33 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { existing = hashmap_get(h, i->path); if (existing) { - - /* Two identical items are fine */ - if (!item_equal(existing, i)) - log_warning("Two or more conflicting lines for %s configured, ignoring.", i->path); - - return 0; - } - - r = hashmap_put(h, i->path, i); - if (r < 0) { - log_error("Failed to insert item %s: %s", i->path, strerror(-r)); - return r; + if (i->type == SET_XATTR) { + r = strv_extend_strv(&existing->xattrs, i->xattrs); + if (r < 0) + return log_oom(); + return 0; + } else if (existing->type == SET_XATTR) { + r = strv_extend_strv(&i->xattrs, existing->xattrs); + if (r < 0) + return log_oom(); + r = hashmap_replace(h, i->path, i); + if (r < 0) { + log_error("Failed to replace item for %s.", i->path); + return r; + } + item_free(existing); + } else { + /* Two identical items are fine */ + if (!item_equal(existing, i)) + log_warning("Two or more conflicting lines for %s configured, ignoring.", i->path); + return 0; + } + } else { + r = hashmap_put(h, i->path, i); + if (r < 0) { + log_error("Failed to insert item %s: %s", i->path, strerror(-r)); + return r; + } } i = NULL; /* avoid cleanup */ @@ -1539,8 +1610,7 @@ static int read_config_file(const char *fn, bool ignore_enoent) { if (ignore_enoent && r == -ENOENT) return 0; - log_error("Failed to open '%s', ignoring: %s", fn, strerror(-r)); - return r; + return log_error_errno(r, "Failed to open '%s', ignoring: %m", fn); } FOREACH_LINE(line, f, break) { @@ -1587,7 +1657,7 @@ static int read_config_file(const char *fn, bool ignore_enoent) { } if (ferror(f)) { - log_error("Failed to read from file %s: %m", fn); + log_error_errno(errno, "Failed to read from file %s: %m", fn); if (r == 0) r = -EIO; } @@ -1637,7 +1707,7 @@ int main(int argc, char *argv[]) { r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs); if (r < 0) { - log_error("Failed to enumerate tmpfiles.d files: %s", strerror(-r)); + log_error_errno(r, "Failed to enumerate tmpfiles.d files: %m"); goto finish; } diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c index e6dc84b440..5fc27f9ae5 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -90,10 +90,8 @@ static int ask_password_plymouth( return -errno; r = connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)); - if (r < 0) { - log_error("Failed to connect to Plymouth: %m"); - return -errno; - } + if (r < 0) + return log_error_errno(errno, "Failed to connect to Plymouth: %m"); if (accept_cached) { packet = strdup("c"); @@ -105,9 +103,9 @@ static int ask_password_plymouth( if (!packet) return log_oom(); - k = loop_write(fd, packet, n + 1, true); - if (k != n + 1) - return k < 0 ? (int) k : -EIO; + r = loop_write(fd, packet, n + 1, true); + if (r < 0) + return r; pollfd[POLL_SOCKET].fd = fd; pollfd[POLL_SOCKET].events = POLLIN; @@ -167,9 +165,9 @@ static int ask_password_plymouth( if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) return -ENOMEM; - k = loop_write(fd, packet, n+1, true); - if (k != n + 1) - return k < 0 ? (int) k : -EIO; + r = loop_write(fd, packet, n+1, true); + if (r < 0) + return r; accept_cached = false; p = 0; @@ -338,16 +336,12 @@ static int parse_password(const char *filename, char **wall) { /* If the query went away, that's OK */ return 0; - if (r < 0) { - log_error("Failed to query password: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to query password: %m"); socket_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); - if (socket_fd < 0) { - log_error("socket(): %m"); - return -errno; - } + if (socket_fd < 0) + return log_error_errno(errno, "socket(): %m"); sa.un.sun_family = AF_UNIX; strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path)); @@ -355,7 +349,7 @@ static int parse_password(const char *filename, char **wall) { r = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)); if (r < 0) { - log_error("Failed to send: %m"); + log_error_errno(errno, "Failed to send: %m"); return r; } } @@ -430,7 +424,7 @@ static int show_passwords(void) { if (errno == ENOENT) return 0; - log_error("opendir(/run/systemd/ask-password): %m"); + log_error_errno(errno, "opendir(/run/systemd/ask-password): %m"); return -errno; } @@ -504,7 +498,7 @@ static int watch_passwords(void) { for (;;) { r = show_passwords(); if (r < 0) - log_error("Failed to show password: %s", strerror(-r)); + log_error_errno(r, "Failed to show password: %m"); if (poll(pollfd, _FD_MAX, -1) < 0) { if (errno == EINTR) @@ -642,7 +636,7 @@ int main(int argc, char *argv[]) { r = show_passwords(); if (r < 0) - log_error("Error: %s", strerror(-r)); + log_error_errno(r, "Error: %m"); finish: return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; diff --git a/src/udev/accelerometer/accelerometer.c b/src/udev/accelerometer/accelerometer.c index 4513bc63c2..dd4b7dc8a2 100644 --- a/src/udev/accelerometer/accelerometer.c +++ b/src/udev/accelerometer/accelerometer.c @@ -69,14 +69,6 @@ #define LONG(x) ((x)/BITS_PER_LONG) #define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1) -_printf_(6,0) -static void log_fn(struct udev *udev, int priority, - const char *file, int line, const char *fn, - const char *format, va_list args) -{ - log_metav(priority, file, line, fn, format, args); -} - typedef enum { ORIENTATION_UNDEFINED, ORIENTATION_NORMAL, @@ -233,8 +225,6 @@ int main (int argc, char** argv) if (udev == NULL) return 1; - udev_set_log_fn(udev, log_fn); - /* CLI argument parsing */ while (1) { int option; @@ -247,7 +237,6 @@ int main (int argc, char** argv) case 'd': log_set_target(LOG_TARGET_CONSOLE); log_set_max_level(LOG_DEBUG); - udev_set_log_priority(udev, LOG_DEBUG); log_open(); break; case 'h': diff --git a/src/udev/ata_id/ata_id.c b/src/udev/ata_id/ata_id.c index bfde57223a..89628c929d 100644 --- a/src/udev/ata_id/ata_id.c +++ b/src/udev/ata_id/ata_id.c @@ -405,14 +405,6 @@ out: return ret; } -_printf_(6,0) -static void log_fn(struct udev *udev, int priority, - const char *file, int line, const char *fn, - const char *format, va_list args) -{ - log_metav(priority, file, line, fn, format, args); -} - int main(int argc, char *argv[]) { struct udev *udev; @@ -442,8 +434,6 @@ int main(int argc, char *argv[]) if (udev == NULL) goto exit; - udev_set_log_fn(udev, log_fn); - while (1) { int option; @@ -504,7 +494,7 @@ int main(int argc, char *argv[]) } else { /* If this fails, then try HDIO_GET_IDENTITY */ if (ioctl(fd, HDIO_GET_IDENTITY, &id) != 0) { - log_debug("HDIO_GET_IDENTITY failed for '%s': %m", node); + log_debug_errno(errno, "HDIO_GET_IDENTITY failed for '%s': %m", node); rc = 2; goto close; } diff --git a/src/udev/cdrom_id/cdrom_id.c b/src/udev/cdrom_id/cdrom_id.c index 7a4b98726c..48ceb657e0 100644 --- a/src/udev/cdrom_id/cdrom_id.c +++ b/src/udev/cdrom_id/cdrom_id.c @@ -37,14 +37,6 @@ #include "libudev.h" #include "libudev-private.h" -_printf_(6,0) -static void log_fn(struct udev *udev, int priority, - const char *file, int line, const char *fn, - const char *format, va_list args) -{ - log_metav(priority, file, line, fn, format, args); -} - /* device info */ static unsigned int cd_cd_rom; static unsigned int cd_cd_r; @@ -875,8 +867,6 @@ int main(int argc, char *argv[]) if (udev == NULL) goto exit; - udev_set_log_fn(udev, log_fn); - while (1) { int option; @@ -897,7 +887,6 @@ int main(int argc, char *argv[]) case 'd': log_set_target(LOG_TARGET_CONSOLE); log_set_max_level(LOG_DEBUG); - udev_set_log_priority(udev, LOG_DEBUG); log_open(); break; case 'h': @@ -922,7 +911,7 @@ int main(int argc, char *argv[]) goto exit; } - srand((unsigned int)getpid()); + initialize_srand(); for (cnt = 20; cnt > 0; cnt--) { struct timespec duration; diff --git a/src/udev/collect/collect.c b/src/udev/collect/collect.c index 4ecb6b0d19..90df360eb2 100644 --- a/src/udev/collect/collect.c +++ b/src/udev/collect/collect.c @@ -86,12 +86,12 @@ static void usage(void) */ static int prepare(char *dir, char *filename) { - struct stat statbuf; char buf[512]; - int fd; + int r, fd; - if (stat(dir, &statbuf) < 0) - mkdir(dir, 0700); + r = mkdir(dir, 0700); + if (r < 0 && errno != EEXIST) + return -errno; snprintf(buf, sizeof(buf), "%s/%s", dir, filename); @@ -254,7 +254,7 @@ static void reject(char *us) * kickout * * Remove all IDs in the internal list which are not part - * of the list passed via the commandline. + * of the list passed via the command line. */ static void kickout(void) { diff --git a/src/udev/net/ethtool-util.c b/src/udev/net/ethtool-util.c index 54cb928091..ec67126b21 100644 --- a/src/udev/net/ethtool-util.c +++ b/src/udev/net/ethtool-util.c @@ -75,10 +75,8 @@ int ethtool_get_driver(int *fd, const char *ifname, char **ret) { if (*fd < 0) { r = ethtool_connect(fd); - if (r < 0) { - log_warning("link_config: could not connect to ethtool: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); } strscpy(ifr.ifr_name, IFNAMSIZ, ifname); @@ -111,10 +109,8 @@ int ethtool_set_speed(int *fd, const char *ifname, unsigned int speed, Duplex du if (*fd < 0) { r = ethtool_connect(fd); - if (r < 0) { - log_warning("link_config: could not connect to ethtool: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); } strscpy(ifr.ifr_name, IFNAMSIZ, ifname); @@ -171,10 +167,8 @@ int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol) { if (*fd < 0) { r = ethtool_connect(fd); - if (r < 0) { - log_warning("link_config: could not connect to ethtool: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); } strscpy(ifr.ifr_name, IFNAMSIZ, ifname); diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf index f562498f6d..191ab68fa1 100644 --- a/src/udev/net/link-config-gperf.gperf +++ b/src/udev/net/link-config-gperf.gperf @@ -17,6 +17,7 @@ struct ConfigPerfItem; %includes %% Match.MACAddress, config_parse_hwaddr, 0, offsetof(link_config, match_mac) +Match.OriginalName, config_parse_ifname, 0, offsetof(link_config, match_name) Match.Path, config_parse_string, 0, offsetof(link_config, match_path) Match.Driver, config_parse_string, 0, offsetof(link_config, match_driver) Match.Type, config_parse_string, 0, offsetof(link_config, match_type) diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index 25e3cc8dfb..bf24f6a7f7 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -20,6 +20,7 @@ ***/ #include <netinet/ether.h> +#include <linux/netdevice.h> #include "sd-id128.h" @@ -95,6 +96,7 @@ static void link_configs_free(link_config_ctx *ctx) { LIST_FOREACH_SAFE(links, link, link_next, ctx->links) { free(link->filename); + free(link->name); free(link->match_path); free(link->match_driver); free(link->match_type); @@ -174,11 +176,10 @@ static bool enable_name_policy(void) { size_t l; r = proc_cmdline(&line); - if (r < 0) - log_warning("Failed to read /proc/cmdline, ignoring: %s", - strerror(-r)); - if (r <= 0) + if (r < 0) { + log_warning_errno(r, "Failed to read /proc/cmdline, ignoring: %m"); return true; + } FOREACH_WORD_QUOTED(word, l, line, state) if (strneq(word, "net.ifnames=0", l)) @@ -196,17 +197,15 @@ int link_config_load(link_config_ctx *ctx) { if (!enable_name_policy()) { ctx->enable_name_policy = false; - log_info("Network interface NamePolicy= disabled on kernel commandline, ignoring."); + log_info("Network interface NamePolicy= disabled on kernel command line, ignoring."); } /* update timestamp */ paths_check_timestamp(link_dirs, &ctx->link_dirs_ts_usec, true); r = conf_files_list_strv(&files, ".link", NULL, link_dirs); - if (r < 0) { - log_error("failed to enumerate link files: %s", strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "failed to enumerate link files: %m"); STRV_FOREACH_BACKWARDS(f, files) { r = load_link(ctx, *f); @@ -226,21 +225,45 @@ int link_config_get(link_config_ctx *ctx, struct udev_device *device, link_config *link; LIST_FOREACH(links, link, ctx->links) { - const char* attr_value = udev_device_get_sysattr_value(device, "address"); + const char* attr_value; + + attr_value = udev_device_get_sysattr_value(device, "address"); if (net_match_config(link->match_mac, link->match_path, link->match_driver, - link->match_type, NULL, link->match_host, + link->match_type, link->match_name, link->match_host, link->match_virt, link->match_kernel, link->match_arch, attr_value ? ether_aton(attr_value) : NULL, udev_device_get_property_value(device, "ID_PATH"), udev_device_get_driver(udev_device_get_parent(device)), udev_device_get_property_value(device, "ID_NET_DRIVER"), udev_device_get_devtype(device), - NULL)) { + udev_device_get_sysname(device))) { + if (link->match_name) { + unsigned char name_assign_type = NET_NAME_UNKNOWN; + + attr_value = udev_device_get_sysattr_value(device, "name_assign_type"); + if (attr_value) + (void)safe_atou8(attr_value, &name_assign_type); + + if (name_assign_type == NET_NAME_ENUM) { + log_warning("Config file %s applies to device based on potentially unpredictable interface name '%s'", + link->filename, udev_device_get_sysname(device)); + *ret = link; + + return 0; + } else if (name_assign_type == NET_NAME_RENAMED) { + log_warning("Config file %s matches device based on renamed interface name '%s', ignoring", + link->filename, udev_device_get_sysname(device)); + + continue; + } + } + log_debug("Config file %s applies to device %s", - link->filename, - udev_device_get_sysname(device)); + link->filename, udev_device_get_sysname(device)); + *ret = link; + return 0; } } @@ -343,14 +366,14 @@ int link_config_apply(link_config_ctx *ctx, link_config *config, r = ethtool_set_speed(&ctx->ethtool_fd, old_name, config->speed / 1024, config->duplex); if (r < 0) - log_warning("Could not set speed or duplex of %s to %u Mbps (%s): %s", - old_name, config->speed / 1024, - duplex_to_string(config->duplex), strerror(-r)); + log_warning_errno(r, "Could not set speed or duplex of %s to %u Mbps (%s): %m", + old_name, config->speed / 1024, + duplex_to_string(config->duplex)); r = ethtool_set_wol(&ctx->ethtool_fd, old_name, config->wol); if (r < 0) - log_warning("Could not set WakeOnLan of %s to %s: %s", - old_name, wol_to_string(config->wol), strerror(-r)); + log_warning_errno(r, "Could not set WakeOnLan of %s to %s: %m", + old_name, wol_to_string(config->wol)); ifindex = udev_device_get_ifindex(device); if (ifindex <= 0) { @@ -422,11 +445,8 @@ int link_config_apply(link_config_ctx *ctx, link_config *config, r = rtnl_set_link_properties(&ctx->rtnl, ifindex, config->alias, mac, config->mtu); - if (r < 0) { - log_warning("Could not set Alias, MACAddress or MTU on %s: %s", - old_name, strerror(-r)); - return r; - } + if (r < 0) + return log_warning_errno(r, "Could not set Alias, MACAddress or MTU on %s: %m", old_name); *name = new_name; diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h index 5f3d4ad142..688f836144 100644 --- a/src/udev/net/link-config.h +++ b/src/udev/net/link-config.h @@ -22,11 +22,10 @@ #pragma once #include "ethtool-util.h" - -#include "condition-util.h" -#include "libudev.h" +#include "condition.h" #include "util.h" #include "list.h" +#include "libudev.h" typedef struct link_config_ctx link_config_ctx; typedef struct link_config link_config; @@ -56,6 +55,7 @@ struct link_config { char *match_path; char *match_driver; char *match_type; + char *match_name; Condition *match_host; Condition *match_virt; Condition *match_kernel; diff --git a/src/udev/scsi_id/scsi_id.c b/src/udev/scsi_id/scsi_id.c index 4d9378a5c0..27adb09d15 100644 --- a/src/udev/scsi_id/scsi_id.c +++ b/src/udev/scsi_id/scsi_id.c @@ -63,14 +63,6 @@ static char model_enc_str[256]; static char revision_str[16]; static char type_str[16]; -_printf_(6,0) -static void log_fn(struct udev *udev, int priority, - const char *file, int line, const char *fn, - const char *format, va_list args) -{ - log_metav(priority, file, line, fn, format, args); -} - static void set_type(const char *from, char *to, size_t len) { int type_num; @@ -182,7 +174,7 @@ static int get_file_options(struct udev *udev, if (errno == ENOENT) return 1; else { - log_error("can't open %s: %m", config_file); + log_error_errno(errno, "can't open %s: %m", config_file); return -1; } } @@ -317,8 +309,8 @@ static void help(void) { " -f,--config= location of config file\n" " -p,--page=0x80|0x83|pre-spc3-83 SCSI page (0x80, 0x83, pre-spc3-83)\n" " -s,--sg-version=3|4 use SGv3 or SGv4\n" - " -b,--blacklisted threat device as blacklisted\n" - " -g,--whitelisted threat device as whitelisted\n" + " -b,--blacklisted treat device as blacklisted\n" + " -g,--whitelisted treat device as whitelisted\n" " -u,--replace-whitespace replace all whitespace by underscores\n" " -v,--verbose verbose logging\n" " --version print version\n" @@ -390,7 +382,6 @@ static int set_options(struct udev *udev, case 'v': log_set_target(LOG_TARGET_CONSOLE); log_set_max_level(LOG_DEBUG); - udev_set_log_priority(udev, LOG_DEBUG); log_open(); break; @@ -591,8 +582,6 @@ int main(int argc, char **argv) if (udev == NULL) goto exit; - udev_set_log_fn(udev, log_fn); - /* * Get config file options. */ diff --git a/src/udev/scsi_id/scsi_serial.c b/src/udev/scsi_id/scsi_serial.c index b3d20a3c2d..dcfff1d4ea 100644 --- a/src/udev/scsi_id/scsi_serial.c +++ b/src/udev/scsi_id/scsi_serial.c @@ -361,7 +361,7 @@ resend: dev_scsi->use_sg = 3; goto resend; } - log_debug("%s: ioctl failed: %m", dev_scsi->kernel); + log_debug_errno(errno, "%s: ioctl failed: %m", dev_scsi->kernel); goto error; } @@ -819,12 +819,12 @@ int scsi_std_inquiry(struct udev *udev, fd = open(devname, O_RDONLY | O_NONBLOCK | O_CLOEXEC); if (fd < 0) { - log_debug("scsi_id: cannot open %s: %m", devname); + log_debug_errno(errno, "scsi_id: cannot open %s: %m", devname); return 1; } if (fstat(fd, &statbuf) < 0) { - log_debug("scsi_id: cannot stat %s: %m", devname); + log_debug_errno(errno, "scsi_id: cannot stat %s: %m", devname); err = 2; goto out; } @@ -861,7 +861,7 @@ int scsi_get_serial(struct udev *udev, int retval; memzero(dev_scsi->serial, len); - srand((unsigned int)getpid()); + initialize_srand(); for (cnt = 20; cnt > 0; cnt--) { struct timespec duration; diff --git a/src/udev/udev-builtin-keyboard.c b/src/udev/udev-builtin-keyboard.c index d6b7dbbac0..273b27ddcb 100644 --- a/src/udev/udev-builtin-keyboard.c +++ b/src/udev/udev-builtin-keyboard.c @@ -62,7 +62,7 @@ static int install_force_release(struct udev_device *dev, const unsigned int *re log_debug("keyboard: updating force-release list with '%s'", codes); ret = udev_device_set_sysattr_value(atkbd, "force_release", codes); if (ret < 0) - log_error("Error writing force-release attribute: %s", strerror(-ret)); + log_error_errno(ret, "Error writing force-release attribute: %m"); return ret; } @@ -140,7 +140,7 @@ static int builtin_keyboard(struct udev_device *dev, int argc, char *argv[], boo fd = open(udev_device_get_devnode(dev), O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); if (fd < 0) { - log_error("Error, opening device '%s': %m", node); + log_error_errno(errno, "Error, opening device '%s': %m", node); return EXIT_FAILURE; } @@ -149,7 +149,7 @@ static int builtin_keyboard(struct udev_device *dev, int argc, char *argv[], boo log_debug("keyboard: mapping scan code %d (0x%x) to key code %d (0x%x)", map[i].scan, map[i].scan, map[i].key, map[i].key); if (ioctl(fd, EVIOCSKEYCODE, &map[i]) < 0) - log_error("Error calling EVIOCSKEYCODE on device node '%s' (scan code 0x%x, key code %d): %m", node, map[i].scan, map[i].key); + log_error_errno(errno, "Error calling EVIOCSKEYCODE on device node '%s' (scan code 0x%x, key code %d): %m", node, map[i].scan, map[i].key); } /* install list of force-release codes */ diff --git a/src/udev/udev-builtin-kmod.c b/src/udev/udev-builtin-kmod.c index 454ee2edff..0949d9fad1 100644 --- a/src/udev/udev-builtin-kmod.c +++ b/src/udev/udev-builtin-kmod.c @@ -31,7 +31,7 @@ #include "udev.h" -static struct kmod_ctx *ctx; +static struct kmod_ctx *ctx = NULL; static int load_module(struct udev *udev, const char *alias) { struct kmod_list *list = NULL; @@ -43,18 +43,18 @@ static int load_module(struct udev *udev, const char *alias) { return err; if (list == NULL) - log_debug("no module matches '%s'", alias); + log_debug("No module matches '%s'", alias); kmod_list_foreach(l, list) { struct kmod_module *mod = kmod_module_get_module(l); err = kmod_module_probe_insert_module(mod, KMOD_PROBE_APPLY_BLACKLIST, NULL, NULL, NULL, NULL); if (err == KMOD_PROBE_APPLY_BLACKLIST) - log_debug("module '%s' is blacklisted", kmod_module_get_name(mod)); + log_debug("Module '%s' is blacklisted", kmod_module_get_name(mod)); else if (err == 0) - log_debug("inserted '%s'", kmod_module_get_name(mod)); + log_debug("Inserted '%s'", kmod_module_get_name(mod)); else - log_debug("failed to insert '%s'", kmod_module_get_name(mod)); + log_debug("Failed to insert '%s'", kmod_module_get_name(mod)); kmod_module_unref(mod); } @@ -63,10 +63,8 @@ static int load_module(struct udev *udev, const char *alias) { return err; } -_printf_(6,0) -static void udev_kmod_log(void *data, int priority, const char *file, int line, - const char *fn, const char *format, va_list args) { - udev_main_log(data, priority, file, line, fn, format, args); +_printf_(6,0) static void udev_kmod_log(void *data, int priority, const char *file, int line, const char *fn, const char *format, va_list args) { + log_internalv(priority, 0, file, line, fn, format, args); } static int builtin_kmod(struct udev_device *dev, int argc, char *argv[], bool test) { @@ -82,7 +80,7 @@ static int builtin_kmod(struct udev_device *dev, int argc, char *argv[], bool te } for (i = 2; argv[i]; i++) { - log_debug("execute '%s' '%s'", argv[1], argv[i]); + log_debug("Execute '%s' '%s'", argv[1], argv[i]); load_module(udev, argv[i]); } @@ -98,7 +96,7 @@ static int builtin_kmod_init(struct udev *udev) { if (!ctx) return -ENOMEM; - log_debug("load module index"); + log_debug("Load module index"); kmod_set_log_fn(ctx, udev_kmod_log, udev); kmod_load_resources(ctx); return 0; @@ -106,13 +104,13 @@ static int builtin_kmod_init(struct udev *udev) { /* called on udev shutdown and reload request */ static void builtin_kmod_exit(struct udev *udev) { - log_debug("unload module index"); + log_debug("Unload module index"); ctx = kmod_unref(ctx); } /* called every couple of seconds during event activity; 'true' if config has changed */ static bool builtin_kmod_validate(struct udev *udev) { - log_debug("validate module index"); + log_debug("Validate module index"); if (!ctx) return false; return (kmod_validate_resources(ctx) != KMOD_RESOURCES_OK); diff --git a/src/udev/udev-builtin-net_setup_link.c b/src/udev/udev-builtin-net_setup_link.c index 14351de6a6..87d10bf78f 100644 --- a/src/udev/udev-builtin-net_setup_link.c +++ b/src/udev/udev-builtin-net_setup_link.c @@ -46,14 +46,14 @@ static int builtin_net_setup_link(struct udev_device *dev, int argc, char **argv log_debug("No matching link configuration found."); return EXIT_SUCCESS; } else { - log_error("Could not get link config: %s", strerror(-r)); + log_error_errno(r, "Could not get link config: %m"); return EXIT_FAILURE; } } r = link_config_apply(ctx, link, dev, &name); if (r < 0) { - log_error("Could not apply link config to %s: %s", udev_device_get_sysname(dev), strerror(-r)); + log_error_errno(r, "Could not apply link config to %s: %m", udev_device_get_sysname(dev)); return EXIT_FAILURE; } diff --git a/src/udev/udev-builtin-path_id.c b/src/udev/udev-builtin-path_id.c index 0d247f6b5a..d540ba8392 100644 --- a/src/udev/udev-builtin-path_id.c +++ b/src/udev/udev-builtin-path_id.c @@ -118,7 +118,7 @@ out: return parent; } -static struct udev_device *handle_scsi_sas(struct udev_device *parent, char **path) { +static struct udev_device *handle_scsi_sas_wide_port(struct udev_device *parent, char **path) { struct udev *udev = udev_device_get_udev(parent); struct udev_device *targetdev; struct udev_device *target_parent; @@ -154,6 +154,100 @@ out: return parent; } +static struct udev_device *handle_scsi_sas(struct udev_device *parent, char **path) +{ + struct udev *udev = udev_device_get_udev(parent); + struct udev_device *targetdev; + struct udev_device *target_parent; + struct udev_device *port; + struct udev_device *expander; + struct udev_device *target_sasdev = NULL; + struct udev_device *expander_sasdev = NULL; + struct udev_device *port_sasdev = NULL; + const char *sas_address = NULL; + const char *phy_id; + const char *phy_count; + char *lun = NULL; + + targetdev = udev_device_get_parent_with_subsystem_devtype(parent, "scsi", "scsi_target"); + if (targetdev == NULL) + return NULL; + + target_parent = udev_device_get_parent(targetdev); + if (target_parent == NULL) + return NULL; + + /* Get sas device */ + target_sasdev = udev_device_new_from_subsystem_sysname(udev, + "sas_device", udev_device_get_sysname(target_parent)); + if (target_sasdev == NULL) + return NULL; + + /* The next parent is sas port */ + port = udev_device_get_parent(target_parent); + if (port == NULL) { + parent = NULL; + goto out; + } + + /* Get port device */ + port_sasdev = udev_device_new_from_subsystem_sysname(udev, + "sas_port", udev_device_get_sysname(port)); + + phy_count = udev_device_get_sysattr_value(port_sasdev, "num_phys"); + if (phy_count == NULL) { + parent = NULL; + goto out; + } + + /* Check if we are simple disk */ + if (strncmp(phy_count, "1", 2) != 0) { + parent = handle_scsi_sas_wide_port(parent, path); + goto out; + } + + /* Get connected phy */ + phy_id = udev_device_get_sysattr_value(target_sasdev, "phy_identifier"); + if (phy_id == NULL) { + parent = NULL; + goto out; + } + + /* The port's parent is either hba or expander */ + expander = udev_device_get_parent(port); + if (expander == NULL) { + parent = NULL; + goto out; + } + + /* Get expander device */ + expander_sasdev = udev_device_new_from_subsystem_sysname(udev, + "sas_device", udev_device_get_sysname(expander)); + if (expander_sasdev != NULL) { + /* Get expander's address */ + sas_address = udev_device_get_sysattr_value(expander_sasdev, + "sas_address"); + if (sas_address == NULL) { + parent = NULL; + goto out; + } + } + + format_lun_number(parent, &lun); + if (sas_address) + path_prepend(path, "sas-exp%s-phy%s-%s", sas_address, phy_id, lun); + else + path_prepend(path, "sas-phy%s-%s", phy_id, lun); + + if (lun) + free(lun); +out: + udev_device_unref(target_sasdev); + udev_device_unref(expander_sasdev); + udev_device_unref(port_sasdev); + return parent; +} + static struct udev_device *handle_scsi_iscsi(struct udev_device *parent, char **path) { struct udev *udev = udev_device_get_udev(parent); struct udev_device *transportdev; @@ -548,9 +642,9 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool } /* - * Do return devices with have an unknown type of parent device, they - * might produce conflicting IDs below multiple independent parent - * devices. + * Do not return devices with an unknown parent device type. They + * might produce conflicting IDs if the parent does not provide a + * unique and predictable name. */ if (!supported_parent) { free(path); @@ -558,9 +652,9 @@ static int builtin_path_id(struct udev_device *dev, int argc, char *argv[], bool } /* - * Do not return a have-only a single-parent block devices, some - * have entire hidden buses behind it, and not create predictable - * IDs that way. + * Do not return block devices without a well-known transport. Some + * devices do not expose their buses and do not provide a unique + * and predictable name that way. */ if (streq(udev_device_get_subsystem(dev), "block") && !supported_transport) { free(path); diff --git a/src/udev/udev-builtin-uaccess.c b/src/udev/udev-builtin-uaccess.c index 6964fb5529..591915435a 100644 --- a/src/udev/udev-builtin-uaccess.c +++ b/src/udev/udev-builtin-uaccess.c @@ -63,7 +63,7 @@ static int builtin_uaccess(struct udev_device *dev, int argc, char *argv[], bool r = devnode_acl(path, true, false, 0, true, uid); if (r < 0) { - log_error("Failed to apply ACL on %s: %s", path, strerror(-r)); + log_error_errno(r, "Failed to apply ACL on %s: %m", path); goto finish; } @@ -77,7 +77,7 @@ finish: /* Better be safe than sorry and reset ACL */ k = devnode_acl(path, true, false, 0, false, 0); if (k < 0) { - log_error("Failed to apply ACL on %s: %s", path, strerror(-k)); + log_error_errno(k, "Failed to apply ACL on %s: %m", path); if (r >= 0) r = k; } diff --git a/src/udev/udev-ctrl.c b/src/udev/udev-ctrl.c index 98fd3a9acf..9cece4a85d 100644 --- a/src/udev/udev-ctrl.c +++ b/src/udev/udev-ctrl.c @@ -84,7 +84,7 @@ struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd) { if (fd < 0) { uctrl->sock = socket(AF_LOCAL, SOCK_SEQPACKET|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); if (uctrl->sock < 0) { - log_error("error getting socket: %m"); + log_error_errno(errno, "error getting socket: %m"); udev_ctrl_unref(uctrl); return NULL; } @@ -94,7 +94,7 @@ struct udev_ctrl *udev_ctrl_new_from_fd(struct udev *udev, int fd) { } r = setsockopt(uctrl->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); if (r < 0) - log_warning("could not set SO_PASSCRED: %m"); + log_warning_errno(errno, "could not set SO_PASSCRED: %m"); uctrl->saddr.sun_family = AF_LOCAL; strscpy(uctrl->saddr.sun_path, sizeof(uctrl->saddr.sun_path), "/run/udev/control"); @@ -118,14 +118,14 @@ int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl) { if (err < 0) { err = -errno; - log_error("bind failed: %m"); + log_error_errno(errno, "bind failed: %m"); return err; } err = listen(uctrl->sock, 0); if (err < 0) { err = -errno; - log_error("listen failed: %m"); + log_error_errno(errno, "listen failed: %m"); return err; } @@ -187,14 +187,14 @@ struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl) { conn->sock = accept4(uctrl->sock, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK); if (conn->sock < 0) { if (errno != EINTR) - log_error("unable to receive ctrl connection: %m"); + log_error_errno(errno, "unable to receive ctrl connection: %m"); goto err; } /* check peer credential of connection */ r = getpeercred(conn->sock, &ucred); if (r < 0) { - log_error("unable to receive credentials of ctrl connection: %s", strerror(-r)); + log_error_errno(r, "unable to receive credentials of ctrl connection: %m"); goto err; } if (ucred.uid > 0) { @@ -205,7 +205,7 @@ struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl) { /* enable receiving of the sender credentials in the messages */ r = setsockopt(conn->sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); if (r < 0) - log_warning("could not set SO_PASSCRED: %m"); + log_warning_errno(errno, "could not set SO_PASSCRED: %m"); udev_ctrl_ref(uctrl); return conn; @@ -361,7 +361,7 @@ struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) { goto err; } else { if (!(pfd[0].revents & POLLIN)) { - log_error("ctrl connection error: %m"); + log_error_errno(errno, "ctrl connection error: %m"); goto err; } } @@ -374,7 +374,7 @@ struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) { size = recvmsg(conn->sock, &smsg, 0); if (size < 0) { - log_error("unable to receive ctrl message: %m"); + log_error_errno(errno, "unable to receive ctrl message: %m"); goto err; } cmsg = CMSG_FIRSTHDR(&smsg); diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c index 30a6708901..6b184303d8 100644 --- a/src/udev/udev-event.c +++ b/src/udev/udev-event.c @@ -387,7 +387,7 @@ static int spawn_exec(struct udev_event *event, if (fd_stderr < 0) dup2(fd, STDERR_FILENO); } else - log_error("open /dev/null failed: %m"); + log_error_errno(errno, "open /dev/null failed: %m"); /* connect pipes to std{out,err} */ if (fd_stdout >= 0) { @@ -409,7 +409,7 @@ static int spawn_exec(struct udev_event *event, execve(argv[0], argv, envp); /* exec failed */ - log_error("failed to execute '%s' '%s': %m", argv[0], cmd); + log_error_errno(errno, "failed to execute '%s' '%s': %m", argv[0], cmd); return -errno; } @@ -437,14 +437,14 @@ static void spawn_read(struct udev_event *event, fd_ep = epoll_create1(EPOLL_CLOEXEC); if (fd_ep < 0) { - log_error("error creating epoll fd: %m"); + log_error_errno(errno, "error creating epoll fd: %m"); return; } if (fd_stdout >= 0) { r = epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stdout, &ep_outpipe); if (r < 0) { - log_error("fail to add stdout fd to epoll: %m"); + log_error_errno(errno, "fail to add stdout fd to epoll: %m"); return; } } @@ -452,7 +452,7 @@ static void spawn_read(struct udev_event *event, if (fd_stderr >= 0) { r = epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_stderr, &ep_errpipe); if (r < 0) { - log_error("fail to add stderr fd to epoll: %m"); + log_error_errno(errno, "fail to add stderr fd to epoll: %m"); return; } } @@ -481,7 +481,7 @@ static void spawn_read(struct udev_event *event, if (fdcount < 0) { if (errno == EINTR) continue; - log_error("failed to poll: %m"); + log_error_errno(errno, "failed to poll: %m"); return; } else if (fdcount == 0) { log_error("timeout '%s'", cmd); @@ -527,7 +527,7 @@ static void spawn_read(struct udev_event *event, } else if (ev[i].events & EPOLLHUP) { r = epoll_ctl(fd_ep, EPOLL_CTL_DEL, *fd, NULL); if (r < 0) { - log_error("failed to remove fd from epoll: %m"); + log_error_errno(errno, "failed to remove fd from epoll: %m"); return; } *fd = -1; @@ -576,7 +576,7 @@ static int spawn_wait(struct udev_event *event, if (errno == EINTR) continue; err = -errno; - log_error("failed to poll: %m"); + log_error_errno(errno, "failed to poll: %m"); goto out; } if (fdcount == 0) { @@ -587,7 +587,7 @@ static int spawn_wait(struct udev_event *event, if (errno == EINTR) continue; err = -errno; - log_error("failed to poll: %m"); + log_error_errno(errno, "failed to poll: %m"); goto out; } if (fdcount == 0) { @@ -676,7 +676,6 @@ int udev_event_spawn(struct udev_event *event, usec_t timeout_warn_usec, const char *cmd, char **envp, const sigset_t *sigmask, char *result, size_t ressize) { - struct udev *udev = event->udev; int outpipe[2] = {-1, -1}; int errpipe[2] = {-1, -1}; pid_t pid; @@ -689,17 +688,17 @@ int udev_event_spawn(struct udev_event *event, udev_build_argv(event->udev, arg, NULL, argv); /* pipes from child to parent */ - if (result != NULL || udev_get_log_priority(udev) >= LOG_INFO) { + if (result != NULL || log_get_max_level() >= LOG_INFO) { if (pipe2(outpipe, O_NONBLOCK) != 0) { err = -errno; - log_error("pipe failed: %m"); + log_error_errno(errno, "pipe failed: %m"); goto out; } } - if (udev_get_log_priority(udev) >= LOG_INFO) { + if (log_get_max_level() >= LOG_INFO) { if (pipe2(errpipe, O_NONBLOCK) != 0) { err = -errno; - log_error("pipe failed: %m"); + log_error_errno(errno, "pipe failed: %m"); goto out; } } @@ -730,7 +729,7 @@ int udev_event_spawn(struct udev_event *event, _exit(2 ); case -1: - log_error("fork of '%s' failed: %m", cmd); + log_error_errno(errno, "fork of '%s' failed: %m", cmd); err = -1; goto out; default: @@ -776,21 +775,19 @@ static int rename_netif(struct udev_event *event) { strscpy(name, IFNAMSIZ, event->name); r = rtnl_set_link_name(&event->rtnl, udev_device_get_ifindex(dev), name); - if (r < 0) { - log_error("error changing net interface name '%s' to '%s': %s", - oldname, name, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Error changing net interface name '%s' to '%s': %m", oldname, name); - log_debug("renamed network interface '%s' to '%s'\n", oldname, name); + log_debug("renamed network interface '%s' to '%s'", oldname, name); return 0; } void udev_event_execute_rules(struct udev_event *event, - usec_t timeout_usec, - usec_t timeout_warn_usec, - struct udev_rules *rules, const sigset_t *sigmask) { + usec_t timeout_usec, usec_t timeout_warn_usec, + struct udev_list *properties_list, + struct udev_rules *rules, + const sigset_t *sigmask) { struct udev_device *dev = event->dev; if (udev_device_get_subsystem(dev) == NULL) @@ -804,7 +801,10 @@ void udev_event_execute_rules(struct udev_event *event, if (major(udev_device_get_devnum(dev)) != 0) udev_watch_end(event->udev, dev); - udev_rules_apply_to_event(rules, event, timeout_usec, timeout_warn_usec, sigmask); + udev_rules_apply_to_event(rules, event, + timeout_usec, timeout_warn_usec, + properties_list, + sigmask); if (major(udev_device_get_devnum(dev)) != 0) udev_node_remove(dev); @@ -838,7 +838,10 @@ void udev_event_execute_rules(struct udev_event *event, } } - udev_rules_apply_to_event(rules, event, timeout_usec, timeout_warn_usec, sigmask); + udev_rules_apply_to_event(rules, event, + timeout_usec, timeout_warn_usec, + properties_list, + sigmask); /* rename a new network interface, if needed */ if (udev_device_get_ifindex(dev) > 0 && streq(udev_device_get_action(dev), "add") && diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c index 030e459198..c30a428ea1 100644 --- a/src/udev/udev-node.c +++ b/src/udev/udev-node.c @@ -112,12 +112,12 @@ static int node_symlink(struct udev_device *dev, const char *node, const char *s mac_selinux_create_file_clear(); } while (err == -ENOENT); if (err != 0) { - log_error("symlink '%s' '%s' failed: %m", target, slink_tmp); + log_error_errno(errno, "symlink '%s' '%s' failed: %m", target, slink_tmp); goto exit; } err = rename(slink_tmp, slink); if (err != 0) { - log_error("rename '%s' '%s' failed: %m", slink_tmp, slink); + log_error_errno(errno, "rename '%s' '%s' failed: %m", slink_tmp, slink); unlink(slink_tmp); } exit: @@ -264,7 +264,7 @@ static int node_permissions_apply(struct udev_device *dev, bool apply, if (lstat(devnode, &stats) != 0) { err = -errno; - log_debug("can not stat() node '%s' (%m)", devnode); + log_debug_errno(errno, "can not stat() node '%s' (%m)", devnode); goto out; } @@ -283,10 +283,10 @@ static int node_permissions_apply(struct udev_device *dev, bool apply, log_debug("set permissions %s, %#o, uid=%u, gid=%u", devnode, mode, uid, gid); err = chmod(devnode, mode); if (err < 0) - log_warning("setting mode of %s to %#o failed: %m", devnode, mode); + log_warning_errno(errno, "setting mode of %s to %#o failed: %m", devnode, mode); err = chown(devnode, uid, gid); if (err < 0) - log_warning("setting owner of %s to uid=%u, gid=%u failed: %m", devnode, uid, gid); + log_warning_errno(errno, "setting owner of %s to uid=%u, gid=%u failed: %m", devnode, uid, gid); } else { log_debug("preserve permissions %s, %#o, uid=%u, gid=%u", devnode, mode, uid, gid); } @@ -304,7 +304,7 @@ static int node_permissions_apply(struct udev_device *dev, bool apply, r = mac_selinux_apply(devnode, label); if (r < 0) - log_error("SECLABEL: failed to set SELinux label '%s': %s", label, strerror(-r)); + log_error_errno(r, "SECLABEL: failed to set SELinux label '%s': %m", label); else log_debug("SECLABEL: set SELinux label '%s'", label); @@ -313,7 +313,7 @@ static int node_permissions_apply(struct udev_device *dev, bool apply, r = mac_smack_apply(devnode, label); if (r < 0) - log_error("SECLABEL: failed to set SMACK label '%s': %s", label, strerror(-r)); + log_error_errno(r, "SECLABEL: failed to set SMACK label '%s': %m", label); else log_debug("SECLABEL: set SMACK label '%s'", label); diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index 3bc25f7c26..c9a0197534 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -474,9 +474,9 @@ static uid_t add_uid(struct udev_rules *rules, const char *owner) { r = get_user_creds(&owner, &uid, NULL, NULL, NULL); if (r < 0) { if (r == -ENOENT || r == -ESRCH) - udev_err(rules->udev, "specified user '%s' unknown\n", owner); + log_error("specified user '%s' unknown", owner); else - udev_err(rules->udev, "error resolving user '%s': %s\n", owner, strerror(-r)); + log_error_errno(r, "error resolving user '%s': %m", owner); } /* grow buffer if needed */ @@ -521,9 +521,9 @@ static gid_t add_gid(struct udev_rules *rules, const char *group) { r = get_group_creds(&group, &gid); if (r < 0) { if (r == -ENOENT || r == -ESRCH) - udev_err(rules->udev, "specified group '%s' unknown\n", group); + log_error("specified group '%s' unknown", group); else - udev_err(rules->udev, "error resolving group '%s': %s\n", group, strerror(-r)); + log_error_errno(r, "error resolving group '%s': %m", group); } /* grow buffer if needed */ @@ -1042,11 +1042,11 @@ static int add_rule(struct udev_rules *rules, char *line, const char *filename, unsigned int filename_off, unsigned int lineno) { char *linepos; const char *attr; - struct rule_tmp rule_tmp; + struct rule_tmp rule_tmp = { + .rules = rules, + .rule.type = TK_RULE, + }; - memzero(&rule_tmp, sizeof(struct rule_tmp)); - rule_tmp.rules = rules; - rule_tmp.rule.type = TK_RULE; /* the offset in the rule is limited to unsigned short */ if (filename_off < USHRT_MAX) rule_tmp.rule.rule.filename_off = filename_off; @@ -1067,14 +1067,14 @@ static int add_rule(struct udev_rules *rules, char *line, /* If we aren't at the end of the line, this is a parsing error. * Make a best effort to describe where the problem is. */ - if (*linepos != '\n') { - char buf[2] = {linepos[1]}; + if (!strchr(NEWLINE, *linepos)) { + char buf[2] = {*linepos}; _cleanup_free_ char *tmp; tmp = cescape(buf); - log_error("invalid key/value pair in file %s on line %u, starting at character %tu ('%s')\n", + log_error("invalid key/value pair in file %s on line %u, starting at character %tu ('%s')", filename, lineno, linepos - line + 1, tmp); - if (linepos[1] == '#') + if (*linepos == '#') log_error("hint: comments can only start at beginning of line"); } break; @@ -1685,7 +1685,7 @@ struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names) { r = conf_files_list_strv(&files, ".rules", NULL, rules_dirs); if (r < 0) { - log_error("failed to enumerate rules files: %s", strerror(-r)); + log_error_errno(r, "failed to enumerate rules files: %m"); return udev_rules_unref(rules); } @@ -1876,6 +1876,7 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event, usec_t timeout_usec, usec_t timeout_warn_usec, + struct udev_list *properties_list, const sigset_t *sigmask) { struct token *cur; struct token *rule; @@ -1941,7 +1942,18 @@ int udev_rules_apply_to_event(struct udev_rules *rules, const char *value; value = udev_device_get_property_value(event->dev, key_name); - if (value == NULL) + + /* check global properties */ + if (!value && properties_list) { + struct udev_list_entry *list_entry; + + list_entry = udev_list_get_entry(properties_list); + list_entry = udev_list_entry_get_by_name(list_entry, key_name); + if (list_entry != NULL) + value = udev_list_entry_get_value(list_entry); + } + + if (!value) value = ""; if (match_key(rules, cur, value)) goto nomatch; @@ -2265,9 +2277,9 @@ int udev_rules_apply_to_event(struct udev_rules *rules, r = get_user_creds(&ow, &event->uid, NULL, NULL, NULL); if (r < 0) { if (r == -ENOENT || r == -ESRCH) - udev_err(event->udev, "specified user '%s' unknown\n", owner); + log_error("specified user '%s' unknown", owner); else - udev_err(event->udev, "error resolving user '%s': %s\n", owner, strerror(-r)); + log_error_errno(r, "error resolving user '%s': %m", owner); event->uid = 0; } @@ -2291,9 +2303,9 @@ int udev_rules_apply_to_event(struct udev_rules *rules, r = get_group_creds(&gr, &event->gid); if (r < 0) { if (r == -ENOENT || r == -ESRCH) - udev_err(event->udev, "specified group '%s' unknown\n", group); + log_error("specified group '%s' unknown", group); else - udev_err(event->udev, "error resolving group '%s': %s\n", group, strerror(-r)); + log_error_errno(r, "error resolving group '%s': %m", group); event->gid = 0; } @@ -2523,10 +2535,10 @@ int udev_rules_apply_to_event(struct udev_rules *rules, f = fopen(attr, "we"); if (f != NULL) { if (fprintf(f, "%s", value) <= 0) - log_error("error writing ATTR{%s}: %m", attr); + log_error_errno(errno, "error writing ATTR{%s}: %m", attr); fclose(f); } else { - log_error("error opening ATTR{%s} for writing: %m", attr); + log_error_errno(errno, "error opening ATTR{%s} for writing: %m", attr); } break; } @@ -2640,19 +2652,17 @@ int udev_rules_apply_static_dev_perms(struct udev_rules *rules) { strscpyl(tags_dir, sizeof(tags_dir), "/run/udev/static_node-tags/", *t, "/", NULL); r = mkdir_p(tags_dir, 0755); - if (r < 0) { - log_error("failed to create %s: %s", tags_dir, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "failed to create %s: %m", tags_dir); unescaped_filename = xescape(rules_str(rules, cur->key.value_off), "/."); strscpyl(tag_symlink, sizeof(tag_symlink), tags_dir, unescaped_filename, NULL); r = symlink(device_node, tag_symlink); - if (r < 0 && errno != EEXIST) { - log_error("failed to create symlink %s -> %s: %m", tag_symlink, device_node); - return -errno; - } else + if (r < 0 && errno != EEXIST) + return log_error_errno(errno, "failed to create symlink %s -> %s: %m", + tag_symlink, device_node); + else r = 0; } } diff --git a/src/udev/udev-watch.c b/src/udev/udev-watch.c index 061bd05875..6ba8674d77 100644 --- a/src/udev/udev-watch.c +++ b/src/udev/udev-watch.c @@ -39,7 +39,7 @@ static int inotify_fd = -1; int udev_watch_init(struct udev *udev) { inotify_fd = inotify_init1(IN_CLOEXEC); if (inotify_fd < 0) - log_error("inotify_init failed: %m"); + log_error_errno(errno, "inotify_init failed: %m"); return inotify_fd; } @@ -56,7 +56,7 @@ void udev_watch_restore(struct udev *udev) { dir = opendir("/run/udev/watch.old"); if (dir == NULL) { - log_error("unable to open old watches dir /run/udev/watch.old; old watches will not be restored: %m"); + log_error_errno(errno, "unable to open old watches dir /run/udev/watch.old; old watches will not be restored: %m"); return; } @@ -88,7 +88,7 @@ unlink: rmdir("/run/udev/watch.old"); } else if (errno != ENOENT) { - log_error("unable to move watches dir /run/udev/watch; old watches will not be restored: %m"); + log_error_errno(errno, "unable to move watches dir /run/udev/watch; old watches will not be restored: %m"); } } @@ -103,7 +103,7 @@ void udev_watch_begin(struct udev *udev, struct udev_device *dev) { log_debug("adding watch on '%s'", udev_device_get_devnode(dev)); wd = inotify_add_watch(inotify_fd, udev_device_get_devnode(dev), IN_CLOSE_WRITE); if (wd < 0) { - log_error("inotify_add_watch(%d, %s, %o) failed: %m", + log_error_errno(errno, "inotify_add_watch(%d, %s, %o) failed: %m", inotify_fd, udev_device_get_devnode(dev), IN_CLOSE_WRITE); return; } @@ -113,7 +113,7 @@ void udev_watch_begin(struct udev *udev, struct udev_device *dev) { unlink(filename); r = symlink(udev_device_get_id_filename(dev), filename); if (r < 0) - log_error("Failed to create symlink %s: %m", filename); + log_error_errno(errno, "Failed to create symlink %s: %m", filename); udev_device_set_watch_handle(dev, wd); } diff --git a/src/udev/udev.h b/src/udev/udev.h index 765ba9e86d..dece6eccab 100644 --- a/src/udev/udev.h +++ b/src/udev/udev.h @@ -73,7 +73,9 @@ struct udev_rules; struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names); struct udev_rules *udev_rules_unref(struct udev_rules *rules); bool udev_rules_check_timestamp(struct udev_rules *rules); -int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event, usec_t timeout_usec, usec_t timeout_warn_usec, +int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event, + usec_t timeout_usec, usec_t timeout_warn_usec, + struct udev_list *properties_list, const sigset_t *sigmask); int udev_rules_apply_static_dev_perms(struct udev_rules *rules); @@ -88,8 +90,11 @@ int udev_event_spawn(struct udev_event *event, usec_t timeout_warn_usec, const char *cmd, char **envp, const sigset_t *sigmask, char *result, size_t ressize); -void udev_event_execute_rules(struct udev_event *event, usec_t timeout_usec, usec_t timeout_warn_usec, - struct udev_rules *rules, const sigset_t *sigset); +void udev_event_execute_rules(struct udev_event *event, + usec_t timeout_usec, usec_t timeout_warn_usec, + struct udev_list *properties_list, + struct udev_rules *rules, + const sigset_t *sigset); void udev_event_execute_run(struct udev_event *event, usec_t timeout_usec, usec_t timeout_warn_usec, const sigset_t *sigset); int udev_build_argv(struct udev *udev, char *cmd, int *argc, char *argv[]); @@ -197,11 +202,6 @@ int udev_builtin_add_property(struct udev_device *dev, bool test, const char *ke int udev_builtin_hwdb_lookup(struct udev_device *dev, const char *prefix, const char *modalias, const char *filter, bool test); -/* udev logging */ -void udev_main_log(struct udev *udev, int priority, - const char *file, int line, const char *fn, - const char *format, va_list args) _printf_(6, 0); - /* udevadm commands */ struct udevadm_cmd { const char *name; diff --git a/src/udev/udevadm-hwdb.c b/src/udev/udevadm-hwdb.c index 3ca755e2ed..a5870d1cee 100644 --- a/src/udev/udevadm-hwdb.c +++ b/src/udev/udevadm-hwdb.c @@ -428,6 +428,10 @@ static int insert_data(struct trie *trie, struct udev_list *match_list, value[0] = '\0'; value++; + /* libudev requires properties to start with a space */ + while (isblank(line[0]) && isblank(line[1])) + line++; + if (line[0] == '\0' || value[0] == '\0') { log_error("Error, empty key or value '%s' in '%s':", line, filename); return -EINVAL; @@ -618,7 +622,7 @@ static int adm_hwdb(struct udev *udev, int argc, char *argv[]) { err = conf_files_list_strv(&files, ".hwdb", root, conf_file_dirs); if (err < 0) { - log_error("failed to enumerate hwdb files: %s", strerror(-err)); + log_error_errno(err, "failed to enumerate hwdb files: %m"); rc = EXIT_FAILURE; goto out; } @@ -652,7 +656,7 @@ static int adm_hwdb(struct udev *udev, int argc, char *argv[]) { mkdir_parents(hwdb_bin, 0755); err = trie_store(trie, hwdb_bin); if (err < 0) { - log_error("Failure writing database %s: %s", hwdb_bin, strerror(-err)); + log_error_errno(err, "Failure writing database %s: %m", hwdb_bin); rc = EXIT_FAILURE; } } diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c index 22d0826011..a56f159543 100644 --- a/src/udev/udevadm-info.c +++ b/src/udev/udevadm-info.c @@ -31,6 +31,7 @@ #include "udev.h" #include "udev-util.h" +#include "udevadm-util.h" static bool skip_attribute(const char *name) { static const char* const skip[] = { @@ -257,35 +258,6 @@ static void cleanup_db(struct udev *udev) { } } -static struct udev_device *find_device(struct udev *udev, const char *id, const char *prefix) { - char name[UTIL_PATH_SIZE]; - - if (prefix && !startswith(id, prefix)) { - strscpyl(name, sizeof(name), prefix, id, NULL); - id = name; - } - - if (startswith(id, "/dev/")) { - struct stat statbuf; - char type; - - if (stat(id, &statbuf) < 0) - return NULL; - - if (S_ISBLK(statbuf.st_mode)) - type = 'b'; - else if (S_ISCHR(statbuf.st_mode)) - type = 'c'; - else - return NULL; - - return udev_device_new_from_devnum(udev, type, statbuf.st_rdev); - } else if (startswith(id, "/sys/")) - return udev_device_new_from_syspath(udev, id); - else - return NULL; -} - static int uinfo(struct udev *udev, int argc, char *argv[]) { _cleanup_udev_device_unref_ struct udev_device *device = NULL; bool root = 0; diff --git a/src/udev/udevadm-monitor.c b/src/udev/udevadm-monitor.c index e776fb99dc..1a5f516ddc 100644 --- a/src/udev/udevadm-monitor.c +++ b/src/udev/udevadm-monitor.c @@ -155,7 +155,7 @@ static int adm_monitor(struct udev *udev, int argc, char *argv[]) { fd_ep = epoll_create1(EPOLL_CLOEXEC); if (fd_ep < 0) { - log_error("error creating epoll fd: %m"); + log_error_errno(errno, "error creating epoll fd: %m"); return 1; } @@ -195,7 +195,7 @@ static int adm_monitor(struct udev *udev, int argc, char *argv[]) { ep_udev.events = EPOLLIN; ep_udev.data.fd = fd_udev; if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_udev, &ep_udev) < 0) { - log_error("fail to add fd to epoll: %m"); + log_error_errno(errno, "fail to add fd to epoll: %m"); return 2; } @@ -229,7 +229,7 @@ static int adm_monitor(struct udev *udev, int argc, char *argv[]) { ep_kernel.events = EPOLLIN; ep_kernel.data.fd = fd_kernel; if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_kernel, &ep_kernel) < 0) { - log_error("fail to add fd to epoll: %m"); + log_error_errno(errno, "fail to add fd to epoll: %m"); return 5; } diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c index 4738b611c3..d9d61b42d5 100644 --- a/src/udev/udevadm-test.c +++ b/src/udev/udevadm-test.c @@ -136,7 +136,11 @@ static int adm_test(struct udev *udev, int argc, char *argv[]) { goto out; } - udev_event_execute_rules(event, 60 * USEC_PER_SEC, 20 * USEC_PER_SEC, rules, &sigmask_orig); + udev_event_execute_rules(event, + 60 * USEC_PER_SEC, 20 * USEC_PER_SEC, + NULL, + rules, + &sigmask_orig); udev_list_entry_foreach(entry, udev_device_get_properties_list_entry(dev)) printf("%s=%s\n", udev_list_entry_get_name(entry), udev_list_entry_get_value(entry)); diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c index a52d1b5ad5..4308466b84 100644 --- a/src/udev/udevadm-trigger.c +++ b/src/udev/udevadm-trigger.c @@ -32,6 +32,7 @@ #include "udev.h" #include "udev-util.h" +#include "udevadm-util.h" #include "util.h" static int verbose; @@ -53,7 +54,7 @@ static void exec_list(struct udev_enumerate *udev_enumerate, const char *action) if (fd < 0) continue; if (write(fd, action, strlen(action)) < 0) - log_debug("error writing '%s' to '%s': %m", action, filename); + log_debug_errno(errno, "error writing '%s' to '%s': %m", action, filename); close(fd); } } @@ -85,26 +86,32 @@ static void help(void) { " -A,--attr-nomatch=<file[=<value>]> exclude devices with a matching attribute\n" " -p,--property-match=<key>=<value> trigger devices with a matching property\n" " -g,--tag-match=<key>=<value> trigger devices with a matching property\n" - " -y,--sysname-match=<name> trigger devices with a matching name\n" + " -y,--sysname-match=<name> trigger devices with this /sys path\n" + " --name-match=<name> trigger devices with this /dev name\n" " -b,--parent-match=<name> trigger devices with that parent device\n" " -h,--help\n\n"); } static int adm_trigger(struct udev *udev, int argc, char *argv[]) { + enum { + ARG_NAME = 0x100, + }; + static const struct option options[] = { - { "verbose", no_argument, NULL, 'v' }, - { "dry-run", no_argument, NULL, 'n' }, - { "type", required_argument, NULL, 't' }, - { "action", required_argument, NULL, 'c' }, - { "subsystem-match", required_argument, NULL, 's' }, - { "subsystem-nomatch", required_argument, NULL, 'S' }, - { "attr-match", required_argument, NULL, 'a' }, - { "attr-nomatch", required_argument, NULL, 'A' }, - { "property-match", required_argument, NULL, 'p' }, - { "tag-match", required_argument, NULL, 'g' }, - { "sysname-match", required_argument, NULL, 'y' }, - { "parent-match", required_argument, NULL, 'b' }, - { "help", no_argument, NULL, 'h' }, + { "verbose", no_argument, NULL, 'v' }, + { "dry-run", no_argument, NULL, 'n' }, + { "type", required_argument, NULL, 't' }, + { "action", required_argument, NULL, 'c' }, + { "subsystem-match", required_argument, NULL, 's' }, + { "subsystem-nomatch", required_argument, NULL, 'S' }, + { "attr-match", required_argument, NULL, 'a' }, + { "attr-nomatch", required_argument, NULL, 'A' }, + { "property-match", required_argument, NULL, 'p' }, + { "tag-match", required_argument, NULL, 'g' }, + { "sysname-match", required_argument, NULL, 'y' }, + { "name-match", required_argument, NULL, ARG_NAME }, + { "parent-match", required_argument, NULL, 'b' }, + { "help", no_argument, NULL, 'h' }, {} }; enum { @@ -174,25 +181,31 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[]) { udev_enumerate_add_match_sysname(udev_enumerate, optarg); break; case 'b': { - char path[UTIL_PATH_SIZE]; - struct udev_device *dev; - - /* add sys dir if needed */ - if (!startswith(optarg, "/sys")) - strscpyl(path, sizeof(path), "/sys", optarg, NULL); - else - strscpy(path, sizeof(path), optarg); - util_remove_trailing_chars(path, '/'); - dev = udev_device_new_from_syspath(udev, path); + _cleanup_udev_device_unref_ struct udev_device *dev; + + dev = find_device(udev, optarg, "/sys"); + if (dev == NULL) { + log_error("unable to open the device '%s'", optarg); + return 2; + } + + udev_enumerate_add_match_parent(udev_enumerate, dev); + break; + } + + case ARG_NAME: { + _cleanup_udev_device_unref_ struct udev_device *dev; + + dev = find_device(udev, optarg, "/dev/"); if (dev == NULL) { log_error("unable to open the device '%s'", optarg); return 2; } + udev_enumerate_add_match_parent(udev_enumerate, dev); - /* drop reference immediately, enumerate pins the device as long as needed */ - udev_device_unref(dev); break; } + case 'h': help(); return 0; @@ -203,9 +216,16 @@ static int adm_trigger(struct udev *udev, int argc, char *argv[]) { } } - if (optind < argc) { - fprintf(stderr, "Extraneous argument: '%s'\n", argv[optind]); - return 1; + for (; optind < argc; optind++) { + _cleanup_udev_device_unref_ struct udev_device *dev; + + dev = find_device(udev, argv[optind], NULL); + if (dev == NULL) { + log_error("unable to open the device '%s'", argv[optind]); + return 2; + } + + udev_enumerate_add_match_parent(udev_enumerate, dev); } switch (device_type) { diff --git a/src/udev/udevadm-util.c b/src/udev/udevadm-util.c new file mode 100644 index 0000000000..37e80c31df --- /dev/null +++ b/src/udev/udevadm-util.c @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2008-2009 Kay Sievers <kay@vrfy.org> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "udevadm-util.h" + +struct udev_device *find_device(struct udev *udev, + const char *id, + const char *prefix) { + + assert(udev); + assert(id); + + if (prefix && !startswith(id, prefix)) + id = strappenda(prefix, id); + + if (startswith(id, "/dev/")) { + struct stat statbuf; + char type; + + if (stat(id, &statbuf) < 0) + return NULL; + + if (S_ISBLK(statbuf.st_mode)) + type = 'b'; + else if (S_ISCHR(statbuf.st_mode)) + type = 'c'; + else + return NULL; + + return udev_device_new_from_devnum(udev, type, statbuf.st_rdev); + } else if (startswith(id, "/sys/")) + return udev_device_new_from_syspath(udev, id); + else + return NULL; +} diff --git a/src/udev/udevadm-util.h b/src/udev/udevadm-util.h new file mode 100644 index 0000000000..dba651fddb --- /dev/null +++ b/src/udev/udevadm-util.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2014 Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "udev.h" + +struct udev_device *find_device(struct udev *udev, + const char *id, + const char *prefix); diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c index 7026c50d2b..d9bd69742c 100644 --- a/src/udev/udevadm.c +++ b/src/udev/udevadm.c @@ -26,12 +26,6 @@ #include "udev.h" -void udev_main_log(struct udev *udev, int priority, - const char *file, int line, const char *fn, - const char *format, va_list args) { - log_metav(priority, file, line, fn, format, args); -} - static int adm_version(struct udev *udev, int argc, char *argv[]) { printf("%s\n", VERSION); return 0; @@ -98,7 +92,6 @@ int main(int argc, char *argv[]) { log_parse_environment(); log_open(); - udev_set_log_fn(udev, udev_main_log); mac_selinux_init("/dev"); while ((c = getopt_long(argc, argv, "+dhV", options, NULL)) >= 0) @@ -106,7 +99,6 @@ int main(int argc, char *argv[]) { case 'd': log_set_max_level(LOG_DEBUG); - udev_set_log_priority(udev, LOG_DEBUG); break; case 'h': diff --git a/src/udev/udevd.c b/src/udev/udevd.c index 2e6c71352f..8bec03e77f 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -54,12 +54,6 @@ #include "dev-setup.h" #include "fileio.h" -void udev_main_log(struct udev *udev, int priority, - const char *file, int line, const char *fn, - const char *format, va_list args) { - log_metav(priority, file, line, fn, format, args); -} - static struct udev_rules *rules; static struct udev_ctrl *udev_ctrl; static struct udev_monitor *monitor; @@ -81,6 +75,7 @@ static sigset_t sigmask_orig; static UDEV_LIST(event_list); static UDEV_LIST(worker_list); static char *udev_cgroup; +static struct udev_list properties_list; static bool udev_exit; enum event_state { @@ -225,14 +220,14 @@ static void worker_new(struct event *event) { sigfillset(&mask); fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); if (fd_signal < 0) { - log_error("error creating signalfd %m"); + log_error_errno(errno, "error creating signalfd %m"); rc = 2; goto out; } fd_ep = epoll_create1(EPOLL_CLOEXEC); if (fd_ep < 0) { - log_error("error creating epoll fd: %m"); + log_error_errno(errno, "error creating epoll fd: %m"); rc = 3; goto out; } @@ -248,7 +243,7 @@ static void worker_new(struct event *event) { if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 || epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_monitor, &ep_monitor) < 0) { - log_error("fail to add fds to epoll: %m"); + log_error_errno(errno, "fail to add fds to epoll: %m"); rc = 4; goto out; } @@ -298,7 +293,7 @@ static void worker_new(struct event *event) { if (d) { fd_lock = open(udev_device_get_devnode(d), O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK); if (fd_lock >= 0 && flock(fd_lock, LOCK_SH|LOCK_NB) < 0) { - log_debug("Unable to flock(%s), skipping event handling: %m", udev_device_get_devnode(d)); + log_debug_errno(errno, "Unable to flock(%s), skipping event handling: %m", udev_device_get_devnode(d)); err = -EWOULDBLOCK; fd_lock = safe_close(fd_lock); goto skip; @@ -310,12 +305,19 @@ static void worker_new(struct event *event) { udev_event->rtnl = rtnl; /* apply rules, create node, symlinks */ - udev_event_execute_rules(udev_event, arg_event_timeout_usec, arg_event_timeout_warn_usec, rules, &sigmask_orig); + udev_event_execute_rules(udev_event, + arg_event_timeout_usec, arg_event_timeout_warn_usec, + &properties_list, + rules, + &sigmask_orig); - udev_event_execute_run(udev_event, arg_event_timeout_usec, arg_event_timeout_warn_usec, &sigmask_orig); + udev_event_execute_run(udev_event, + arg_event_timeout_usec, arg_event_timeout_warn_usec, + &sigmask_orig); - /* in case rtnl was initialized */ - rtnl = sd_rtnl_ref(udev_event->rtnl); + if (udev_event->rtnl) + /* in case rtnl was initialized */ + rtnl = sd_rtnl_ref(udev_event->rtnl); /* apply/restore inotify watch */ if (udev_event->inotify_watch) { @@ -357,7 +359,7 @@ skip: if (fdcount < 0) { if (errno == EINTR) continue; - log_error("failed to poll: %m"); + log_error_errno(errno, "failed to poll: %m"); goto out; } @@ -397,7 +399,7 @@ out: udev_monitor_unref(worker_monitor); event->state = EVENT_QUEUED; free(worker); - log_error("fork of child failed: %m"); + log_error_errno(errno, "fork of child failed: %m"); break; default: /* close monitor, but keep address around */ @@ -428,7 +430,7 @@ static void event_run(struct event *event) { count = udev_monitor_send_device(monitor, worker->monitor, event->dev); if (count < 0) { - log_error("worker [%u] did not accept message %zi (%m), kill it", worker->pid, count); + log_error_errno(errno, "worker [%u] did not accept message %zi (%m), kill it", worker->pid, count); kill(worker->pid, SIGKILL); worker->state = WORKER_KILLED; continue; @@ -644,7 +646,6 @@ static struct udev_ctrl_connection *handle_ctrl_msg(struct udev_ctrl *uctrl) { if (i >= 0) { log_debug("udevd message (SET_LOG_LEVEL) received, log_priority=%i", i); log_set_max_level(i); - udev_set_log_priority(udev, i); worker_kill(udev); } @@ -677,10 +678,10 @@ static struct udev_ctrl_connection *handle_ctrl_msg(struct udev_ctrl *uctrl) { val = &val[1]; if (val[0] == '\0') { log_debug("udevd message (ENV) received, unset '%s'", key); - udev_add_property(udev, key, NULL); + udev_list_entry_add(&properties_list, key, NULL); } else { log_debug("udevd message (ENV) received, set '%s=%s'", key, val); - udev_add_property(udev, key, val); + udev_list_entry_add(&properties_list, key, val); } } else { log_error("wrong key format '%s'", key); @@ -815,41 +816,34 @@ static int synthesize_change(struct udev_device *dev) { } static int handle_inotify(struct udev *udev) { - int nbytes, pos; - char *buf; - struct inotify_event *ev; - int r; + uint8_t buffer[INOTIFY_EVENT_MAX] _alignas_(struct inotify_event); + struct inotify_event *e; + ssize_t l; - r = ioctl(fd_inotify, FIONREAD, &nbytes); - if (r < 0 || nbytes <= 0) - return -errno; + l = read(fd_inotify, buffer, sizeof(buffer)); + if (l < 0) { + if (errno == EAGAIN || errno == EINTR) + return 0; - buf = malloc(nbytes); - if (!buf) { - log_error("error getting buffer for inotify"); - return -ENOMEM; + return log_error_errno(errno, "Failed to read inotify fd: %m"); } - nbytes = read(fd_inotify, buf, nbytes); - - for (pos = 0; pos < nbytes; pos += sizeof(struct inotify_event) + ev->len) { + FOREACH_INOTIFY_EVENT(e, buffer, l) { struct udev_device *dev; - ev = (struct inotify_event *)(buf + pos); - dev = udev_watch_lookup(udev, ev->wd); + dev = udev_watch_lookup(udev, e->wd); if (!dev) continue; - log_debug("inotify event: %x for %s", ev->mask, udev_device_get_devnode(dev)); - if (ev->mask & IN_CLOSE_WRITE) + log_debug("inotify event: %x for %s", e->mask, udev_device_get_devnode(dev)); + if (e->mask & IN_CLOSE_WRITE) synthesize_change(dev); - else if (ev->mask & IN_IGNORED) + else if (e->mask & IN_IGNORED) udev_watch_end(udev, dev); udev_device_unref(dev); } - free(buf); return 0; } @@ -949,7 +943,7 @@ static int systemd_fds(struct udev *udev, int *rctrl, int *rnetlink) { } /* - * read the kernel commandline, in case we need to get into debug mode + * read the kernel command line, in case we need to get into debug mode * udev.log-priority=<level> syslog priority * udev.children-max=<number of workers> events are fully serialized if set to 1 * udev.exec-delay=<number of seconds> delay execution of every executed program @@ -961,13 +955,13 @@ static void kernel_cmdline_options(struct udev *udev) { int r; r = proc_cmdline(&line); - if (r < 0) - log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r)); - if (r <= 0) + if (r < 0) { + log_warning_errno(r, "Failed to read /proc/cmdline, ignoring: %m"); return; + } FOREACH_WORD_QUOTED(word, l, line, state) { - char *s, *opt; + char *s, *opt, *value; s = strndup(word, l); if (!s) @@ -979,24 +973,23 @@ static void kernel_cmdline_options(struct udev *udev) { else opt = s; - if (startswith(opt, "udev.log-priority=")) { + if ((value = startswith(opt, "udev.log-priority="))) { int prio; - prio = util_log_priority(opt + 18); + prio = util_log_priority(value); log_set_max_level(prio); - udev_set_log_priority(udev, prio); - } else if (startswith(opt, "udev.children-max=")) { - r = safe_atoi(opt + 18, &arg_children_max); + } else if ((value = startswith(opt, "udev.children-max="))) { + r = safe_atoi(value, &arg_children_max); if (r < 0) - log_warning("Invalid udev.children-max ignored: %s", opt + 18); - } else if (startswith(opt, "udev.exec-delay=")) { - r = safe_atoi(opt + 16, &arg_exec_delay); + log_warning("Invalid udev.children-max ignored: %s", value); + } else if ((value = startswith(opt, "udev.exec-delay="))) { + r = safe_atoi(value, &arg_exec_delay); if (r < 0) - log_warning("Invalid udev.exec-delay ignored: %s", opt + 16); - } else if (startswith(opt, "udev.event-timeout=")) { - r = safe_atou64(opt + 16, &arg_event_timeout_usec); + log_warning("Invalid udev.exec-delay ignored: %s", value); + } else if ((value = startswith(opt, "udev.event-timeout="))) { + r = safe_atou64(value, &arg_event_timeout_usec); if (r < 0) { - log_warning("Invalid udev.event-timeout ignored: %s", opt + 16); + log_warning("Invalid udev.event-timeout ignored: %s", value); break; } arg_event_timeout_usec *= USEC_PER_SEC; @@ -1120,19 +1113,14 @@ int main(int argc, char *argv[]) { log_parse_environment(); log_open(); - udev_set_log_fn(udev, udev_main_log); - log_set_max_level(udev_get_log_priority(udev)); - r = parse_argv(argc, argv); if (r <= 0) goto exit; kernel_cmdline_options(udev); - if (arg_debug) { + if (arg_debug) log_set_max_level(LOG_DEBUG); - udev_set_log_priority(udev, LOG_DEBUG); - } if (getuid() != 0) { log_error("root privileges required"); @@ -1141,22 +1129,24 @@ int main(int argc, char *argv[]) { r = mac_selinux_init("/dev"); if (r < 0) { - log_error("could not initialize labelling: %s", strerror(-r)); + log_error_errno(r, "could not initialize labelling: %m"); goto exit; } /* set umask before creating any file/directory */ r = chdir("/"); if (r < 0) { - log_error("could not change dir to /: %m"); + log_error_errno(errno, "could not change dir to /: %m"); goto exit; } umask(022); + udev_list_init(udev, &properties_list, true); + r = mkdir("/run/udev", 0755); if (r < 0 && errno != EEXIST) { - log_error("could not create /run/udev: %m"); + log_error_errno(errno, "could not create /run/udev: %m"); goto exit; } @@ -1215,6 +1205,8 @@ int main(int argc, char *argv[]) { goto exit; } fd_netlink = udev_monitor_get_fd(monitor); + + udev_monitor_set_receive_buffer_size(monitor, 128 * 1024 * 1024); } if (udev_monitor_enable_receiving(monitor) < 0) { @@ -1229,9 +1221,7 @@ int main(int argc, char *argv[]) { goto exit; } - udev_monitor_set_receive_buffer_size(monitor, 128 * 1024 * 1024); - - log_info("starting version " VERSION "\n"); + log_info("starting version " VERSION); udev_builtin_init(udev); @@ -1243,7 +1233,7 @@ int main(int argc, char *argv[]) { rc = udev_rules_apply_static_dev_perms(rules); if (rc < 0) - log_error("failed to apply permissions on static device nodes - %s", strerror(-rc)); + log_error_errno(rc, "failed to apply permissions on static device nodes - %m"); if (arg_daemonize) { pid_t pid; @@ -1253,7 +1243,7 @@ int main(int argc, char *argv[]) { case 0: break; case -1: - log_error("fork of daemon failed: %m"); + log_error_errno(errno, "fork of daemon failed: %m"); rc = 4; goto exit; default: @@ -1316,7 +1306,7 @@ int main(int argc, char *argv[]) { fd_ep = epoll_create1(EPOLL_CLOEXEC); if (fd_ep < 0) { - log_error("error creating epoll fd: %m"); + log_error_errno(errno, "error creating epoll fd: %m"); goto exit; } if (epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_ctrl, &ep_ctrl) < 0 || @@ -1324,7 +1314,7 @@ int main(int argc, char *argv[]) { epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_signal, &ep_signal) < 0 || epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_netlink, &ep_netlink) < 0 || epoll_ctl(fd_ep, EPOLL_CTL_ADD, fd_worker, &ep_worker) < 0) { - log_error("fail to add fds to epoll: %m"); + log_error_errno(errno, "fail to add fds to epoll: %m"); goto exit; } @@ -1542,6 +1532,7 @@ exit_daemonize: udev_monitor_unref(monitor); udev_ctrl_connection_unref(ctrl_conn); udev_ctrl_unref(udev_ctrl); + udev_list_cleanup(&properties_list); mac_selinux_finish(); udev_unref(udev); log_close(); diff --git a/src/update-done/update-done.c b/src/update-done/update-done.c index 1437c30d14..a910808d04 100644 --- a/src/update-done/update-done.c +++ b/src/update-done/update-done.c @@ -46,13 +46,10 @@ static int apply_timestamp(const char *path, struct timespec *ts) { if (utimensat(AT_FDCWD, path, twice, AT_SYMLINK_NOFOLLOW) < 0) { - if (errno == EROFS) { - log_debug("Can't update timestamp file %s, file system is read-only.", path); - return 0; - } + if (errno == EROFS) + return log_debug("Can't update timestamp file %s, file system is read-only.", path); - log_error("Failed to update timestamp on %s: %m", path); - return -errno; + return log_error_errno(errno, "Failed to update timestamp on %s: %m", path); } } else if (errno == ENOENT) { @@ -62,24 +59,17 @@ static int apply_timestamp(const char *path, struct timespec *ts) { /* The timestamp file doesn't exist yet? Then let's create it. */ r = mac_selinux_create_file_prepare(path, S_IFREG); - if (r < 0) { - log_error("Failed to set SELinux context for %s: %s", - path, strerror(-r)); - return r; - } + if (r < 0) + return log_error_errno(r, "Failed to set SELinux context for %s: %m", path); fd = open(path, O_CREAT|O_EXCL|O_WRONLY|O_TRUNC|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0644); mac_selinux_create_file_clear(); if (fd < 0) { + if (errno == EROFS) + return log_debug("Can't create timestamp file %s, file system is read-only.", path); - if (errno == EROFS) { - log_debug("Can't create timestamp file %s, file system is read-only.", path); - return 0; - } - - log_error("Failed to create timestamp file %s: %m", path); - return -errno; + return log_error_errno(errno, "Failed to create timestamp file %s: %m", path); } (void) loop_write(fd, MESSAGE, strlen(MESSAGE), false); @@ -87,14 +77,10 @@ static int apply_timestamp(const char *path, struct timespec *ts) { twice[0] = *ts; twice[1] = *ts; - if (futimens(fd, twice) < 0) { - log_error("Failed to update timestamp on %s: %m", path); - return -errno; - } - } else { - log_error("Failed to stat() timestamp file %s: %m", path); - return -errno; - } + if (futimens(fd, twice) < 0) + return log_error_errno(errno, "Failed to update timestamp on %s: %m", path); + } else + log_error_errno(errno, "Failed to stat() timestamp file %s: %m", path); return 0; } @@ -108,13 +94,13 @@ int main(int argc, char *argv[]) { log_open(); if (stat("/usr", &st) < 0) { - log_error("Failed to stat /usr: %m"); + log_error_errno(errno, "Failed to stat /usr: %m"); return EXIT_FAILURE; } r = mac_selinux_init(NULL); if (r < 0) { - log_error("SELinux setup failed: %s", strerror(-r)); + log_error_errno(r, "SELinux setup failed: %m"); goto finish; } diff --git a/src/update-utmp/update-utmp.c b/src/update-utmp/update-utmp.c index 31cae70950..15da83193b 100644 --- a/src/update-utmp/update-utmp.c +++ b/src/update-utmp/update-utmp.c @@ -131,9 +131,9 @@ static int on_reboot(Context *c) { #ifdef HAVE_AUDIT if (c->audit_fd >= 0) - if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_BOOT, "init", NULL, NULL, NULL, 1) < 0 && + if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_BOOT, "", "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 && errno != EPERM) { - log_error("Failed to send audit message: %m"); + log_error_errno(errno, "Failed to send audit message: %m"); r = -errno; } #endif @@ -144,7 +144,7 @@ static int on_reboot(Context *c) { q = utmp_put_reboot(t); if (q < 0) { - log_error("Failed to write utmp record: %s", strerror(-q)); + log_error_errno(q, "Failed to write utmp record: %m"); r = q; } @@ -161,16 +161,16 @@ static int on_shutdown(Context *c) { #ifdef HAVE_AUDIT if (c->audit_fd >= 0) - if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_SHUTDOWN, "init", NULL, NULL, NULL, 1) < 0 && + if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_SHUTDOWN, "", "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 && errno != EPERM) { - log_error("Failed to send audit message: %m"); + log_error_errno(errno, "Failed to send audit message: %m"); r = -errno; } #endif q = utmp_put_shutdown(); if (q < 0) { - log_error("Failed to write utmp record: %s", strerror(-q)); + log_error_errno(q, "Failed to write utmp record: %m"); r = q; } @@ -189,10 +189,8 @@ static int on_runlevel(Context *c) { q = utmp_get_runlevel(&previous, NULL); if (q < 0) { - if (q != -ESRCH && q != -ENOENT) { - log_error("Failed to get current runlevel: %s", strerror(-q)); - return q; - } + if (q != -ESRCH && q != -ENOENT) + return log_error_errno(q, "Failed to get current runlevel: %m"); previous = 0; } @@ -215,9 +213,9 @@ static int on_runlevel(Context *c) { runlevel > 0 ? runlevel : 'N') < 0) return log_oom(); - if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_RUNLEVEL, s, NULL, NULL, NULL, 1) < 0 && + if (audit_log_user_comm_message(c->audit_fd, AUDIT_SYSTEM_RUNLEVEL, s, "systemd-update-utmp", NULL, NULL, NULL, 1) < 0 && errno != EPERM) { - log_error("Failed to send audit message: %m"); + log_error_errno(errno, "Failed to send audit message: %m"); r = -errno; } } @@ -225,7 +223,7 @@ static int on_runlevel(Context *c) { q = utmp_put_runlevel(runlevel, previous); if (q < 0 && q != -ESRCH && q != -ENOENT) { - log_error("Failed to write utmp record: %s", strerror(-q)); + log_error_errno(q, "Failed to write utmp record: %m"); r = q; } @@ -261,11 +259,11 @@ int main(int argc, char *argv[]) { * don't worry about it. */ c.audit_fd = audit_open(); if (c.audit_fd < 0 && errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT) - log_error("Failed to connect to audit log: %m"); + log_error_errno(errno, "Failed to connect to audit log: %m"); #endif r = bus_open_system_systemd(&c.bus); if (r < 0) { - log_error("Failed to get D-Bus connection: %s", strerror(-r)); + log_error_errno(r, "Failed to get D-Bus connection: %m"); r = -EIO; goto finish; } diff --git a/src/vconsole/90-vconsole.rules.in b/src/vconsole/90-vconsole.rules.in new file mode 100644 index 0000000000..062009640c --- /dev/null +++ b/src/vconsole/90-vconsole.rules.in @@ -0,0 +1,11 @@ +# This file is part of systemd. +# +# 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. + +# Kernel resets vconsole state when changing console drivers so run +# systemd-vconsole-setup when fbcon loads + +ACTION=="add", SUBSYSTEM=="graphics", KERNEL=="fbcon", RUN+="@rootlibexecdir@/systemd-vconsole-setup" diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index 0db97f88bc..28371711b6 100644 --- a/src/vconsole/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -54,15 +54,16 @@ static int disable_utf8(int fd) { if (ioctl(fd, KDSKBMODE, K_XLATE) < 0) r = -errno; - if (loop_write(fd, "\033%@", 3, false) < 0) - r = -errno; + k = loop_write(fd, "\033%@", 3, false); + if (k < 0) + r = k; k = write_string_file("/sys/module/vt/parameters/default_utf8", "0"); if (k < 0) r = k; if (r < 0) - log_warning("Failed to disable UTF-8: %s", strerror(-r)); + log_warning_errno(r, "Failed to disable UTF-8: %m"); return r; } @@ -86,15 +87,16 @@ static int enable_utf8(int fd) { r = -errno; } - if (loop_write(fd, "\033%G", 3, false) < 0) - r = -errno; + k = loop_write(fd, "\033%G", 3, false); + if (k < 0) + r = k; k = write_string_file("/sys/module/vt/parameters/default_utf8", "1"); if (k < 0) r = k; if (r < 0) - log_warning("Failed to enable UTF-8: %s", strerror(-r)); + log_warning_errno(r, "Failed to enable UTF-8: %m"); return r; } @@ -122,10 +124,9 @@ static int keymap_load(const char *vc, const char *map, const char *map_toggle, args[i++] = NULL; pid = fork(); - if (pid < 0) { - log_error("Failed to fork: %m"); - return -errno; - } else if (pid == 0) { + if (pid < 0) + return log_error_errno(errno, "Failed to fork: %m"); + else if (pid == 0) { execv(args[0], (char **) args); _exit(EXIT_FAILURE); } @@ -160,10 +161,9 @@ static int font_load(const char *vc, const char *font, const char *map, const ch args[i++] = NULL; pid = fork(); - if (pid < 0) { - log_error("Failed to fork: %m"); - return -errno; - } else if (pid == 0) { + if (pid < 0) + return log_error_errno(errno, "Failed to fork: %m"); + else if (pid == 0) { execv(args[0], (char **) args); _exit(EXIT_FAILURE); } @@ -262,7 +262,7 @@ int main(int argc, char **argv) { fd = open_terminal(vc, O_RDWR|O_CLOEXEC); if (fd < 0) { - log_error("Failed to open %s: %m", vc); + log_error_errno(errno, "Failed to open %s: %m", vc); return EXIT_FAILURE; } @@ -282,7 +282,7 @@ int main(int argc, char **argv) { NULL); if (r < 0 && r != -ENOENT) - log_warning("Failed to read /etc/vconsole.conf: %s", strerror(-r)); + log_warning_errno(r, "Failed to read /etc/vconsole.conf: %m"); /* Let the kernel command line override /etc/vconsole.conf */ if (detect_container(NULL) <= 0) { @@ -295,7 +295,7 @@ int main(int argc, char **argv) { NULL); if (r < 0 && r != -ENOENT) - log_warning("Failed to read /proc/cmdline: %s", strerror(-r)); + log_warning_errno(r, "Failed to read /proc/cmdline: %m"); } if (utf8) @@ -305,21 +305,21 @@ int main(int argc, char **argv) { r = font_load(vc, vc_font, vc_font_map, vc_font_unimap, &font_pid); if (r < 0) { - log_error("Failed to start " KBD_SETFONT ": %s", strerror(-r)); + log_error_errno(r, "Failed to start " KBD_SETFONT ": %m"); return EXIT_FAILURE; } if (font_pid > 0) - wait_for_terminate_and_warn(KBD_SETFONT, font_pid); + wait_for_terminate_and_warn(KBD_SETFONT, font_pid, true); r = keymap_load(vc, vc_keymap, vc_keymap_toggle, utf8, &keymap_pid); if (r < 0) { - log_error("Failed to start " KBD_LOADKEYS ": %s", strerror(-r)); + log_error_errno(r, "Failed to start " KBD_LOADKEYS ": %m"); return EXIT_FAILURE; } if (keymap_pid > 0) - wait_for_terminate_and_warn(KBD_LOADKEYS, keymap_pid); + wait_for_terminate_and_warn(KBD_LOADKEYS, keymap_pid, true); /* Only copy the font when we started setfont successfully */ if (font_copy && font_pid > 0) |