diff options
| -rw-r--r-- | man/journalctl.xml | 2 | ||||
| -rw-r--r-- | src/basic/fileio.c | 41 | ||||
| -rw-r--r-- | src/basic/fileio.h | 2 | ||||
| -rw-r--r-- | src/basic/fs-util.c | 29 | ||||
| -rw-r--r-- | src/basic/fs-util.h | 2 | ||||
| -rw-r--r-- | src/core/cgroup.c | 5 | ||||
| -rw-r--r-- | src/journal/journal-verify.c | 15 | ||||
| -rw-r--r-- | src/libsystemd-network/network-internal.c | 8 | ||||
| -rw-r--r-- | src/libsystemd/sd-device/device-enumerator.c | 34 | ||||
| -rw-r--r-- | src/libsystemd/sd-device/device-internal.h | 2 | ||||
| -rw-r--r-- | src/libsystemd/sd-device/sd-device.c | 127 | ||||
| -rw-r--r-- | src/machine/image-dbus.c | 4 | ||||
| -rw-r--r-- | src/machine/machine-dbus.c | 4 | ||||
| -rw-r--r-- | src/machine/machinectl.c | 19 | ||||
| -rw-r--r-- | src/machine/machined-dbus.c | 214 | ||||
| -rw-r--r-- | src/machine/operation.c | 41 | ||||
| -rw-r--r-- | src/machine/operation.h | 4 | ||||
| -rw-r--r-- | src/machine/org.freedesktop.machine1.conf | 4 | ||||
| -rw-r--r-- | src/shared/condition.c | 7 | ||||
| -rw-r--r-- | src/test/test-condition.c | 6 | ||||
| -rw-r--r-- | src/test/test-fs-util.c | 46 | ||||
| -rwxr-xr-x | test/networkd-test.py | 75 | 
22 files changed, 521 insertions, 170 deletions
| diff --git a/man/journalctl.xml b/man/journalctl.xml index 3efe6ef62a..29239c6315 100644 --- a/man/journalctl.xml +++ b/man/journalctl.xml @@ -824,7 +824,7 @@          flushed from <filename>/run/log/journal</filename> into          <filename>/var/log/journal</filename> once during system          runtime, and this command exits cleanly without executing any -        operation if this has already has happened. This command +        operation if this has already happened. This command          effectively guarantees that all data is flushed to          <filename>/var/log/journal</filename> at the time it          returns.</para></listitem> diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 29f5374222..0360a8eab3 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -1354,3 +1354,44 @@ int link_tmpfile(int fd, const char *path, const char *target) {          return 0;  } + +int read_nul_string(FILE *f, char **ret) { +        _cleanup_free_ char *x = NULL; +        size_t allocated = 0, n = 0; + +        assert(f); +        assert(ret); + +        /* Reads a NUL-terminated string from the specified file. */ + +        for (;;) { +                int c; + +                if (!GREEDY_REALLOC(x, allocated, n+2)) +                        return -ENOMEM; + +                c = fgetc(f); +                if (c == 0) /* Terminate at NUL byte */ +                        break; +                if (c == EOF) { +                        if (ferror(f)) +                                return -errno; +                        break; /* Terminate at EOF */ +                } + +                x[n++] = (char) c; +        } + +        if (x) +                x[n] = 0; +        else { +                x = new0(char, 1); +                if (!x) +                        return -ENOMEM; +        } + +        *ret = x; +        x = NULL; + +        return 0; +} diff --git a/src/basic/fileio.h b/src/basic/fileio.h index 58dbc80c24..9ac497d9eb 100644 --- a/src/basic/fileio.h +++ b/src/basic/fileio.h @@ -86,3 +86,5 @@ int open_tmpfile_unlinkable(const char *directory, int flags);  int open_tmpfile_linkable(const char *target, int flags, char **ret_path);  int link_tmpfile(int fd, const char *path, const char *target); + +int read_nul_string(FILE *f, char **ret); diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index e24e7036f7..f0c6f3265e 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -38,6 +38,7 @@  #include "mkdir.h"  #include "parse-util.h"  #include "path-util.h" +#include "stat-util.h"  #include "stdio-util.h"  #include "string-util.h"  #include "strv.h" @@ -495,6 +496,34 @@ int get_files_in_directory(const char *path, char ***list) {          return n;  } +int var_tmp(char **ret) { +        const char *tmp_dir = NULL; +        const char *env_tmp_dir = NULL; +        char *c = NULL; +        int r; + +        assert(ret); + +        env_tmp_dir = getenv("TMPDIR"); +        if (env_tmp_dir != NULL) { +                r = is_dir(env_tmp_dir, true); +                if (r < 0 && r != -ENOENT) +                        return r; +                if (r > 0) +                        tmp_dir = env_tmp_dir; +        } + +        if (!tmp_dir) +                tmp_dir = "/var/tmp"; + +        c = strdup(tmp_dir); +        if (!c) +                return -ENOMEM; +        *ret = c; + +        return 0; +} +  int inotify_add_watch_fd(int fd, int what, uint32_t mask) {          char path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];          int r; diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index 517b599d6f..075e5942b1 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -61,6 +61,8 @@ int mkfifo_atomic(const char *path, mode_t mode);  int get_files_in_directory(const char *path, char ***list); +int var_tmp(char **ret); +  #define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1)  #define FOREACH_INOTIFY_EVENT(e, buffer, sz) \ diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 799296ad28..932160d276 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -36,8 +36,7 @@  #define CGROUP_CPU_QUOTA_PERIOD_USEC ((usec_t) 100 * USEC_PER_MSEC) -static void cgroup_compat_warn(void) -{ +static void cgroup_compat_warn(void) {          static bool cgroup_compat_warned = false;          if (cgroup_compat_warned) @@ -50,7 +49,7 @@ static void cgroup_compat_warn(void)  #define log_cgroup_compat(unit, fmt, ...) do {                                  \                  cgroup_compat_warn();                                           \                  log_unit_debug(unit, "cgroup-compat: " fmt, ##__VA_ARGS__);     \ -        } while (0) +        } while (false)  void cgroup_context_init(CGroupContext *c) {          assert(c); diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index a37316b8f9..f61f158e8a 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -26,6 +26,7 @@  #include "compress.h"  #include "fd-util.h"  #include "fileio.h" +#include "fs-util.h"  #include "journal-authenticate.h"  #include "journal-def.h"  #include "journal-file.h" @@ -825,6 +826,8 @@ int journal_file_verify(          int data_fd = -1, entry_fd = -1, entry_array_fd = -1;          unsigned i;          bool found_last = false; +        _cleanup_free_ char *tmp_dir = NULL; +  #ifdef HAVE_GCRYPT          uint64_t last_tag = 0;  #endif @@ -843,19 +846,25 @@ int journal_file_verify(          } else if (f->seal)                  return -ENOKEY; -        data_fd = open_tmpfile_unlinkable("/var/tmp", O_RDWR | O_CLOEXEC); +        r = var_tmp(&tmp_dir); +        if (r < 0) { +                log_error_errno(r, "Failed to determine temporary directory: %m"); +                goto fail; +        } + +        data_fd = open_tmpfile_unlinkable(tmp_dir, O_RDWR | O_CLOEXEC);          if (data_fd < 0) {                  r = log_error_errno(data_fd, "Failed to create data file: %m");                  goto fail;          } -        entry_fd = open_tmpfile_unlinkable("/var/tmp", O_RDWR | O_CLOEXEC); +        entry_fd = open_tmpfile_unlinkable(tmp_dir, O_RDWR | O_CLOEXEC);          if (entry_fd < 0) {                  r = log_error_errno(entry_fd, "Failed to create entry file: %m");                  goto fail;          } -        entry_array_fd = open_tmpfile_unlinkable("/var/tmp", O_RDWR | O_CLOEXEC); +        entry_array_fd = open_tmpfile_unlinkable(tmp_dir, O_RDWR | O_CLOEXEC);          if (entry_array_fd < 0) {                  r = log_error_errno(entry_array_fd,                                      "Failed to create entry array file: %m"); diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c index ce30b7fc25..9d78b953fc 100644 --- a/src/libsystemd-network/network-internal.c +++ b/src/libsystemd-network/network-internal.c @@ -102,16 +102,16 @@ bool net_match_config(const struct ether_addr *match_mac,                        const char *dev_type,                        const char *dev_name) { -        if (match_host && !condition_test(match_host)) +        if (match_host && condition_test(match_host) <= 0)                  return false; -        if (match_virt && !condition_test(match_virt)) +        if (match_virt && condition_test(match_virt) <= 0)                  return false; -        if (match_kernel && !condition_test(match_kernel)) +        if (match_kernel && condition_test(match_kernel) <= 0)                  return false; -        if (match_arch && !condition_test(match_arch)) +        if (match_arch && condition_test(match_arch) <= 0)                  return false;          if (match_mac && (!dev_mac || memcmp(match_mac, dev_mac, ETH_ALEN))) diff --git a/src/libsystemd/sd-device/device-enumerator.c b/src/libsystemd/sd-device/device-enumerator.c index 4a7a8b1f9e..62d03ae00d 100644 --- a/src/libsystemd/sd-device/device-enumerator.c +++ b/src/libsystemd/sd-device/device-enumerator.c @@ -696,17 +696,19 @@ static int enumerator_scan_devices_tag(sd_device_enumerator *enumerator, const c  static int enumerator_scan_devices_tags(sd_device_enumerator *enumerator) {          const char *tag;          Iterator i; -        int r; +        int r = 0;          assert(enumerator);          SET_FOREACH(tag, enumerator->match_tag, i) { -                r = enumerator_scan_devices_tag(enumerator, tag); -                if (r < 0) -                        return r; +                int k; + +                k = enumerator_scan_devices_tag(enumerator, tag); +                if (k < 0) +                        r = k;          } -        return 0; +        return r;  }  static int parent_add_child(sd_device_enumerator *enumerator, const char *path) { @@ -838,7 +840,7 @@ static int enumerator_scan_devices_all(sd_device_enumerator *enumerator) {  int device_enumerator_scan_devices(sd_device_enumerator *enumerator) {          sd_device *device; -        int r; +        int r = 0, k;          assert(enumerator); @@ -850,22 +852,22 @@ int device_enumerator_scan_devices(sd_device_enumerator *enumerator) {                  sd_device_unref(device);          if (!set_isempty(enumerator->match_tag)) { -                r = enumerator_scan_devices_tags(enumerator); -                if (r < 0) -                        return r; +                k = enumerator_scan_devices_tags(enumerator); +                if (k < 0) +                        r = k;          } else if (enumerator->match_parent) { -                r = enumerator_scan_devices_children(enumerator); -                if (r < 0) -                        return r; +                k = enumerator_scan_devices_children(enumerator); +                if (k < 0) +                        r = k;          } else { -                r = enumerator_scan_devices_all(enumerator); -                if (r < 0) -                        return r; +                k = enumerator_scan_devices_all(enumerator); +                if (k < 0) +                        r = k;          }          enumerator->scan_uptodate = true; -        return 0; +        return r;  }  _public_ sd_device *sd_device_enumerator_get_device_first(sd_device_enumerator *enumerator) { diff --git a/src/libsystemd/sd-device/device-internal.h b/src/libsystemd/sd-device/device-internal.h index ab222e27de..9fad388953 100644 --- a/src/libsystemd/sd-device/device-internal.h +++ b/src/libsystemd/sd-device/device-internal.h @@ -76,6 +76,8 @@ struct sd_device {          char *subsystem;          bool subsystem_set; /* don't reread subsystem */ +        char *driver_subsystem; /* only set for the 'drivers' subsystem */ +        bool driver_subsystem_set; /* don't reread subsystem */          char *driver;          bool driver_set; /* don't reread driver */ diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c index 5c9e00ed80..d503232505 100644 --- a/src/libsystemd/sd-device/sd-device.c +++ b/src/libsystemd/sd-device/sd-device.c @@ -75,6 +75,7 @@ _public_ sd_device *sd_device_unref(sd_device *device) {                  free(device->devtype);                  free(device->devname);                  free(device->subsystem); +                free(device->driver_subsystem);                  free(device->driver);                  free(device->id_filename);                  free(device->properties_strv); @@ -258,7 +259,8 @@ _public_ int sd_device_new_from_devnum(sd_device **ret, char type, dev_t devnum)  }  _public_ int sd_device_new_from_subsystem_sysname(sd_device **ret, const char *subsystem, const char *sysname) { -        char *syspath; +        char *name, *syspath; +        size_t len = 0;          assert_return(ret, -EINVAL);          assert_return(subsystem, -EINVAL); @@ -297,33 +299,29 @@ _public_ int sd_device_new_from_subsystem_sysname(sd_device **ret, const char *s                          syspath = strjoina("/sys/bus/", subsys, "/drivers/", driver);                          if (access(syspath, F_OK) >= 0)                                  return sd_device_new_from_syspath(ret, syspath); -                } else -                        return -EINVAL; -        } else { -                char *name; -                size_t len = 0; +                } +        } -                /* translate sysname back to sysfs filename */ -                name = strdupa(sysname); -                while (name[len] != '\0') { -                        if (name[len] == '/') -                                name[len] = '!'; +        /* translate sysname back to sysfs filename */ +        name = strdupa(sysname); +        while (name[len] != '\0') { +                if (name[len] == '/') +                        name[len] = '!'; -                        len++; -                } +                len++; +        } -                syspath = strjoina("/sys/subsystem/", subsystem, "/devices/", name); -                if (access(syspath, F_OK) >= 0) -                        return sd_device_new_from_syspath(ret, syspath); +        syspath = strjoina("/sys/subsystem/", subsystem, "/devices/", name); +        if (access(syspath, F_OK) >= 0) +                return sd_device_new_from_syspath(ret, syspath); -                syspath = strjoina("/sys/bus/", subsystem, "/devices/", name); -                if (access(syspath, F_OK) >= 0) -                        return sd_device_new_from_syspath(ret, syspath); +        syspath = strjoina("/sys/bus/", subsystem, "/devices/", name); +        if (access(syspath, F_OK) >= 0) +                return sd_device_new_from_syspath(ret, syspath); -                syspath = strjoina("/sys/class/", subsystem, "/", name); -                if (access(syspath, F_OK) >= 0) -                        return sd_device_new_from_syspath(ret, syspath); -        } +        syspath = strjoina("/sys/class/", subsystem, "/", name); +        if (access(syspath, F_OK) >= 0) +                return sd_device_new_from_syspath(ret, syspath);          return -ENODEV;  } @@ -766,21 +764,45 @@ int device_set_subsystem(sd_device *device, const char *_subsystem) {          return 0;  } +static int device_set_drivers_subsystem(sd_device *device, const char *_subsystem) { +        _cleanup_free_ char *subsystem = NULL; +        int r; + +        assert(device); +        assert(_subsystem); +        assert(*_subsystem); + +        subsystem = strdup(_subsystem); +        if (!subsystem) +                return -ENOMEM; + +        r = device_set_subsystem(device, "drivers"); +        if (r < 0) +                return r; + +        free(device->driver_subsystem); +        device->driver_subsystem = subsystem; +        subsystem = NULL; + +        return 0; +} +  _public_ int sd_device_get_subsystem(sd_device *device, const char **ret) { +        const char *syspath, *drivers = NULL; +        int r; +          assert_return(ret, -EINVAL);          assert_return(device, -EINVAL); +        r = sd_device_get_syspath(device, &syspath); +        if (r < 0) +                return r; +          if (!device->subsystem_set) {                  _cleanup_free_ char *subsystem = NULL; -                const char *syspath;                  char *path; -                int r;                  /* read 'subsystem' link */ -                r = sd_device_get_syspath(device, &syspath); -                if (r < 0) -                        return r; -                  path = strjoina(syspath, "/subsystem");                  r = readlink_value(path, &subsystem);                  if (r >= 0) @@ -788,16 +810,39 @@ _public_ int sd_device_get_subsystem(sd_device *device, const char **ret) {                  /* use implicit names */                  else if (path_startswith(device->devpath, "/module/"))                          r = device_set_subsystem(device, "module"); -                else if (strstr(device->devpath, "/drivers/")) -                        r = device_set_subsystem(device, "drivers"); -                else if (path_startswith(device->devpath, "/subsystem/") || -                         path_startswith(device->devpath, "/class/") || -                         path_startswith(device->devpath, "/bus/")) +                else if (!(drivers = strstr(syspath, "/drivers/")) && +                         (path_startswith(device->devpath, "/subsystem/") || +                          path_startswith(device->devpath, "/class/") || +                          path_startswith(device->devpath, "/bus/")))                          r = device_set_subsystem(device, "subsystem");                  if (r < 0 && r != -ENOENT)                          return log_debug_errno(r, "sd-device: could not set subsystem for %s: %m", device->devpath);                  device->subsystem_set = true; +        } else if (!device->driver_subsystem_set) +                drivers = strstr(syspath, "/drivers/"); + +        if (!device->driver_subsystem_set) { +                if (drivers) { +                        _cleanup_free_ char *subpath = NULL; + +                        subpath = strndup(syspath, drivers - syspath); +                        if (!subpath) +                                r = -ENOMEM; +                        else { +                                const char *subsys; + +                                subsys = strrchr(subpath, '/'); +                                if (!subsys) +                                        r = -EINVAL; +                                else +                                        r = device_set_drivers_subsystem(device, subsys + 1); +                        } +                        if (r < 0 && r != -ENOENT) +                                return log_debug_errno(r, "sd-device: could not set subsystem for driver %s: %m", device->devpath); +                } + +                device->driver_subsystem_set = true;          }          if (!device->subsystem) @@ -1234,9 +1279,17 @@ int device_get_id_filename(sd_device *device, const char **ret) {                          if (!subsystem)                                  return -EINVAL; -                        r = asprintf(&id, "+%s:%s", subsystem, sysname); -                        if (r < 0) -                                return -ENOMEM; +                        if (streq(subsystem, "drivers")) { +                                /* the 'drivers' pseudo-subsystem is special, and needs the real subsystem +                                 * encoded as well */ +                                r = asprintf(&id, "+drivers:%s:%s", device->driver_subsystem, sysname); +                                if (r < 0) +                                        return -ENOMEM; +                        } else { +                                r = asprintf(&id, "+%s:%s", subsystem, sysname); +                                if (r < 0) +                                        return -ENOMEM; +                        }                  }                  device->id_filename = id; diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c index 0eed9b81bb..867bbc467b 100644 --- a/src/machine/image-dbus.c +++ b/src/machine/image-dbus.c @@ -81,7 +81,7 @@ int bus_image_method_remove(          errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); -        r = operation_new(m, NULL, child, message, errno_pipe_fd[0]); +        r = operation_new(m, NULL, child, message, errno_pipe_fd[0], NULL);          if (r < 0) {                  (void) sigkill_wait(child);                  return r; @@ -193,7 +193,7 @@ int bus_image_method_clone(          errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); -        r = operation_new(m, NULL, child, message, errno_pipe_fd[0]); +        r = operation_new(m, NULL, child, message, errno_pipe_fd[0], NULL);          if (r < 0) {                  (void) sigkill_wait(child);                  return r; diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index de5d98f23e..ba7ac04b56 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -1200,8 +1200,6 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro          child_fail:                  (void) write(errno_pipe_fd[1], &r, sizeof(r)); -                errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); -                  _exit(EXIT_FAILURE);          } @@ -1209,7 +1207,7 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro          /* Copying might take a while, hence install a watch on the child, and return */ -        r = operation_new(m->manager, m, child, message, errno_pipe_fd[0]); +        r = operation_new(m->manager, m, child, message, errno_pipe_fd[0], NULL);          if (r < 0) {                  (void) sigkill_wait(child);                  return r; diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 843e678eca..161dd3922b 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -2418,7 +2418,7 @@ static int set_limit(int argc, char *argv[], void *userdata) {  }  static int clean_images(int argc, char *argv[], void *userdata) { -        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; +        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;          _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;          uint64_t usage, total = 0;          char fb[FORMAT_BYTES_MAX]; @@ -2427,15 +2427,22 @@ static int clean_images(int argc, char *argv[], void *userdata) {          unsigned c = 0;          int r; -        r = sd_bus_call_method( +        r = sd_bus_message_new_method_call(                          bus, +                        &m,                          "org.freedesktop.machine1",                          "/org/freedesktop/machine1",                          "org.freedesktop.machine1.Manager", -                        "CleanPool", -                        &error, -                        &reply, -                        "s", arg_all ? "all" : "hidden"); +                        "CleanPool"); +        if (r < 0) +                return bus_log_create_error(r); + +        r = sd_bus_message_append(m, "s", arg_all ? "all" : "hidden"); +        if (r < 0) +                return bus_log_create_error(r); + +        /* This is a slow operation, hence permit a longer time for completion. */ +        r = sd_bus_call(bus, m, USEC_INFINITY, &error, &reply);          if (r < 0)                  return log_error_errno(r, "Could not clean pool: %s", bus_error_message(&error, r)); diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 31efa3695b..52ce83a185 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -29,6 +29,7 @@  #include "bus-util.h"  #include "cgroup-util.h"  #include "fd-util.h" +#include "fileio.h"  #include "formats-util.h"  #include "hostname-util.h"  #include "image-dbus.h" @@ -822,22 +823,106 @@ static int method_mark_image_read_only(sd_bus_message *message, void *userdata,          return bus_image_method_mark_read_only(message, i, error);  } +static int clean_pool_done(Operation *operation, int ret, sd_bus_error *error) { +        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; +        _cleanup_fclose_ FILE *f = NULL; +        bool success; +        size_t n; +        int r; + +        assert(operation); +        assert(operation->extra_fd >= 0); + +        if (lseek(operation->extra_fd, 0, SEEK_SET) == (off_t) -1) +                return -errno; + +        f = fdopen(operation->extra_fd, "re"); +        if (!f) +                return -errno; + +        operation->extra_fd = -1; + +        /* The resulting temporary file starts with a boolean value that indicates success or not. */ +        errno = 0; +        n = fread(&success, 1, sizeof(success), f); +        if (n != sizeof(success)) +                return ret < 0 ? ret : (errno != 0 ? -errno : -EIO); + +        if (ret < 0) { +                _cleanup_free_ char *name = NULL; + +                /* The clean-up operation failed. In this case the resulting temporary file should contain a boolean +                 * set to false followed by the name of the failed image. Let's try to read this and use it for the +                 * error message. If we can't read it, don't mind, and return the naked error. */ + +                if (success) /* The resulting temporary file could not be updated, ignore it. */ +                        return ret; + +                r = read_nul_string(f, &name); +                if (r < 0 || isempty(name)) /* Same here... */ +                        return ret; + +                return sd_bus_error_set_errnof(error, ret, "Failed to remove image %s: %m", name); +        } + +        assert(success); + +        r = sd_bus_message_new_method_return(operation->message, &reply); +        if (r < 0) +                return r; + +        r = sd_bus_message_open_container(reply, 'a', "(st)"); +        if (r < 0) +                return r; + +        /* On success the resulting temporary file will contain a list of image names that were removed followed by +         * their size on disk. Let's read that and turn it into a bus message. */ +        for (;;) { +                _cleanup_free_ char *name = NULL; +                uint64_t size; + +                r = read_nul_string(f, &name); +                if (r < 0) +                        return r; +                if (isempty(name)) /* reached the end */ +                        break; + +                errno = 0; +                n = fread(&size, 1, sizeof(size), f); +                if (n != sizeof(size)) +                        return errno != 0 ? -errno : -EIO; + +                r = sd_bus_message_append(reply, "(st)", name, size); +                if (r < 0) +                        return r; +        } + +        r = sd_bus_message_close_container(reply); +        if (r < 0) +                return r; + +        return sd_bus_send(NULL, reply, NULL); +} +  static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_error *error) {          enum {                  REMOVE_ALL,                  REMOVE_HIDDEN,          } mode; -        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; -        _cleanup_(image_hashmap_freep) Hashmap *images = NULL; +        _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; +        _cleanup_close_ int result_fd = -1;          Manager *m = userdata; -        Image *image; +        Operation *operation;          const char *mm; -        Iterator i; +        pid_t child;          int r;          assert(message); +        if (m->n_operations >= OPERATIONS_MAX) +                return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations."); +          r = sd_bus_message_read(message, "s", &mm);          if (r < 0)                  return r; @@ -863,50 +948,109 @@ static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_err          if (r == 0)                  return 1; /* Will call us back */ -        images = hashmap_new(&string_hash_ops); -        if (!images) -                return -ENOMEM; +        if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) +                return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m"); -        r = image_discover(images); -        if (r < 0) -                return r; +        /* Create a temporary file we can dump information about deleted images into. We use a temporary file for this +         * instead of a pipe or so, since this might grow quit large in theory and we don't want to process this +         * continously */ +        result_fd = open_tmpfile_unlinkable("/tmp/", O_RDWR|O_CLOEXEC); +        if (result_fd < 0) +                return -errno; -        r = sd_bus_message_new_method_return(message, &reply); -        if (r < 0) -                return r; +        /* This might be a slow operation, run it asynchronously in a background process */ +        child = fork(); +        if (child < 0) +                return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m"); -        r = sd_bus_message_open_container(reply, 'a', "(st)"); -        if (r < 0) -                return r; +        if (child == 0) { +                _cleanup_(image_hashmap_freep) Hashmap *images = NULL; +                bool success = true; +                Image *image; +                Iterator i; +                ssize_t l; -        HASHMAP_FOREACH(image, images, i) { +                errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); -                /* We can't remove vendor images (i.e. those in /usr) */ -                if (IMAGE_IS_VENDOR(image)) -                        continue; +                images = hashmap_new(&string_hash_ops); +                if (!images) { +                        r = -ENOMEM; +                        goto child_fail; +                } -                if (IMAGE_IS_HOST(image)) -                        continue; +                r = image_discover(images); +                if (r < 0) +                        goto child_fail; -                if (mode == REMOVE_HIDDEN && !IMAGE_IS_HIDDEN(image)) -                        continue; +                l = write(result_fd, &success, sizeof(success)); +                if (l < 0) { +                        r = -errno; +                        goto child_fail; +                } -                r = image_remove(image); -                if (r == -EBUSY) /* keep images that are currently being used. */ -                        continue; -                if (r < 0) -                        return sd_bus_error_set_errnof(error, r, "Failed to remove image %s: %m", image->name); +                HASHMAP_FOREACH(image, images, i) { -                r = sd_bus_message_append(reply, "(st)", image->name, image->usage_exclusive); -                if (r < 0) -                        return r; +                        /* We can't remove vendor images (i.e. those in /usr) */ +                        if (IMAGE_IS_VENDOR(image)) +                                continue; + +                        if (IMAGE_IS_HOST(image)) +                                continue; + +                        if (mode == REMOVE_HIDDEN && !IMAGE_IS_HIDDEN(image)) +                                continue; + +                        r = image_remove(image); +                        if (r == -EBUSY) /* keep images that are currently being used. */ +                                continue; +                        if (r < 0) { +                                /* If the operation failed, let's override everything we wrote, and instead write there at which image we failed. */ +                                success = false; +                                (void) ftruncate(result_fd, 0); +                                (void) lseek(result_fd, 0, SEEK_SET); +                                (void) write(result_fd, &success, sizeof(success)); +                                (void) write(result_fd, image->name, strlen(image->name)+1); +                                goto child_fail; +                        } + +                        l = write(result_fd, image->name, strlen(image->name)+1); +                        if (l < 0) { +                                r = -errno; +                                goto child_fail; +                        } + +                        l = write(result_fd, &image->usage_exclusive, sizeof(image->usage_exclusive)); +                        if (l < 0) { +                                r = -errno; +                                goto child_fail; +                        } +                } + +                result_fd = safe_close(result_fd); +                _exit(EXIT_SUCCESS); + +        child_fail: +                (void) write(errno_pipe_fd[1], &r, sizeof(r)); +                _exit(EXIT_FAILURE);          } -        r = sd_bus_message_close_container(reply); -        if (r < 0) +        errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); + +        /* The clean-up might take a while, hence install a watch on the child and return */ + +        r = operation_new(m, NULL, child, message, errno_pipe_fd[0], &operation); +        if (r < 0) { +                (void) sigkill_wait(child);                  return r; +        } -        return sd_bus_send(NULL, reply, NULL); +        operation->extra_fd = result_fd; +        operation->done = clean_pool_done; + +        result_fd = -1; +        errno_pipe_fd[0] = -1; + +        return 1;  }  static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus_error *error) { diff --git a/src/machine/operation.c b/src/machine/operation.c index e6ddc41a55..8f8321a8b3 100644 --- a/src/machine/operation.c +++ b/src/machine/operation.c @@ -41,18 +41,33 @@ static int operation_done(sd_event_source *s, const siginfo_t *si, void *userdat                  goto fail;          } -        if (si->si_status != EXIT_SUCCESS) { -                if (read(o->errno_fd, &r, sizeof(r)) == sizeof(r)) -                        r = sd_bus_error_set_errnof(&error, r, "%m"); -                else -                        r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child failed."); - +        if (si->si_status == EXIT_SUCCESS) +                r = 0; +        else if (read(o->errno_fd, &r, sizeof(r)) != sizeof(r)) { /* Try to acquire error code for failed operation */ +                r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child failed.");                  goto fail;          } -        r = sd_bus_reply_method_return(o->message, NULL); -        if (r < 0) -                log_error_errno(r, "Failed to reply to message: %m"); +        if (o->done) { +                /* A completion routine is set for this operation, call it. */ +                r = o->done(o, r, &error); +                if (r < 0) { +                        if (!sd_bus_error_is_set(&error)) +                                sd_bus_error_set_errno(&error, r); + +                        goto fail; +                } + +        } else { +                /* The default default operaton when done is to simply return an error on failure or an empty success +                 * message on success. */ +                if (r < 0) +                        goto fail; + +                r = sd_bus_reply_method_return(o->message, NULL); +                if (r < 0) +                        log_error_errno(r, "Failed to reply to message: %m"); +        }          operation_free(o);          return 0; @@ -66,7 +81,7 @@ fail:          return 0;  } -int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_message *message, int errno_fd) { +int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_message *message, int errno_fd, Operation **ret) {          Operation *o;          int r; @@ -79,6 +94,8 @@ int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_messag          if (!o)                  return -ENOMEM; +        o->extra_fd = -1; +          r = sd_event_add_child(manager->event, &o->event_source, child, WEXITED, operation_done, o);          if (r < 0) {                  free(o); @@ -102,6 +119,9 @@ int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_messag          /* At this point we took ownership of both the child and the errno file descriptor! */ +        if (ret) +                *ret = o; +          return 0;  } @@ -112,6 +132,7 @@ Operation *operation_free(Operation *o) {          sd_event_source_unref(o->event_source);          safe_close(o->errno_fd); +        safe_close(o->extra_fd);          if (o->pid > 1)                  (void) sigkill_wait(o->pid); diff --git a/src/machine/operation.h b/src/machine/operation.h index 7ca47bc3af..9831b123d7 100644 --- a/src/machine/operation.h +++ b/src/machine/operation.h @@ -38,10 +38,12 @@ struct Operation {          pid_t pid;          sd_bus_message *message;          int errno_fd; +        int extra_fd;          sd_event_source *event_source; +        int (*done)(Operation *o, int ret, sd_bus_error *error);          LIST_FIELDS(Operation, operations);          LIST_FIELDS(Operation, operations_by_machine);  }; -int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_message *message, int errno_fd); +int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_message *message, int errno_fd, Operation **ret);  Operation *operation_free(Operation *o); diff --git a/src/machine/org.freedesktop.machine1.conf b/src/machine/org.freedesktop.machine1.conf index 9d40b90151..562b9d3cc0 100644 --- a/src/machine/org.freedesktop.machine1.conf +++ b/src/machine/org.freedesktop.machine1.conf @@ -118,6 +118,10 @@                  <allow send_destination="org.freedesktop.machine1"                         send_interface="org.freedesktop.machine1.Manager" +                       send_member="CleanPool"/> + +                <allow send_destination="org.freedesktop.machine1" +                       send_interface="org.freedesktop.machine1.Manager"                         send_member="MapFromMachineUser"/>                  <allow send_destination="org.freedesktop.machine1" diff --git a/src/shared/condition.c b/src/shared/condition.c index 3a45ed265c..6bb42c0692 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -182,10 +182,11 @@ static int condition_test_architecture(Condition *c) {          if (streq(c->parameter, "native"))                  b = native_architecture(); -        else +        else {                  b = architecture_from_string(c->parameter); -        if (b < 0) -                return b; +                if (b < 0) /* unknown architecture? Then it's definitely not ours */ +                        return false; +        }          return a == b;  } diff --git a/src/test/test-condition.c b/src/test/test-condition.c index 8903d10db7..987862f1c6 100644 --- a/src/test/test-condition.c +++ b/src/test/test-condition.c @@ -159,15 +159,15 @@ static void test_condition_test_architecture(void) {          assert_se(sa);          condition = condition_new(CONDITION_ARCHITECTURE, sa, false, false); -        assert_se(condition_test(condition)); +        assert_se(condition_test(condition) > 0);          condition_free(condition);          condition = condition_new(CONDITION_ARCHITECTURE, "garbage value", false, false); -        assert_se(condition_test(condition) < 0); +        assert_se(condition_test(condition) == 0);          condition_free(condition);          condition = condition_new(CONDITION_ARCHITECTURE, sa, false, true); -        assert_se(!condition_test(condition)); +        assert_se(condition_test(condition) == 0);          condition_free(condition);  } diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index 6db2c2b6f1..e0c040f39b 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -82,10 +82,56 @@ static void test_get_files_in_directory(void) {          assert_se(get_files_in_directory(".", NULL) >= 0);  } +static void test_var_tmp(void) { +        char *tmp_dir = NULL; +        char *tmpdir_backup = NULL; +        const char *default_var_tmp = NULL; +        const char *var_name; +        bool do_overwrite = true; + +        default_var_tmp = "/var/tmp"; +        var_name = "TMPDIR"; + +        if (getenv(var_name) != NULL) { +                tmpdir_backup = strdup(getenv(var_name)); +                assert_se(tmpdir_backup != NULL); +        } + +        unsetenv(var_name); + +        var_tmp(&tmp_dir); +        assert_se(!strcmp(tmp_dir, default_var_tmp)); + +        free(tmp_dir); + +        setenv(var_name, "/tmp", do_overwrite); +        assert_se(!strcmp(getenv(var_name), "/tmp")); + +        var_tmp(&tmp_dir); +        assert_se(!strcmp(tmp_dir, "/tmp")); + +        free(tmp_dir); + +        setenv(var_name, "/88_does_not_exist_88", do_overwrite); +        assert_se(!strcmp(getenv(var_name), "/88_does_not_exist_88")); + +        var_tmp(&tmp_dir); +        assert_se(!strcmp(tmp_dir, default_var_tmp)); + +        free(tmp_dir); + +        if (tmpdir_backup != NULL)  { +                setenv(var_name, tmpdir_backup, do_overwrite); +                assert_se(!strcmp(getenv(var_name), tmpdir_backup)); +                free(tmpdir_backup); +        } +} +  int main(int argc, char *argv[]) {          test_unlink_noerrno();          test_readlink_and_make_absolute();          test_get_files_in_directory(); +        test_var_tmp();          return 0;  } diff --git a/test/networkd-test.py b/test/networkd-test.py index 8f5d43bc88..bfa1bf3580 100755 --- a/test/networkd-test.py +++ b/test/networkd-test.py @@ -42,6 +42,8 @@ networkd_active = subprocess.call(['systemctl', 'is-active', '--quiet',                                     'systemd-networkd']) == 0  have_dnsmasq = shutil.which('dnsmasq') +RESOLV_CONF = '/run/systemd/resolve/resolv.conf' +  @unittest.skipIf(networkd_active,                   'networkd is already active') @@ -104,6 +106,7 @@ class ClientTestBase:      def do_test(self, coldplug=True, ipv6=False, extra_opts='',                  online_timeout=10, dhcp_mode='yes'): +        subprocess.check_call(['systemctl', 'start', 'systemd-resolved'])          with open(self.config, 'w') as f:              f.write('''[Match]  Name=%s @@ -179,20 +182,14 @@ DHCP=%s              self.print_server_log()              raise -        # verify resolv.conf if it gets dynamically managed -        if os.path.islink('/etc/resolv.conf'): -            for timeout in range(50): -                with open('/etc/resolv.conf') as f: -                    contents = f.read() -                if 'nameserver 192.168.5.1\n' in contents: -                    break -                # resolv.conf can have at most three nameservers; if we already -                # have three different ones, that's also okay -                if contents.count('nameserver ') >= 3: -                    break -                time.sleep(0.1) -            else: -                self.fail('nameserver 192.168.5.1 not found in /etc/resolv.conf') +        for timeout in range(50): +            with open(RESOLV_CONF) as f: +                contents = f.read() +            if 'nameserver 192.168.5.1\n' in contents: +                break +            time.sleep(0.1) +        else: +            self.fail('nameserver 192.168.5.1 not found in ' + RESOLV_CONF)          if not coldplug:              # check post-down.d hook @@ -246,17 +243,12 @@ Domains= ~company''')          self.do_test(coldplug=True, ipv6=False,                       extra_opts='IPv6AcceptRouterAdvertisements=False') -        if os.path.islink('/etc/resolv.conf'): -            with open('/etc/resolv.conf') as f: -                contents = f.read() - +        with open(RESOLV_CONF) as f: +            contents = f.read()              # ~company is not a search domain, only a routing domain              self.assertNotRegex(contents, 'search.*company') - -            # our global server should appear, unless we already have three -            # (different) servers -            if contents.count('nameserver ') < 3: -                self.assertIn('nameserver 192.168.5.1\n', contents) +            # our global server should appear +            self.assertIn('nameserver 192.168.5.1\n', contents)  @unittest.skipUnless(have_dnsmasq, 'dnsmasq not installed') @@ -423,16 +415,15 @@ Domains= one two three four five six seven eight nine ten''')          subprocess.check_call(['systemctl', 'start', 'systemd-networkd']) -        if os.path.islink('/etc/resolv.conf'): -            for timeout in range(50): -                with open('/etc/resolv.conf') as f: -                    contents = f.read() -                if 'search one\n' in contents: -                    break -                time.sleep(0.1) -            self.assertIn('search one two three four five six\n' -                          '# Too many search domains configured, remaining ones ignored.\n', -                          contents) +        for timeout in range(50): +            with open(RESOLV_CONF) as f: +                contents = f.read() +            if ' one' in contents: +                break +            time.sleep(0.1) +        self.assertRegex(contents, 'search .*one two three four') +        self.assertNotIn('seven\n', contents) +        self.assertIn('# Too many search domains configured, remaining ones ignored.\n', contents)      def test_search_domains_too_long(self): @@ -461,16 +452,14 @@ Domains=''')          subprocess.check_call(['systemctl', 'start', 'systemd-networkd']) -        if os.path.islink('/etc/resolv.conf'): -            for timeout in range(50): -                with open('/etc/resolv.conf') as f: -                    contents = f.read() -                if 'search one\n' in contents: -                    break -                time.sleep(0.1) -            self.assertIn('search %(p)s0 %(p)s1 %(p)s2 %(p)s3\n' -                          '# Total length of all search domains is too long, remaining ones ignored.' % {'p': name_prefix}, -                          contents) +        for timeout in range(50): +            with open(RESOLV_CONF) as f: +                contents = f.read() +            if ' one' in contents: +                break +            time.sleep(0.1) +        self.assertRegex(contents, 'search .*%(p)s0 %(p)s1 %(p)s2' % {'p': name_prefix}) +        self.assertIn('# Total length of all search domains is too long, remaining ones ignored.', contents)  if __name__ == '__main__': | 
