summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/boot/bootctl.c928
-rw-r--r--src/bus-proxyd/driver.c11
-rw-r--r--src/console/consoled-manager.c11
-rw-r--r--src/console/consoled.c5
-rw-r--r--src/core/automount.c2
-rw-r--r--src/core/busname.c5
-rw-r--r--src/core/dbus-kill.c4
-rw-r--r--src/core/dbus-unit.c6
-rw-r--r--src/core/device.c54
-rw-r--r--src/core/execute.c12
-rw-r--r--src/core/hostname-setup.c24
-rw-r--r--src/core/job.c85
-rw-r--r--src/core/job.h2
-rw-r--r--src/core/kill.c4
-rw-r--r--src/core/load-fragment.c5
-rw-r--r--src/core/machine-id-setup.c2
-rw-r--r--src/core/main.c9
-rw-r--r--src/core/manager.c24
-rw-r--r--src/core/mount-setup.c4
-rw-r--r--src/core/mount.c39
-rw-r--r--src/core/namespace.c20
-rw-r--r--src/core/service.c1
-rw-r--r--src/core/socket.c3
-rw-r--r--src/core/swap.c43
-rw-r--r--src/core/transaction.c98
-rw-r--r--src/core/unit.c92
-rw-r--r--src/core/unit.h11
-rw-r--r--src/efi-boot-generator/efi-boot-generator.c2
-rw-r--r--src/fsck/fsck.c1
-rw-r--r--src/fstab-generator/fstab-generator.c28
-rw-r--r--src/gpt-auto-generator/gpt-auto-generator.c2
-rw-r--r--src/hostname/hostnamed.c2
-rw-r--r--src/import/export.c1
-rw-r--r--src/import/import-common.c1
-rw-r--r--src/import/import.c1
-rw-r--r--src/import/importd.c1
-rw-r--r--src/import/pull-common.c1
-rw-r--r--src/import/pull-dkr.c542
-rw-r--r--src/import/pull-dkr.h3
-rw-r--r--src/import/pull.c29
-rw-r--r--src/journal-remote/journal-remote.c1
-rw-r--r--src/journal-remote/journal-upload.c3
-rw-r--r--src/journal/coredumpctl.c1
-rw-r--r--src/journal/journald-audit.c11
-rw-r--r--src/journal/journald-server.c9
-rw-r--r--src/kernel-install/90-loaderentry.install6
-rw-r--r--src/libsystemd-network/dhcp-identifier.c9
-rw-r--r--src/libsystemd-network/dhcp-identifier.h3
-rw-r--r--src/libsystemd-network/sd-dhcp-lease.c2
-rw-r--r--src/libsystemd-terminal/evcat.c11
-rw-r--r--src/libsystemd-terminal/grdev-drm.c10
-rw-r--r--src/libsystemd-terminal/grdev-internal.h6
-rw-r--r--src/libsystemd-terminal/grdev.c8
-rw-r--r--src/libsystemd-terminal/grdev.h4
-rw-r--r--src/libsystemd-terminal/idev-evdev.c8
-rw-r--r--src/libsystemd-terminal/idev-internal.h6
-rw-r--r--src/libsystemd-terminal/idev-keyboard.c10
-rw-r--r--src/libsystemd-terminal/idev.c8
-rw-r--r--src/libsystemd-terminal/idev.h4
-rw-r--r--src/libsystemd-terminal/modeset.c14
-rw-r--r--src/libsystemd-terminal/subterm.c7
-rw-r--r--src/libsystemd-terminal/sysview-internal.h6
-rw-r--r--src/libsystemd-terminal/sysview.c12
-rw-r--r--src/libsystemd-terminal/sysview.h4
-rw-r--r--src/libsystemd/libsystemd.sym.m453
-rw-r--r--src/libsystemd/sd-bus/bus-control.c2
-rw-r--r--src/libsystemd/sd-bus/bus-creds.c6
-rw-r--r--src/libsystemd/sd-bus/bus-socket.c23
-rw-r--r--src/libsystemd/sd-bus/bus-util.c1
-rw-r--r--src/libsystemd/sd-bus/sd-bus.c12
-rw-r--r--src/libsystemd/sd-device/device-enumerator.c12
-rw-r--r--src/libsystemd/sd-device/device-private.c29
-rw-r--r--src/libsystemd/sd-device/sd-device.c6
-rw-r--r--src/libsystemd/sd-event/sd-event.c1
-rw-r--r--src/libsystemd/sd-event/test-event.c1
-rw-r--r--src/login/loginctl.c1
-rw-r--r--src/login/logind-dbus.c5
-rw-r--r--src/login/logind-user.c2
-rw-r--r--src/login/logind.c5
-rw-r--r--src/machine/machinectl.c1
-rw-r--r--src/machine/machined.c3
-rw-r--r--src/network/networkctl.c2
-rw-r--r--src/network/networkd-link.c31
-rw-r--r--src/network/networkd-wait-online.c5
-rw-r--r--src/network/networkd.c4
-rw-r--r--src/nspawn/nspawn.c1559
-rw-r--r--src/remount-fs/remount-fs.c1
-rw-r--r--src/resolve/resolved.c1
-rw-r--r--src/run/run.c1
-rw-r--r--src/shared/architecture.c2
-rw-r--r--src/shared/architecture.h2
-rw-r--r--src/shared/ask-password-api.c2
-rw-r--r--src/shared/base-filesystem.c16
-rw-r--r--src/shared/base-filesystem.h4
-rw-r--r--src/shared/capability.h2
-rw-r--r--src/shared/cgroup-util.c2
-rw-r--r--src/shared/condition.c2
-rw-r--r--src/shared/conf-parser.c2
-rw-r--r--src/shared/dev-setup.c29
-rw-r--r--src/shared/dev-setup.h4
-rw-r--r--src/shared/fdset.c2
-rw-r--r--src/shared/generator.c2
-rw-r--r--src/shared/hostname-util.c33
-rw-r--r--src/shared/hostname-util.h2
-rw-r--r--src/shared/import-util.c21
-rw-r--r--src/shared/import-util.h2
-rw-r--r--src/shared/json.c441
-rw-r--r--src/shared/json.h39
-rw-r--r--src/shared/log.c1
-rw-r--r--src/shared/machine-pool.c3
-rw-r--r--src/shared/missing.h11
-rw-r--r--src/shared/path-util.c34
-rw-r--r--src/shared/path-util.h4
-rw-r--r--src/shared/process-util.c3
-rw-r--r--src/shared/pty.c3
-rw-r--r--src/shared/random-util.c2
-rw-r--r--src/shared/rm-rf.c2
-rw-r--r--src/shared/signal-util.c228
-rw-r--r--src/shared/signal-util.h41
-rw-r--r--src/shared/switch-root.c2
-rw-r--r--src/shared/unit-name.c2
-rw-r--r--src/shared/unit-name.h6
-rw-r--r--src/shared/util.c278
-rw-r--r--src/shared/util.h30
-rw-r--r--src/systemctl/systemctl.c23
-rwxr-xr-xsrc/systemctl/systemd-sysv-install.SKELETON47
-rw-r--r--src/systemd/sd-bus.h6
-rw-r--r--src/test/test-conf-parser.c234
-rw-r--r--src/test/test-fdset.c53
-rw-r--r--src/test/test-hashmap-plain.c2
-rw-r--r--src/test/test-json.c96
-rw-r--r--src/test/test-path-util.c76
-rw-r--r--src/test/test-pty.c1
-rw-r--r--src/test/test-udev.c7
-rw-r--r--src/test/test-unit-file.c19
-rw-r--r--src/test/test-util.c55
-rw-r--r--src/timesync/timesyncd.c1
-rw-r--r--src/tty-ask-password-agent/tty-ask-password-agent.c1
-rw-r--r--src/udev/net/link-config.c11
-rw-r--r--src/udev/net/link-config.h4
-rw-r--r--src/udev/udev-builtin-net_id.c9
-rw-r--r--src/udev/udev-event.c193
-rw-r--r--src/udev/udev.h2
-rw-r--r--src/udev/udevadm-test.c8
-rw-r--r--src/udev/udevd.c561
145 files changed, 4216 insertions, 2500 deletions
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index 29dcec7166..1e65597acf 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -41,70 +41,53 @@
#include "build.h"
#include "util.h"
#include "rm-rf.h"
+#include "blkid-util.h"
static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t *psize, sd_id128_t *uuid) {
struct statfs sfs;
struct stat st, st2;
- char *t;
- blkid_probe b = NULL;
+ _cleanup_free_ char *t = NULL;
+ _cleanup_blkid_free_probe_ blkid_probe b = NULL;
int r;
- const char *v;
+ const char *v, *t2;
- if (statfs(p, &sfs) < 0) {
- fprintf(stderr, "Failed to check file system type of %s: %m\n", p);
- return -errno;
- }
+ if (statfs(p, &sfs) < 0)
+ return log_error_errno(errno, "Failed to check file system type of \"%s\": %m", p);
if (sfs.f_type != 0x4d44) {
- fprintf(stderr, "File system %s is not a FAT EFI System Partition (ESP) file system.\n", p);
+ log_error("File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p);
return -ENODEV;
}
- if (stat(p, &st) < 0) {
- fprintf(stderr, "Failed to determine block device node of %s: %m\n", p);
- return -errno;
- }
+ if (stat(p, &st) < 0)
+ return log_error_errno(errno, "Failed to determine block device node of \"%s\": %m", p);
if (major(st.st_dev) == 0) {
- fprintf(stderr, "Block device node of %p is invalid.\n", p);
+ log_error("Block device node of %p is invalid.", p);
return -ENODEV;
}
- r = asprintf(&t, "%s/..", p);
- if (r < 0) {
- fprintf(stderr, "Out of memory.\n");
- return -ENOMEM;
- }
-
- r = stat(t, &st2);
- free(t);
- if (r < 0) {
- fprintf(stderr, "Failed to determine block device node of parent of %s: %m\n", p);
- return -errno;
- }
+ t2 = strjoina(p, "/..");
+ r = stat(t2, &st2);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to determine block device node of parent of \"%s\": %m", p);
if (st.st_dev == st2.st_dev) {
- fprintf(stderr, "Directory %s is not the root of the EFI System Partition (ESP) file system.\n", p);
+ log_error("Directory \"%s\" is not the root of the EFI System Partition (ESP) file system.", p);
return -ENODEV;
}
r = asprintf(&t, "/dev/block/%u:%u", major(st.st_dev), minor(st.st_dev));
- if (r < 0) {
- fprintf(stderr, "Out of memory.\n");
- return -ENOMEM;
- }
+ if (r < 0)
+ return log_oom();
errno = 0;
b = blkid_new_probe_from_filename(t);
- free(t);
if (!b) {
- if (errno != 0) {
- fprintf(stderr, "Failed to open file system %s: %m\n", p);
- return -errno;
- }
+ if (errno == 0)
+ return log_oom();
- fprintf(stderr, "Out of memory.\n");
- return -ENOMEM;
+ return log_error_errno(errno, "Failed to open file system \"%s\": %m", p);
}
blkid_probe_enable_superblocks(b, 1);
@@ -115,82 +98,70 @@ static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t
errno = 0;
r = blkid_do_safeprobe(b);
if (r == -2) {
- fprintf(stderr, "File system %s is ambigious.\n", p);
- r = -ENODEV;
- goto fail;
+ log_error("File system \"%s\" is ambigious.", p);
+ return -ENODEV;
} else if (r == 1) {
- fprintf(stderr, "File system %s does not contain a label.\n", p);
- r = -ENODEV;
- goto fail;
+ log_error("File system \"%s\" does not contain a label.", p);
+ return -ENODEV;
} else if (r != 0) {
r = errno ? -errno : -EIO;
- fprintf(stderr, "Failed to probe file system %s: %s\n", p, strerror(-r));
- goto fail;
+ return log_error_errno(r, "Failed to probe file system \"%s\": %m", p);
}
errno = 0;
r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
if (r != 0) {
r = errno ? -errno : -EIO;
- fprintf(stderr, "Failed to probe file system type %s: %s\n", p, strerror(-r));
- goto fail;
+ return log_error_errno(r, "Failed to probe file system type \"%s\": %m", p);
}
- if (strcmp(v, "vfat") != 0) {
- fprintf(stderr, "File system %s is not a FAT EFI System Partition (ESP) file system after all.\n", p);
- r = -ENODEV;
- goto fail;
+ if (!streq(v, "vfat")) {
+ log_error("File system \"%s\" is not FAT.", p);
+ return -ENODEV;
}
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
if (r != 0) {
r = errno ? -errno : -EIO;
- fprintf(stderr, "Failed to probe partition scheme %s: %s\n", p, strerror(-r));
- goto fail;
+ return log_error_errno(r, "Failed to probe partition scheme \"%s\": %m", p);
}
- if (strcmp(v, "gpt") != 0) {
- fprintf(stderr, "File system %s is not on a GPT partition table.\n", p);
- r = -ENODEV;
- goto fail;
+ if (!streq(v, "gpt")) {
+ log_error("File system \"%s\" is not on a GPT partition table.", p);
+ return -ENODEV;
}
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
if (r != 0) {
r = errno ? -errno : -EIO;
- fprintf(stderr, "Failed to probe partition type UUID %s: %s\n", p, strerror(-r));
- goto fail;
+ return log_error_errno(r, "Failed to probe partition type UUID \"%s\": %m", p);
}
- if (strcmp(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b") != 0) {
- r = -ENODEV;
- fprintf(stderr, "File system %s is not an EFI System Partition (ESP).\n", p);
- goto fail;
+ if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b")) {
+ log_error("File system \"%s\" has wrong type for an EFI System Partition (ESP).", p);
+ return -ENODEV;
}
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
if (r != 0) {
r = errno ? -errno : -EIO;
- fprintf(stderr, "Failed to probe partition entry UUID %s: %s\n", p, strerror(-r));
- goto fail;
+ return log_error_errno(r, "Failed to probe partition entry UUID \"%s\": %m", p);
}
r = sd_id128_from_string(v, uuid);
if (r < 0) {
- fprintf(stderr, "Partition %s has invalid UUID: %s\n", p, v);
- r = -EIO;
- goto fail;
+ log_error("Partition \"%s\" has invalid UUID \"%s\".", p, v);
+ return -EIO;
}
errno = 0;
r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL);
if (r != 0) {
r = errno ? -errno : -EIO;
- fprintf(stderr, "Failed to probe partition number %s: %s\n", p, strerror(-r));
- goto fail;
+ return log_error_errno(r, "Failed to probe partition number \"%s\": m", p);
}
*part = strtoul(v, NULL, 10);
@@ -198,8 +169,7 @@ static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t
r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL);
if (r != 0) {
r = errno ? -errno : -EIO;
- fprintf(stderr, "Failed to probe partition offset %s: %s\n", p, strerror(-r));
- goto fail;
+ return log_error_errno(r, "Failed to probe partition offset \"%s\": %m", p);
}
*pstart = strtoul(v, NULL, 10);
@@ -207,37 +177,31 @@ static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t
r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL);
if (r != 0) {
r = errno ? -errno : -EIO;
- fprintf(stderr, "Failed to probe partition size %s: %s\n", p, strerror(-r));
- goto fail;
+ return log_error_errno(r, "Failed to probe partition size \"%s\": %m", p);
}
*psize = strtoul(v, NULL, 10);
- blkid_free_probe(b);
return 0;
-fail:
- if (b)
- blkid_free_probe(b);
- return r;
}
/* search for "#### LoaderInfo: systemd-boot 218 ####" string inside the binary */
-static int get_file_version(FILE *f, char **v) {
+static int get_file_version(int fd, char **v) {
struct stat st;
char *buf;
const char *s, *e;
char *x = NULL;
int r = 0;
- assert(f);
+ assert(fd >= 0);
assert(v);
- if (fstat(fileno(f), &st) < 0)
+ if (fstat(fd, &st) < 0)
return -errno;
if (st.st_size < 27)
return 0;
- buf = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fileno(f), 0);
+ buf = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (buf == MAP_FAILED)
return -errno;
@@ -248,15 +212,14 @@ static int get_file_version(FILE *f, char **v) {
e = memmem(s, st.st_size - (s - buf), " ####", 5);
if (!e || e - s < 3) {
- fprintf(stderr, "Malformed version string.\n");
+ log_error("Malformed version string.");
r = -EINVAL;
goto finish;
}
x = strndup(s, e - s);
if (!x) {
- fprintf(stderr, "Out of memory.\n");
- r = -ENOMEM;
+ r = log_oom();
goto finish;
}
r = 1;
@@ -268,83 +231,48 @@ finish:
}
static int enumerate_binaries(const char *esp_path, const char *path, const char *prefix) {
+ char *p;
+ _cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
- char *p = NULL, *q = NULL;
- DIR *d = NULL;
int r = 0, c = 0;
- if (asprintf(&p, "%s/%s", esp_path, path) < 0) {
- fprintf(stderr, "Out of memory.\n");
- r = -ENOMEM;
- goto finish;
- }
-
+ p = strjoina(esp_path, "/", path);
d = opendir(p);
if (!d) {
- if (errno == ENOENT) {
- r = 0;
- goto finish;
- }
+ if (errno == ENOENT)
+ return 0;
- fprintf(stderr, "Failed to read %s: %m\n", p);
- r = -errno;
- goto finish;
+ return log_error_errno(errno, "Failed to read \"%s\": %m", p);
}
while ((de = readdir(d))) {
- char *v;
- size_t n;
- FILE *f;
+ _cleanup_close_ int fd = -1;
+ _cleanup_free_ char *v = NULL;
if (de->d_name[0] == '.')
continue;
- n = strlen(de->d_name);
- if (n < 4 || strcasecmp(de->d_name + n - 4, ".efi") != 0)
+ if (!endswith_no_case(de->d_name, ".efi"))
continue;
- if (prefix && strncasecmp(de->d_name, prefix, strlen(prefix)) != 0)
+ if (prefix && !startswith_no_case(de->d_name, prefix))
continue;
- free(q);
- q = NULL;
- if (asprintf(&q, "%s/%s/%s", esp_path, path, de->d_name) < 0) {
- fprintf(stderr, "Out of memory.\n");
- r = -ENOMEM;
- goto finish;
- }
-
- f = fopen(q, "re");
- if (!f) {
- fprintf(stderr, "Failed to open %s for reading: %m\n", q);
- r = -errno;
- goto finish;
- }
-
- r = get_file_version(f, &v);
- fclose(f);
+ fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to open \"%s/%s\" for reading: %m", p, de->d_name);
+ r = get_file_version(fd, &v);
if (r < 0)
- goto finish;
-
+ return r;
if (r > 0)
printf(" File: └─/%s/%s (%s)\n", path, de->d_name, v);
else
printf(" File: └─/%s/%s\n", path, de->d_name);
-
c++;
- free(v);
}
- r = c;
-
-finish:
- if (d)
- closedir(d);
-
- free(p);
- free(q);
- return r;
+ return c;
}
static int status_binaries(const char *esp_path, sd_id128_t partition) {
@@ -356,17 +284,16 @@ static int status_binaries(const char *esp_path, sd_id128_t partition) {
r = enumerate_binaries(esp_path, "EFI/systemd", NULL);
if (r == 0)
- fprintf(stderr, "systemd-boot not installed in ESP.\n");
+ log_error("systemd-boot not installed in ESP.");
else if (r < 0)
return r;
r = enumerate_binaries(esp_path, "EFI/Boot", "boot");
if (r == 0)
- fprintf(stderr, "No default/fallback boot loader installed in ESP.\n");
+ log_error("No default/fallback boot loader installed in ESP.");
else if (r < 0)
return r;
- printf("\n");
return 0;
}
@@ -399,62 +326,46 @@ static int print_efi_option(uint16_t id, bool in_order) {
static int status_variables(void) {
int n_options, n_order;
- uint16_t *options = NULL, *order = NULL;
- int r, i;
+ _cleanup_free_ uint16_t *options = NULL, *order = NULL;
+ int i;
if (!is_efi_boot()) {
- fprintf(stderr, "Not booted with EFI, not showing EFI variables.\n");
+ log_notice("Not booted with EFI, not showing EFI variables.");
return 0;
}
n_options = efi_get_boot_options(&options);
- if (n_options < 0) {
- if (n_options == -ENOENT)
- fprintf(stderr, "Failed to access EFI variables, "
- "efivarfs needs to be available at /sys/firmware/efi/efivars/.\n");
- else
- fprintf(stderr, "Failed to read EFI boot entries: %s\n", strerror(-n_options));
- r = n_options;
- goto finish;
- }
+ if (n_options == -ENOENT)
+ return log_error_errno(ENOENT, "Failed to access EFI variables, efivarfs"
+ " needs to be available at /sys/firmware/efi/efivars/.");
+ else if (n_options < 0)
+ return log_error_errno(n_options, "Failed to read EFI boot entries: %m");
- printf("Boot Loader Entries in EFI Variables:\n");
n_order = efi_get_boot_order(&order);
- if (n_order == -ENOENT) {
+ if (n_order == -ENOENT)
n_order = 0;
- } else if (n_order < 0) {
- fprintf(stderr, "Failed to read EFI boot order.\n");
- r = n_order;
- goto finish;
- }
+ else if (n_order < 0)
+ return log_error_errno(n_order, "Failed to read EFI boot order.");
/* print entries in BootOrder first */
+ printf("Boot Loader Entries in EFI Variables:\n");
for (i = 0; i < n_order; i++)
print_efi_option(order[i], true);
/* print remaining entries */
for (i = 0; i < n_options; i++) {
int j;
- bool found = false;
for (j = 0; j < n_order; j++)
- if (options[i] == order[j]) {
- found = true;
- break;
- }
-
- if (found)
- continue;
+ if (options[i] == order[j])
+ goto next;
print_efi_option(options[i], false);
+ next:
+ continue;
}
- r = 0;
-finish:
- free(options);
- free(order);
-
- return r;
+ return 0;
}
static int compare_product(const char *a, const char *b) {
@@ -483,64 +394,50 @@ static int compare_version(const char *a, const char *b) {
return strverscmp(a, b);
}
-static int version_check(FILE *f, const char *from, const char *to) {
- FILE *g = NULL;
- char *a = NULL, *b = NULL;
+static int version_check(int fd, const char *from, const char *to) {
+ _cleanup_free_ char *a = NULL, *b = NULL;
+ _cleanup_close_ int fd2 = -1;
int r;
- assert(f);
+ assert(fd >= 0);
assert(from);
assert(to);
- r = get_file_version(f, &a);
+ r = get_file_version(fd, &a);
if (r < 0)
- goto finish;
+ return r;
if (r == 0) {
- r = -EINVAL;
- fprintf(stderr, "Source file %s does not carry version information!\n", from);
- goto finish;
+ log_error("Source file \"%s\" does not carry version information!", from);
+ return -EINVAL;
}
- g = fopen(to, "re");
- if (!g) {
- if (errno == ENOENT) {
- r = 0;
- goto finish;
- }
+ fd2 = open(to, O_RDONLY|O_CLOEXEC);
+ if (fd2 < 0) {
+ if (errno == ENOENT)
+ return 0;
- r = -errno;
- fprintf(stderr, "Failed to open %s for reading: %m\n", to);
- goto finish;
+ return log_error_errno(errno, "Failed to open \"%s\" for reading: %m", to);
}
- r = get_file_version(g, &b);
+ r = get_file_version(fd2, &b);
if (r < 0)
- goto finish;
+ return r;
if (r == 0 || compare_product(a, b) != 0) {
- r = -EEXIST;
- fprintf(stderr, "Skipping %s, since it's owned by another boot loader.\n", to);
- goto finish;
+ log_notice("Skipping \"%s\", since it's owned by another boot loader.", to);
+ return -EEXIST;
}
if (compare_version(a, b) < 0) {
- r = -EEXIST;
- fprintf(stderr, "Skipping %s, since it's a newer boot loader version already.\n", to);
- goto finish;
+ log_warning("Skipping \"%s\", since a newer boot loader version exists already.", to);
+ return -ESTALE;
}
- r = 0;
-
-finish:
- free(a);
- free(b);
- if (g)
- fclose(g);
- return r;
+ return 0;
}
static int copy_file(const char *from, const char *to, bool force) {
- FILE *f = NULL, *g = NULL;
- char *p = NULL;
+ _cleanup_fclose_ FILE *f = NULL, *g = NULL;
+ char *p;
int r;
struct timespec t[2];
struct stat st;
@@ -549,35 +446,24 @@ static int copy_file(const char *from, const char *to, bool force) {
assert(to);
f = fopen(from, "re");
- if (!f) {
- fprintf(stderr, "Failed to open %s for reading: %m\n", from);
- return -errno;
- }
+ if (!f)
+ return log_error_errno(errno, "Failed to open \"%s\" for reading: %m", from);
if (!force) {
/* If this is an update, then let's compare versions first */
- r = version_check(f, from, to);
+ r = version_check(fileno(f), from, to);
if (r < 0)
- goto finish;
- }
-
- if (asprintf(&p, "%s~", to) < 0) {
- fprintf(stderr, "Out of memory.\n");
- r = -ENOMEM;
- goto finish;
+ return r;
}
+ p = strjoina(to, "~");
g = fopen(p, "wxe");
if (!g) {
/* Directory doesn't exist yet? Then let's skip this... */
- if (!force && errno == ENOENT) {
- r = 0;
- goto finish;
- }
+ if (!force && errno == ENOENT)
+ return 0;
- fprintf(stderr, "Failed to open %s for writing: %m\n", to);
- r = -errno;
- goto finish;
+ return log_error_errno(errno, "Failed to open \"%s\" for writing: %m", to);
}
rewind(f);
@@ -587,33 +473,30 @@ static int copy_file(const char *from, const char *to, bool force) {
k = fread(buf, 1, sizeof(buf), f);
if (ferror(f)) {
- fprintf(stderr, "Failed to read %s: %m\n", from);
- r = -errno;
- goto finish;
+ r = log_error_errno(EIO, "Failed to read \"%s\": %m", from);
+ goto error;
}
+
if (k == 0)
break;
fwrite(buf, 1, k, g);
if (ferror(g)) {
- fprintf(stderr, "Failed to write %s: %m\n", to);
- r = -errno;
- goto finish;
+ r = log_error_errno(EIO, "Failed to write \"%s\": %m", to);
+ goto error;
}
} while (!feof(f));
fflush(g);
if (ferror(g)) {
- fprintf(stderr, "Failed to write %s: %m\n", to);
- r = -errno;
- goto finish;
+ r = log_error_errno(EIO, "Failed to write \"%s\": %m", to);
+ goto error;
}
r = fstat(fileno(f), &st);
if (r < 0) {
- fprintf(stderr, "Failed to get file timestamps of %s: %m", from);
- r = -errno;
- goto finish;
+ r = log_error_errno(errno, "Failed to get file timestamps of \"%s\": %m", from);
+ goto error;
}
t[0] = st.st_atim;
@@ -621,32 +504,20 @@ static int copy_file(const char *from, const char *to, bool force) {
r = futimens(fileno(g), t);
if (r < 0) {
- fprintf(stderr, "Failed to change file timestamps for %s: %m", p);
- r = -errno;
- goto finish;
+ r = log_error_errno(errno, "Failed to set file timestamps on \"%s\": %m", p);
+ goto error;
}
if (rename(p, to) < 0) {
- fprintf(stderr, "Failed to rename %s to %s: %m\n", p, to);
- r = -errno;
- goto finish;
+ r = log_error_errno(errno, "Failed to rename \"%s\" to \"%s\": %m", p, to);
+ goto error;
}
- fprintf(stderr, "Copied %s to %s.\n", from, to);
-
- free(p);
- p = NULL;
- r = 0;
+ log_info("Copied \"%s\" to \"%s\".", from, to);
+ return 0;
-finish:
- if (f)
- fclose(f);
- if (g)
- fclose(g);
- if (p) {
- unlink(p);
- free(p);
- }
+error:
+ unlink(p);
return r;
}
@@ -662,81 +533,56 @@ static char* strupper(char *s) {
static int mkdir_one(const char *prefix, const char *suffix) {
char *p;
- if (asprintf(&p, "%s/%s", prefix, suffix) < 0) {
- fprintf(stderr, "Out of memory.\n");
- return -ENOMEM;
- }
-
+ p = strjoina(prefix, "/", suffix);
if (mkdir(p, 0700) < 0) {
- if (errno != EEXIST) {
- fprintf(stderr, "Failed to create %s: %m\n", p);
- free(p);
- return -errno;
- }
+ if (errno != EEXIST)
+ return log_error_errno(errno, "Failed to create \"%s\": %m", p);
} else
- fprintf(stderr, "Created %s.\n", p);
+ log_info("Created \"%s\".", p);
- free(p);
return 0;
}
+static const char *efi_subdirs[] = {
+ "EFI",
+ "EFI/systemd",
+ "EFI/Boot",
+ "loader",
+ "loader/entries"
+};
+
static int create_dirs(const char *esp_path) {
int r;
+ unsigned i;
- r = mkdir_one(esp_path, "EFI");
- if (r < 0)
- return r;
-
- r = mkdir_one(esp_path, "EFI/systemd");
- if (r < 0)
- return r;
-
- r = mkdir_one(esp_path, "EFI/Boot");
- if (r < 0)
- return r;
-
- r = mkdir_one(esp_path, "loader");
- if (r < 0)
- return r;
-
- r = mkdir_one(esp_path, "loader/entries");
- if (r < 0)
- return r;
+ for (i = 0; i < ELEMENTSOF(efi_subdirs); i++) {
+ r = mkdir_one(esp_path, efi_subdirs[i]);
+ if (r < 0)
+ return r;
+ }
return 0;
}
static int copy_one_file(const char *esp_path, const char *name, bool force) {
- _cleanup_free_ char *p = NULL;
- _cleanup_free_ char *q = NULL;
- _cleanup_free_ char *v = NULL;
+ char *p, *q;
int r;
- if (asprintf(&p, BOOTLIBDIR "/%s", name) < 0) {
- fprintf(stderr, "Out of memory.\n");
- return -ENOMEM;
- }
-
- if (asprintf(&q, "%s/EFI/systemd/%s", esp_path, name) < 0) {
- fprintf(stderr, "Out of memory.\n");
- return -ENOMEM;
- }
-
+ p = strjoina(BOOTLIBDIR "/", name);
+ q = strjoina(esp_path, "/EFI/systemd/", name);
r = copy_file(p, q, force);
if (startswith(name, "systemd-boot")) {
int k;
+ char *v;
/* Create the EFI default boot loader name (specified for removable devices) */
- if (asprintf(&v, "%s/EFI/Boot/BOOT%s", esp_path, name + strlen("systemd-boot")) < 0) {
- fprintf(stderr, "Out of memory.\n");
- return -ENOMEM;
- }
+ v = strjoina(esp_path, "/EFI/Boot/BOOT", name + strlen("systemd-boot"));
strupper(strrchr(v, '/') + 1);
k = copy_file(p, v, force);
if (k < 0 && r == 0)
- return k;
+ r = k;
}
return r;
@@ -744,7 +590,7 @@ static int copy_one_file(const char *esp_path, const char *name, bool force) {
static int install_binaries(const char *esp_path, bool force) {
struct dirent *de;
- DIR *d;
+ _cleanup_closedir_ DIR *d = NULL;
int r = 0;
if (force) {
@@ -759,20 +605,16 @@ static int install_binaries(const char *esp_path, bool force) {
}
d = opendir(BOOTLIBDIR);
- if (!d) {
- fprintf(stderr, "Failed to open "BOOTLIBDIR": %m\n");
- return -errno;
- }
+ if (!d)
+ return log_error_errno(errno, "Failed to open \""BOOTLIBDIR"\": %m");
while ((de = readdir(d))) {
- size_t n;
int k;
if (de->d_name[0] == '.')
continue;
- n = strlen(de->d_name);
- if (n < 4 || strcmp(de->d_name + n - 4, ".efi") != 0)
+ if (!endswith_no_case(de->d_name, ".efi"))
continue;
k = copy_one_file(esp_path, de->d_name, force);
@@ -780,347 +622,239 @@ static int install_binaries(const char *esp_path, bool force) {
r = k;
}
- closedir(d);
return r;
}
static bool same_entry(uint16_t id, const sd_id128_t uuid, const char *path) {
- char *opath = NULL;
+ _cleanup_free_ char *opath = NULL;
sd_id128_t ouuid;
- int err;
- bool same = false;
+ int r;
- err = efi_get_boot_option(id, NULL, &ouuid, &opath, NULL);
- if (err < 0)
+ r = efi_get_boot_option(id, NULL, &ouuid, &opath, NULL);
+ if (r < 0)
return false;
if (!sd_id128_equal(uuid, ouuid))
- goto finish;
-
+ return false;
if (!streq_ptr(path, opath))
- goto finish;
-
- same = true;
+ return false;
-finish:
- return same;
+ return true;
}
static int find_slot(sd_id128_t uuid, const char *path, uint16_t *id) {
- uint16_t *options = NULL;
- int n_options;
- int i;
- uint16_t new_id = 0;
- bool existing = false;
+ _cleanup_free_ uint16_t *options = NULL;
+ int n, i;
- n_options = efi_get_boot_options(&options);
- if (n_options < 0)
- return n_options;
+ n = efi_get_boot_options(&options);
+ if (n < 0)
+ return n;
/* find already existing systemd-boot entry */
- for (i = 0; i < n_options; i++)
+ for (i = 0; i < n; i++)
if (same_entry(options[i], uuid, path)) {
- new_id = options[i];
- existing = true;
- goto finish;
+ *id = options[i];
+ return 1;
}
/* find free slot in the sorted BootXXXX variable list */
- for (i = 0; i < n_options; i++)
+ for (i = 0; i < n; i++)
if (i != options[i]) {
- new_id = i;
- goto finish;
+ *id = i;
+ return 1;
}
/* use the next one */
if (i == 0xffff)
return -ENOSPC;
- new_id = i;
-
-finish:
- *id = new_id;
- free(options);
- return existing;
+ *id = i;
+ return 0;
}
static int insert_into_order(uint16_t slot, bool first) {
- uint16_t *order = NULL;
- uint16_t *new_order;
- int n_order;
- int i;
- int err = 0;
+ _cleanup_free_ uint16_t *order = NULL;
+ uint16_t *t;
+ int n, i;
- n_order = efi_get_boot_order(&order);
- if (n_order <= 0) {
+ n = efi_get_boot_order(&order);
+ if (n <= 0)
/* no entry, add us */
- err = efi_set_boot_order(&slot, 1);
- goto finish;
- }
+ return efi_set_boot_order(&slot, 1);
/* are we the first and only one? */
- if (n_order == 1 && order[0] == slot)
- goto finish;
+ if (n == 1 && order[0] == slot)
+ return 0;
/* are we already in the boot order? */
- for (i = 0; i < n_order; i++) {
+ for (i = 0; i < n; i++) {
if (order[i] != slot)
continue;
/* we do not require to be the first one, all is fine */
if (!first)
- goto finish;
+ return 0;
/* move us to the first slot */
- memmove(&order[1], order, i * sizeof(uint16_t));
+ memmove(order + 1, order, i * sizeof(uint16_t));
order[0] = slot;
- efi_set_boot_order(order, n_order);
- goto finish;
+ return efi_set_boot_order(order, n);
}
/* extend array */
- new_order = realloc(order, (n_order+1) * sizeof(uint16_t));
- if (!new_order) {
- err = -ENOMEM;
- goto finish;
- }
- order = new_order;
+ t = realloc(order, (n + 1) * sizeof(uint16_t));
+ if (!t)
+ return -ENOMEM;
+ order = t;
/* add us to the top or end of the list */
if (first) {
- memmove(&order[1], order, n_order * sizeof(uint16_t));
+ memmove(order + 1, order, n * sizeof(uint16_t));
order[0] = slot;
} else
- order[n_order] = slot;
-
- efi_set_boot_order(order, n_order+1);
+ order[n] = slot;
-finish:
- free(order);
- return err;
+ return efi_set_boot_order(order, n + 1);
}
static int remove_from_order(uint16_t slot) {
_cleanup_free_ uint16_t *order = NULL;
- int n_order;
- int i;
- int err = 0;
+ int n, i;
- n_order = efi_get_boot_order(&order);
- if (n_order < 0)
- return n_order;
- if (n_order == 0)
- return 0;
+ n = efi_get_boot_order(&order);
+ if (n <= 0)
+ return n;
- for (i = 0; i < n_order; i++) {
+ for (i = 0; i < n; i++) {
if (order[i] != slot)
continue;
- if (i+1 < n_order)
- memmove(&order[i], &order[i+1], (n_order - i) * sizeof(uint16_t));
- efi_set_boot_order(order, n_order-1);
- break;
+ if (i + 1 < n)
+ memmove(order + i, order + i+1, (n - i) * sizeof(uint16_t));
+ return efi_set_boot_order(order, n - 1);
}
- return err;
+ return 0;
}
static int install_variables(const char *esp_path,
uint32_t part, uint64_t pstart, uint64_t psize,
sd_id128_t uuid, const char *path,
bool first) {
- char *p = NULL;
- uint16_t *options = NULL;
+ char *p;
uint16_t slot;
int r;
if (!is_efi_boot()) {
- fprintf(stderr, "Not booted with EFI, skipping EFI variable setup.\n");
+ log_warning("Not booted with EFI, skipping EFI variable setup.");
return 0;
}
- if (asprintf(&p, "%s%s", esp_path, path) < 0) {
- fprintf(stderr, "Out of memory.\n");
- return -ENOMEM;
- }
-
+ p = strjoina(esp_path, path);
if (access(p, F_OK) < 0) {
if (errno == ENOENT)
- r = 0;
+ return 0;
else
- r = -errno;
- goto finish;
+ return log_error_errno(errno, "Cannot access \"%s\": %m", p);
}
r = find_slot(uuid, path, &slot);
- if (r < 0) {
- if (r == -ENOENT)
- fprintf(stderr, "Failed to access EFI variables. Is the \"efivarfs\" filesystem mounted?\n");
- else
- fprintf(stderr, "Failed to determine current boot order: %s\n", strerror(-r));
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r,
+ r == -ENOENT ?
+ "Failed to access EFI variables. Is the \"efivarfs\" filesystem mounted?" :
+ "Failed to determine current boot order: %m");
if (first || r == false) {
r = efi_add_boot_option(slot, "Linux Boot Manager",
part, pstart, psize,
uuid, path);
- if (r < 0) {
- fprintf(stderr, "Failed to create EFI Boot variable entry: %s\n", strerror(-r));
- goto finish;
- }
- fprintf(stderr, "Created EFI boot entry \"Linux Boot Manager\".\n");
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to create EFI Boot variable entry: %m");
- insert_into_order(slot, first);
+ log_info("Created EFI boot entry \"Linux Boot Manager\".");
+ }
-finish:
- free(p);
- free(options);
- return r;
+ return insert_into_order(slot, first);
}
static int remove_boot_efi(const char *esp_path) {
+ char *p;
+ _cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
- char *p = NULL, *q = NULL;
- DIR *d = NULL;
- int r = 0, c = 0;
-
- if (asprintf(&p, "%s/EFI/Boot", esp_path) < 0) {
- fprintf(stderr, "Out of memory.\n");
- return -ENOMEM;
- }
+ int r, c = 0;
+ p = strjoina(esp_path, "/EFI/Boot");
d = opendir(p);
if (!d) {
- if (errno == ENOENT) {
- r = 0;
- goto finish;
- }
+ if (errno == ENOENT)
+ return 0;
- fprintf(stderr, "Failed to read %s: %m\n", p);
- r = -errno;
- goto finish;
+ return log_error_errno(errno, "Failed to open directory \"%s\": %m", p);
}
while ((de = readdir(d))) {
- char *v;
- size_t n;
- FILE *f;
+ _cleanup_close_ int fd = -1;
+ _cleanup_free_ char *v = NULL;
if (de->d_name[0] == '.')
continue;
- n = strlen(de->d_name);
- if (n < 4 || strcasecmp(de->d_name + n - 4, ".EFI") != 0)
+ if (!endswith_no_case(de->d_name, ".efi"))
continue;
- if (strncasecmp(de->d_name, "Boot", 4) != 0)
+ if (!startswith_no_case(de->d_name, "Boot"))
continue;
- free(q);
- q = NULL;
- if (asprintf(&q, "%s/%s", p, de->d_name) < 0) {
- fprintf(stderr, "Out of memory.\n");
- r = -ENOMEM;
- goto finish;
- }
-
- f = fopen(q, "re");
- if (!f) {
- fprintf(stderr, "Failed to open %s for reading: %m\n", q);
- r = -errno;
- goto finish;
- }
-
- r = get_file_version(f, &v);
- fclose(f);
+ fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to open \"%s/%s\" for reading: %m", p, de->d_name);
+ r = get_file_version(fd, &v);
if (r < 0)
- goto finish;
-
- if (r > 0 && strncmp(v, "systemd-boot ", 10) == 0) {
-
- r = unlink(q);
- if (r < 0) {
- fprintf(stderr, "Failed to remove %s: %m\n", q);
- r = -errno;
- free(v);
- goto finish;
- } else
- fprintf(stderr, "Removed %s.\n", q);
+ return r;
+ if (r > 0 && startswith(v, "systemd-boot ")) {
+ r = unlinkat(dirfd(d), de->d_name, 0);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to remove \"%s/%s\": %m", p, de->d_name);
+
+ log_info("Removed \"%s/\%s\".", p, de->d_name);
}
c++;
- free(v);
}
- r = c;
-
-finish:
- if (d)
- closedir(d);
- free(p);
- free(q);
-
- return r;
+ return c;
}
static int rmdir_one(const char *prefix, const char *suffix) {
char *p;
- if (asprintf(&p, "%s/%s", prefix, suffix) < 0) {
- fprintf(stderr, "Out of memory.\n");
- return -ENOMEM;
- }
-
+ p = strjoina(prefix, "/", suffix);
if (rmdir(p) < 0) {
- if (errno != ENOENT && errno != ENOTEMPTY) {
- fprintf(stderr, "Failed to remove %s: %m\n", p);
- free(p);
- return -errno;
- }
+ if (!IN_SET(errno, ENOENT, ENOTEMPTY))
+ return log_error_errno(errno, "Failed to remove \"%s\": %m", p);
} else
- fprintf(stderr, "Removed %s.\n", p);
+ log_info("Removed \"%s\".", p);
- free(p);
return 0;
}
-
static int remove_binaries(const char *esp_path) {
char *p;
int r, q;
+ unsigned i;
- if (asprintf(&p, "%s/EFI/systemd-boot", esp_path) < 0) {
- fprintf(stderr, "Out of memory.\n");
- return -ENOMEM;
- }
-
+ p = strjoina(esp_path, "/EFI/systemd");
r = rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL);
- free(p);
q = remove_boot_efi(esp_path);
if (q < 0 && r == 0)
r = q;
- q = rmdir_one(esp_path, "loader/entries");
- if (q < 0 && r == 0)
- r = q;
-
- q = rmdir_one(esp_path, "loader");
- if (q < 0 && r == 0)
- r = q;
-
- q = rmdir_one(esp_path, "EFI/Boot");
- if (q < 0 && r == 0)
- r = q;
-
- q = rmdir_one(esp_path, "EFI/systemd-boot");
- if (q < 0 && r == 0)
- r = q;
-
- q = rmdir_one(esp_path, "EFI");
- if (q < 0 && r == 0)
- r = q;
+ for (i = ELEMENTSOF(efi_subdirs); i > 0; i--) {
+ q = rmdir_one(esp_path, efi_subdirs[i-1]);
+ if (q < 0 && r == 0)
+ r = q;
+ }
return r;
}
@@ -1141,16 +875,16 @@ static int remove_variables(sd_id128_t uuid, const char *path, bool in_order) {
return r;
if (in_order)
- remove_from_order(slot);
-
- return 0;
+ return remove_from_order(slot);
+ else
+ return 0;
}
static int install_loader_config(const char *esp_path) {
- char *p = NULL;
+ char *p;
char line[64];
char *machine = NULL;
- FILE *f;
+ _cleanup_fclose_ FILE *f = NULL, *g = NULL;
f = fopen("/etc/machine-id", "re");
if (!f)
@@ -1166,24 +900,18 @@ static int install_loader_config(const char *esp_path) {
machine = line;
}
- fclose(f);
-
if (!machine)
return -ESRCH;
- if (asprintf(&p, "%s/%s", esp_path, "loader/loader.conf") < 0) {
- fprintf(stderr, "Out of memory.\n");
- return -ENOMEM;
- }
-
- f = fopen(p, "wxe");
- if (f) {
- fprintf(f, "#timeout 3\n");
- fprintf(f, "default %s-*\n", machine);
- fclose(f);
+ p = strjoina(esp_path, "/loader/loader.conf");
+ g = fopen(p, "wxe");
+ if (g) {
+ fprintf(g, "#timeout 3\n");
+ fprintf(g, "default %s-*\n", machine);
+ if (ferror(g))
+ return log_error_errno(EIO, "Failed to write \"%s\": %m", p);
}
- free(p);
return 0;
}
@@ -1196,7 +924,7 @@ static int help(void) {
" --path=PATH Path to the EFI System Partition (ESP)\n"
" --no-variables Don't touch EFI variables\n"
"\n"
- "Comands:\n"
+ "Commands:\n"
" status Show status of installed systemd-boot and EFI variables\n"
" install Install systemd-boot to the ESP and EFI variables\n"
" update Update systemd-boot in the ESP and EFI variables\n"
@@ -1206,7 +934,7 @@ static int help(void) {
return 0;
}
-static const char *arg_path = NULL;
+static const char *arg_path = "/boot";
static bool arg_touch_variables = true;
static int parse_argv(int argc, char *argv[]) {
@@ -1229,7 +957,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argc >= 0);
assert(argv);
- while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
+ while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
switch (c) {
case 'h':
@@ -1252,14 +980,20 @@ static int parse_argv(int argc, char *argv[]) {
return -EINVAL;
default:
- fprintf(stderr, "Unknown option code '%c'.\n", c);
- return -EINVAL;
+ assert_not_reached("Unknown option");
}
- }
return 1;
}
+static void read_loader_efi_var(const char *name, char **var) {
+ int r;
+
+ r = efi_get_variable_string(EFI_VENDOR_LOADER, name, var);
+ if (r < 0 && r != -ENOENT)
+ log_warning_errno(r, "Failed to read EFI variable %s: %m", name);
+}
+
static int bootctl_main(int argc, char*argv[]) {
enum action {
ACTION_STATUS,
@@ -1279,13 +1013,12 @@ static int bootctl_main(int argc, char*argv[]) {
sd_id128_t uuid = {};
uint32_t part = 0;
- uint64_t pstart = 0;
- uint64_t psize = 0;
- unsigned int i;
- int q;
- int r;
+ uint64_t pstart = 0, psize = 0;
+ int r, q;
if (argv[optind]) {
+ unsigned i;
+
for (i = 0; i < ELEMENTSOF(verbs); i++) {
if (!streq(argv[optind], verbs[i].verb))
continue;
@@ -1293,26 +1026,19 @@ static int bootctl_main(int argc, char*argv[]) {
break;
}
if (i >= ELEMENTSOF(verbs)) {
- fprintf(stderr, "Unknown operation %s\n", argv[optind]);
- r = -EINVAL;
- goto finish;
+ log_error("Unknown operation \"%s\"", argv[optind]);
+ return -EINVAL;
}
}
- if (!arg_path)
- arg_path = "/boot";
-
- if (geteuid() != 0) {
- fprintf(stderr, "Need to be root.\n");
- r = -EPERM;
- goto finish;
- }
+ if (geteuid() != 0)
+ return log_error_errno(EPERM, "Need to be root.");
r = verify_esp(arg_path, &part, &pstart, &psize, &uuid);
if (r == -ENODEV && !arg_path)
- fprintf(stderr, "You might want to use --path= to indicate the path to your ESP, in case it is not mounted to /boot.\n");
+ log_notice("You might want to use --path= to indicate the path to your ESP, in case it is not mounted on /boot.");
if (r < 0)
- goto finish;
+ return r;
switch (arg_action) {
case ACTION_STATUS: {
@@ -1322,32 +1048,48 @@ static int bootctl_main(int argc, char*argv[]) {
_cleanup_free_ char *loader_path = NULL;
sd_id128_t loader_part_uuid = {};
- efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderFirmwareType", &fw_type);
- efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderFirmwareInfo", &fw_info);
- efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderInfo", &loader);
- if (efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderImageIdentifier", &loader_path) > 0)
- efi_tilt_backslashes(loader_path);
- efi_loader_get_device_part_uuid(&loader_part_uuid);
-
- printf("System:\n");
- printf(" Firmware: %s (%s)\n", fw_type, strna(fw_info));
- printf(" Secure Boot: %s\n", is_efi_secure_boot() ? "enabled" : "disabled");
- printf(" Setup Mode: %s\n", is_efi_secure_boot_setup_mode() ? "setup" : "user");
- printf("\n");
-
- printf("Loader:\n");
- printf(" Product: %s\n", strna(loader));
- if (!sd_id128_equal(loader_part_uuid, SD_ID128_NULL))
- printf(" Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
- SD_ID128_FORMAT_VAL(loader_part_uuid));
- else
- printf(" Partition: n/a\n");
- printf(" File: %s%s\n", draw_special_char(DRAW_TREE_RIGHT), strna(loader_path));
- printf("\n");
+ if (is_efi_boot()) {
+ read_loader_efi_var("LoaderFirmwareType", &fw_type);
+ read_loader_efi_var("LoaderFirmwareInfo", &fw_info);
+ read_loader_efi_var("LoaderInfo", &loader);
+ read_loader_efi_var("LoaderImageIdentifier", &loader_path);
+ if (loader_path)
+ efi_tilt_backslashes(loader_path);
+ r = efi_loader_get_device_part_uuid(&loader_part_uuid);
+ if (r < 0 && r == -ENOENT)
+ log_warning_errno(r, "Failed to read EFI variable LoaderDevicePartUUID: %m");
+
+ printf("System:\n");
+ printf(" Firmware: %s (%s)\n", strna(fw_type), strna(fw_info));
+
+ r = is_efi_secure_boot();
+ if (r < 0)
+ log_warning_errno(r, "Failed to query secure boot status: %m");
+ else
+ printf(" Secure Boot: %s\n", r ? "enabled" : "disabled");
+
+ r = is_efi_secure_boot_setup_mode();
+ if (r < 0)
+ log_warning_errno(r, "Failed to query secure boot mode: %m");
+ else
+ printf(" Setup Mode: %s\n", r ? "setup" : "user");
+ printf("\n");
+
+ printf("Loader:\n");
+ printf(" Product: %s\n", strna(loader));
+ if (!sd_id128_equal(loader_part_uuid, SD_ID128_NULL))
+ printf(" Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
+ SD_ID128_FORMAT_VAL(loader_part_uuid));
+ else
+ printf(" Partition: n/a\n");
+ printf(" File: %s%s\n", draw_special_char(DRAW_TREE_RIGHT), strna(loader_path));
+ printf("\n");
+ } else
+ printf("System:\n Not booted with EFI\n");
r = status_binaries(arg_path, uuid);
if (r < 0)
- goto finish;
+ return r;
if (arg_touch_variables)
r = status_variables();
@@ -1360,10 +1102,13 @@ static int bootctl_main(int argc, char*argv[]) {
r = install_binaries(arg_path, arg_action == ACTION_INSTALL);
if (r < 0)
- goto finish;
+ return r;
- if (arg_action == ACTION_INSTALL)
- install_loader_config(arg_path);
+ if (arg_action == ACTION_INSTALL) {
+ r = install_loader_config(arg_path);
+ if (r < 0)
+ return r;
+ }
if (arg_touch_variables)
r = install_variables(arg_path,
@@ -1383,8 +1128,7 @@ static int bootctl_main(int argc, char*argv[]) {
break;
}
-finish:
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return r;
}
int main(int argc, char *argv[]) {
diff --git a/src/bus-proxyd/driver.c b/src/bus-proxyd/driver.c
index 90ddd735dc..4ac955da41 100644
--- a/src/bus-proxyd/driver.c
+++ b/src/bus-proxyd/driver.c
@@ -280,6 +280,7 @@ int bus_proxy_process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m, SharedPoli
return synthetic_driver_send(m->bus, reply);
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetConnectionSELinuxSecurityContext")) {
+ _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
_cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
@@ -293,7 +294,15 @@ int bus_proxy_process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m, SharedPoli
if (!(creds->mask & SD_BUS_CREDS_SELINUX_CONTEXT))
return synthetic_reply_method_errno(m, -EOPNOTSUPP, NULL);
- return synthetic_reply_method_return(m, "y", creds->label, strlen(creds->label));
+ r = sd_bus_message_new_method_return(m, &reply);
+ if (r < 0)
+ return synthetic_reply_method_errno(m, r, NULL);
+
+ r = sd_bus_message_append_array(reply, 'y', creds->label, strlen(creds->label));
+ if (r < 0)
+ return synthetic_reply_method_errno(m, r, NULL);
+
+ return synthetic_driver_send(m->bus, reply);
} else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetConnectionUnixProcessID")) {
_cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
diff --git a/src/console/consoled-manager.c b/src/console/consoled-manager.c
index b288239cae..e560dcf524 100644
--- a/src/console/consoled-manager.c
+++ b/src/console/consoled-manager.c
@@ -21,16 +21,17 @@
#include <errno.h>
#include <stdlib.h>
-#include "consoled.h"
-#include "grdev.h"
-#include "idev.h"
-#include "log.h"
#include "sd-bus.h"
#include "sd-event.h"
#include "sd-login.h"
+#include "log.h"
+#include "signal-util.h"
+#include "util.h"
+#include "consoled.h"
+#include "idev.h"
+#include "grdev.h"
#include "sysview.h"
#include "unifont.h"
-#include "util.h"
int manager_new(Manager **out) {
_cleanup_(manager_freep) Manager *m = NULL;
diff --git a/src/console/consoled.c b/src/console/consoled.c
index 26dd068f5c..9f69e8983f 100644
--- a/src/console/consoled.c
+++ b/src/console/consoled.c
@@ -21,9 +21,10 @@
#include <errno.h>
#include <stdlib.h>
-#include "consoled.h"
-#include "log.h"
#include "sd-daemon.h"
+#include "log.h"
+#include "signal-util.h"
+#include "consoled.h"
int main(int argc, char *argv[]) {
_cleanup_(manager_freep) Manager *m = NULL;
diff --git a/src/core/automount.c b/src/core/automount.c
index 13f80c2abd..d847dc1629 100644
--- a/src/core/automount.c
+++ b/src/core/automount.c
@@ -747,7 +747,7 @@ static int automount_start(Unit *u) {
assert(a);
assert(a->state == AUTOMOUNT_DEAD || a->state == AUTOMOUNT_FAILED);
- if (path_is_mount_point(a->where, false) > 0) {
+ if (path_is_mount_point(a->where, 0) > 0) {
log_unit_error(u, "Path %s is already a mount point, refusing start.", a->where);
return -EEXIST;
}
diff --git a/src/core/busname.c b/src/core/busname.c
index 17b85134e8..11f3b98009 100644
--- a/src/core/busname.c
+++ b/src/core/busname.c
@@ -22,15 +22,16 @@
#include <sys/mman.h>
#include "special.h"
+#include "formats-util.h"
+#include "signal-util.h"
#include "bus-kernel.h"
#include "bus-internal.h"
#include "bus-util.h"
-#include "service.h"
#include "kdbus.h"
#include "bus-policy.h"
+#include "service.h"
#include "dbus-busname.h"
#include "busname.h"
-#include "formats-util.h"
static const UnitActiveState state_translation_table[_BUSNAME_STATE_MAX] = {
[BUSNAME_DEAD] = UNIT_INACTIVE,
diff --git a/src/core/dbus-kill.c b/src/core/dbus-kill.c
index fb29e147cb..3b8116281c 100644
--- a/src/core/dbus-kill.c
+++ b/src/core/dbus-kill.c
@@ -19,9 +19,11 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "signal-util.h"
+#include "bus-util.h"
+
#include "kill.h"
#include "dbus-kill.h"
-#include "bus-util.h"
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_kill_mode, kill_mode, KillMode);
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index a4ece2adb5..1892725f91 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -561,6 +561,8 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("PartOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_PART_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RequiredBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRED_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RequiredByOverridable", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUIRED_BY_OVERRIDABLE]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RequisiteOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("RequisiteOfOverridable", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_REQUISITE_OF_OVERRIDABLE]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("WantedBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_WANTED_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("BoundBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_BOUND_BY]), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ConsistsOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_CONSISTS_OF]), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -714,7 +716,7 @@ static int send_new_signal(sd_bus *bus, void *userdata) {
assert(u);
p = unit_dbus_path(u);
- if (!u)
+ if (!p)
return -ENOMEM;
r = sd_bus_message_new_signal(
@@ -791,7 +793,7 @@ static int send_removed_signal(sd_bus *bus, void *userdata) {
assert(u);
p = unit_dbus_path(u);
- if (!u)
+ if (!p)
return -ENOMEM;
r = sd_bus_message_new_signal(
diff --git a/src/core/device.c b/src/core/device.c
index 8eca4c2fa0..e7efcf0f0a 100644
--- a/src/core/device.c
+++ b/src/core/device.c
@@ -292,18 +292,19 @@ static int device_add_udev_wants(Unit *u, struct udev_device *dev) {
static int device_setup_unit(Manager *m, struct udev_device *dev, const char *path, bool main) {
_cleanup_free_ char *e = NULL;
- const char *sysfs;
+ const char *sysfs = NULL;
Unit *u = NULL;
bool delete;
int r;
assert(m);
- assert(dev);
assert(path);
- sysfs = udev_device_get_syspath(dev);
- if (!sysfs)
- return 0;
+ if (dev) {
+ sysfs = udev_device_get_syspath(dev);
+ if (!sysfs)
+ return 0;
+ }
r = unit_name_from_path(path, ".device", &e);
if (r < 0)
@@ -312,6 +313,7 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa
u = manager_get_unit(m, e);
if (u &&
+ sysfs &&
DEVICE(u)->sysfs &&
!path_equal(DEVICE(u)->sysfs, sysfs)) {
log_unit_debug(u, "Device %s appeared twice with different sysfs paths %s and %s", e, DEVICE(u)->sysfs, sysfs);
@@ -336,17 +338,19 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa
/* If this was created via some dependency and has not
* actually been seen yet ->sysfs will not be
* initialized. Hence initialize it if necessary. */
+ if (sysfs) {
+ r = device_set_sysfs(DEVICE(u), sysfs);
+ if (r < 0)
+ goto fail;
- r = device_set_sysfs(DEVICE(u), sysfs);
- if (r < 0)
- goto fail;
+ (void) device_update_description(u, dev, path);
- (void) device_update_description(u, dev, path);
+ /* The additional systemd udev properties we only interpret
+ * for the main object */
+ if (main)
+ (void) device_add_udev_wants(u, dev);
+ }
- /* The additional systemd udev properties we only interpret
- * for the main object */
- if (main)
- (void) device_add_udev_wants(u, dev);
/* Note that this won't dispatch the load queue, the caller
* has to do that if needed and appropriate */
@@ -767,6 +771,9 @@ int device_found_node(Manager *m, const char *node, bool add, DeviceFound found,
assert(m);
assert(node);
+ if (!device_supported())
+ return 0;
+
/* This is called whenever we find a device referenced in
* /proc/swaps or /proc/self/mounts. Such a device might be
* mounted/enabled at a time where udev has not finished
@@ -788,23 +795,16 @@ int device_found_node(Manager *m, const char *node, bool add, DeviceFound found,
* will still have a device node even when the medium
* is not there... */
- if (stat(node, &st) < 0) {
- if (errno == ENOENT)
+ if (stat(node, &st) >= 0) {
+ if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
return 0;
- return log_error_errno(errno, "Failed to stat device node file %s: %m", node);
- }
-
- if (!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode))
- return 0;
+ dev = udev_device_new_from_devnum(m->udev, S_ISBLK(st.st_mode) ? 'b' : 'c', st.st_rdev);
+ if (!dev && errno != ENOENT)
+ return log_error_errno(errno, "Failed to get udev device from devnum %u:%u: %m", major(st.st_rdev), minor(st.st_rdev));
- dev = udev_device_new_from_devnum(m->udev, S_ISBLK(st.st_mode) ? 'b' : 'c', st.st_rdev);
- if (!dev) {
- if (errno == ENOENT)
- return 0;
-
- return log_oom();
- }
+ } else if (errno != ENOENT)
+ return log_error_errno(errno, "Failed to stat device node file %s: %m", node);
/* If the device is known in the kernel and newly
* appeared, then we'll create a device unit for it,
diff --git a/src/core/execute.c b/src/core/execute.c
index 97498b23d7..4120493bda 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -49,14 +49,13 @@
#include <sys/apparmor.h>
#endif
+#include "sd-messages.h"
#include "rm-rf.h"
-#include "execute.h"
#include "strv.h"
#include "macro.h"
#include "capability.h"
#include "util.h"
#include "log.h"
-#include "sd-messages.h"
#include "ioprio.h"
#include "securebits.h"
#include "namespace.h"
@@ -79,6 +78,7 @@
#include "formats-util.h"
#include "process-util.h"
#include "terminal-util.h"
+#include "signal-util.h"
#ifdef HAVE_APPARMOR
#include "apparmor-util.h"
@@ -88,6 +88,8 @@
#include "seccomp-util.h"
#endif
+#include "execute.h"
+
#define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC)
#define IDLE_TIMEOUT2_USEC (1*USEC_PER_SEC)
@@ -1491,7 +1493,7 @@ static int exec_child(
return -errno;
}
- if (context->personality != 0xffffffffUL)
+ if (context->personality != PERSONALITY_INVALID)
if (personality(context->personality) < 0) {
*exit_status = EXIT_PERSONALITY;
return -errno;
@@ -1946,7 +1948,7 @@ void exec_context_init(ExecContext *c) {
c->syslog_level_prefix = true;
c->ignore_sigpipe = true;
c->timer_slack_nsec = NSEC_INFINITY;
- c->personality = 0xffffffffUL;
+ c->personality = PERSONALITY_INVALID;
c->runtime_directory_mode = 0755;
}
@@ -2427,7 +2429,7 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
"%sSELinuxContext: %s%s\n",
prefix, c->selinux_context_ignore ? "-" : "", c->selinux_context);
- if (c->personality != 0xffffffffUL)
+ if (c->personality != PERSONALITY_INVALID)
fprintf(f,
"%sPersonality: %s\n",
prefix, strna(personality_to_string(c->personality)));
diff --git a/src/core/hostname-setup.c b/src/core/hostname-setup.c
index 217f201d05..932ddbf95a 100644
--- a/src/core/hostname-setup.c
+++ b/src/core/hostname-setup.c
@@ -30,35 +30,13 @@
#include "hostname-util.h"
#include "hostname-setup.h"
-static int read_and_strip_hostname(const char *path, char **hn) {
- char *s;
- int r;
-
- assert(path);
- assert(hn);
-
- r = read_one_line_file(path, &s);
- if (r < 0)
- return r;
-
- hostname_cleanup(s, false);
-
- if (isempty(s)) {
- free(s);
- return -ENOENT;
- }
-
- *hn = s;
- return 0;
-}
-
int hostname_setup(void) {
int r;
_cleanup_free_ char *b = NULL;
const char *hn;
bool enoent = false;
- r = read_and_strip_hostname("/etc/hostname", &b);
+ r = read_hostname_config("/etc/hostname", &b);
if (r < 0) {
if (r == -ENOENT)
enoent = true;
diff --git a/src/core/job.c b/src/core/job.c
index 9ad1f735f0..8a047df0c3 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -388,38 +388,38 @@ bool job_type_is_redundant(JobType a, UnitActiveState b) {
}
}
-void job_type_collapse(JobType *t, Unit *u) {
+JobType job_type_collapse(JobType t, Unit *u) {
UnitActiveState s;
- switch (*t) {
+ switch (t) {
case JOB_TRY_RESTART:
s = unit_active_state(u);
if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s))
- *t = JOB_NOP;
- else
- *t = JOB_RESTART;
- break;
+ return JOB_NOP;
+
+ return JOB_RESTART;
case JOB_RELOAD_OR_START:
s = unit_active_state(u);
if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s))
- *t = JOB_START;
- else
- *t = JOB_RELOAD;
- break;
+ return JOB_START;
+
+ return JOB_RELOAD;
default:
- ;
+ return t;
}
}
int job_type_merge_and_collapse(JobType *a, JobType b, Unit *u) {
- JobType t = job_type_lookup_merge(*a, b);
+ JobType t;
+
+ t = job_type_lookup_merge(*a, b);
if (t < 0)
return -EEXIST;
- *a = t;
- job_type_collapse(a, u);
+
+ *a = job_type_collapse(t, u);
return 0;
}
@@ -790,6 +790,24 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) {
NULL);
}
+static void job_fail_dependencies(Unit *u, UnitDependency d) {
+ Unit *other;
+ Iterator i;
+
+ assert(u);
+
+ SET_FOREACH(other, u->dependencies[d], i) {
+ Job *j = other->job;
+
+ if (!j)
+ continue;
+ if (!IN_SET(j->type, JOB_START, JOB_VERIFY_ACTIVE))
+ continue;
+
+ job_finish_and_invalidate(j, JOB_DEPENDENCY, true);
+ }
+}
+
int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) {
Unit *u;
Unit *other;
@@ -831,37 +849,14 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) {
/* Fail depending jobs on failure */
if (result != JOB_DONE && recursive) {
-
- if (t == JOB_START ||
- t == JOB_VERIFY_ACTIVE) {
-
- SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY], i)
- if (other->job &&
- (other->job->type == JOB_START ||
- other->job->type == JOB_VERIFY_ACTIVE))
- job_finish_and_invalidate(other->job, JOB_DEPENDENCY, true);
-
- SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i)
- if (other->job &&
- (other->job->type == JOB_START ||
- other->job->type == JOB_VERIFY_ACTIVE))
- job_finish_and_invalidate(other->job, JOB_DEPENDENCY, true);
-
- SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i)
- if (other->job &&
- !other->job->override &&
- (other->job->type == JOB_START ||
- other->job->type == JOB_VERIFY_ACTIVE))
- job_finish_and_invalidate(other->job, JOB_DEPENDENCY, true);
-
- } else if (t == JOB_STOP) {
-
- SET_FOREACH(other, u->dependencies[UNIT_CONFLICTED_BY], i)
- if (other->job &&
- (other->job->type == JOB_START ||
- other->job->type == JOB_VERIFY_ACTIVE))
- job_finish_and_invalidate(other->job, JOB_DEPENDENCY, true);
- }
+ if (IN_SET(t, JOB_START, JOB_VERIFY_ACTIVE)) {
+ job_fail_dependencies(u, UNIT_REQUIRED_BY);
+ job_fail_dependencies(u, UNIT_REQUISITE_OF);
+ job_fail_dependencies(u, UNIT_BOUND_BY);
+ job_fail_dependencies(u, UNIT_REQUIRED_BY_OVERRIDABLE);
+ job_fail_dependencies(u, UNIT_REQUISITE_OF_OVERRIDABLE);
+ } else if (t == JOB_STOP)
+ job_fail_dependencies(u, UNIT_CONFLICTED_BY);
}
/* Trigger OnFailure dependencies that are not generated by
diff --git a/src/core/job.h b/src/core/job.h
index 9119e4834a..1d1b10f1d3 100644
--- a/src/core/job.h
+++ b/src/core/job.h
@@ -206,7 +206,7 @@ bool job_type_is_redundant(JobType a, UnitActiveState b) _pure_;
/* Collapses a state-dependent job type into a simpler type by observing
* the state of the unit which it is going to be applied to. */
-void job_type_collapse(JobType *t, Unit *u);
+JobType job_type_collapse(JobType t, Unit *u);
int job_type_merge_and_collapse(JobType *a, JobType b, Unit *u);
diff --git a/src/core/kill.c b/src/core/kill.c
index 60a510eae6..2de71c6bf9 100644
--- a/src/core/kill.c
+++ b/src/core/kill.c
@@ -19,9 +19,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-
-#include "kill.h"
#include "util.h"
+#include "signal-util.h"
+#include "kill.h"
void kill_context_init(KillContext *c) {
assert(c);
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index e1cd72fe98..df5fe6fb32 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -52,6 +52,7 @@
#include "errno-list.h"
#include "af-list.h"
#include "cap-list.h"
+#include "signal-util.h"
#include "bus-internal.h"
#ifdef HAVE_SECCOMP
@@ -609,7 +610,7 @@ int config_parse_exec(
else
skip = strneq(word, "\\;", MAX(l, 1U));
- r = cunescape_length(word + skip, l - skip, 0, &c);
+ r = cunescape_length(word + skip, l - skip, UNESCAPE_RELAX, &c);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to unescape command line, ignoring: %s", rvalue);
r = 0;
@@ -3046,7 +3047,7 @@ int config_parse_personality(
assert(personality);
p = personality_from_string(rvalue);
- if (p == 0xffffffffUL) {
+ if (p == PERSONALITY_INVALID) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"Failed to parse personality, ignoring: %s", rvalue);
return 0;
diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c
index e083c5b347..b3d22840cf 100644
--- a/src/core/machine-id-setup.c
+++ b/src/core/machine-id-setup.c
@@ -297,7 +297,7 @@ int machine_id_commit(const char *root) {
etc_machine_id = path_kill_slashes(x);
}
- r = path_is_mount_point(etc_machine_id, false);
+ r = path_is_mount_point(etc_machine_id, 0);
if (r < 0)
return log_error_errno(r, "Failed to determine whether %s is a mount point: %m", etc_machine_id);
if (r == 0) {
diff --git a/src/core/main.c b/src/core/main.c
index c39815b106..674e47e788 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -60,6 +60,10 @@
#include "bus-error.h"
#include "bus-util.h"
#include "selinux-util.h"
+#include "formats-util.h"
+#include "process-util.h"
+#include "terminal-util.h"
+#include "signal-util.h"
#include "manager.h"
#include "dbus-manager.h"
#include "load-fragment.h"
@@ -72,9 +76,6 @@
#include "ima-setup.h"
#include "smack-setup.h"
#include "kmod-setup.h"
-#include "formats-util.h"
-#include "process-util.h"
-#include "terminal-util.h"
static enum {
ACTION_RUN,
@@ -1496,7 +1497,7 @@ int main(int argc, char *argv[]) {
setsid();
/* Move out of the way, so that we won't block unmounts */
- assert_se(chdir("/") == 0);
+ assert_se(chdir("/") == 0);
/* Reset the console, but only if this is really init and we
* are freshly booted */
diff --git a/src/core/manager.c b/src/core/manager.c
index 2fddc44fe3..ae473d05c2 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -40,8 +40,6 @@
#include "sd-daemon.h"
#include "sd-messages.h"
-#include "manager.h"
-#include "transaction.h"
#include "hashmap.h"
#include "macro.h"
#include "strv.h"
@@ -65,14 +63,17 @@
#include "bus-common-errors.h"
#include "bus-error.h"
#include "bus-util.h"
-#include "dbus.h"
-#include "dbus-unit.h"
-#include "dbus-job.h"
-#include "dbus-manager.h"
#include "bus-kernel.h"
#include "time-util.h"
#include "process-util.h"
#include "terminal-util.h"
+#include "signal-util.h"
+#include "dbus.h"
+#include "dbus-unit.h"
+#include "dbus-job.h"
+#include "dbus-manager.h"
+#include "manager.h"
+#include "transaction.h"
/* Initial delay and the interval for printing status messages about running jobs */
#define JOBS_IN_PROGRESS_WAIT_USEC (5*USEC_PER_SEC)
@@ -1204,7 +1205,7 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool ove
log_unit_debug(unit, "Trying to enqueue job %s/%s/%s", unit->id, job_type_to_string(type), job_mode_to_string(mode));
- job_type_collapse(&type, unit);
+ type = job_type_collapse(type, unit);
tr = transaction_new(mode == JOB_REPLACE_IRREVERSIBLY);
if (!tr)
@@ -2703,6 +2704,15 @@ void manager_check_finished(Manager *m) {
assert(m);
+ if (m->n_reloading > 0)
+ return;
+
+ /* Verify that we are actually running currently. Initially
+ * the exit code is set to invalid, and during operation it is
+ * then set to MANAGER_OK */
+ if (m->exit_code != MANAGER_OK)
+ return;
+
if (hashmap_size(m->jobs) > 0) {
if (m->jobs_in_progress_event_source)
diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c
index 23fb0a1e38..c35248eeae 100644
--- a/src/core/mount-setup.c
+++ b/src/core/mount-setup.c
@@ -156,7 +156,7 @@ static int mount_one(const MountPoint *p, bool relabel) {
if (relabel)
label_fix(p->where, true, true);
- r = path_is_mount_point(p->where, true);
+ r = path_is_mount_point(p->where, AT_SYMLINK_FOLLOW);
if (r < 0 && r != -ENOENT)
return r;
if (r > 0)
@@ -373,7 +373,7 @@ int mount_setup(bool loaded_policy) {
/* Create a few default symlinks, which are normally created
* by udevd, but some scripts might need them before we start
* udevd. */
- dev_setup(NULL);
+ dev_setup(NULL, UID_INVALID, GID_INVALID);
/* Mark the root directory as shared in regards to mount
* propagation. The kernel defaults to "private", but we think
diff --git a/src/core/mount.c b/src/core/mount.c
index 8ef3d1755d..ba1dcf1e85 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -1628,7 +1628,10 @@ fail:
}
static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) {
+ _cleanup_set_free_ Set *around = NULL, *gone = NULL;
Manager *m = userdata;
+ const char *what;
+ Iterator i;
Unit *u;
int r;
@@ -1695,6 +1698,19 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
if (!mount->is_mounted) {
+ /* A mount point is not around right now. It
+ * might be gone, or might never have
+ * existed. */
+
+ if (mount->from_proc_self_mountinfo &&
+ mount->parameters_proc_self_mountinfo.what) {
+
+ /* Remember that this device might just have disappeared */
+ if (set_ensure_allocated(&gone, &string_hash_ops) < 0 ||
+ set_put(gone, mount->parameters_proc_self_mountinfo.what) < 0)
+ log_oom(); /* we don't care too much about OOM here... */
+ }
+
mount->from_proc_self_mountinfo = false;
switch (mount->state) {
@@ -1710,13 +1726,9 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
break;
}
- if (mount->parameters_proc_self_mountinfo.what)
- (void) device_found_node(m, mount->parameters_proc_self_mountinfo.what, false, DEVICE_FOUND_MOUNT, true);
-
-
} else if (mount->just_mounted || mount->just_changed) {
- /* New or changed mount entry */
+ /* A mount point was added or changed */
switch (mount->state) {
@@ -1743,10 +1755,27 @@ static int mount_dispatch_io(sd_event_source *source, int fd, uint32_t revents,
}
}
+ if (mount->is_mounted &&
+ mount->from_proc_self_mountinfo &&
+ mount->parameters_proc_self_mountinfo.what) {
+
+ if (set_ensure_allocated(&around, &string_hash_ops) < 0 ||
+ set_put(around, mount->parameters_proc_self_mountinfo.what) < 0)
+ log_oom();
+ }
+
/* Reset the flags for later calls */
mount->is_mounted = mount->just_mounted = mount->just_changed = false;
}
+ SET_FOREACH(what, gone, i) {
+ if (set_contains(around, what))
+ continue;
+
+ /* Let the device units know that the device is no longer mounted */
+ (void) device_found_node(m, what, false, DEVICE_FOUND_MOUNT, true);
+ }
+
return 0;
}
diff --git a/src/core/namespace.c b/src/core/namespace.c
index 5b782798c8..01a817bf23 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -125,22 +125,6 @@ static void drop_duplicates(BindMount *m, unsigned *n) {
*n = t - m;
}
-static int mount_move_root(const char *path) {
- if (chdir(path) < 0)
- return -errno;
-
- if (mount(path, "/", NULL, MS_MOVE, NULL) < 0)
- return -errno;
-
- if (chroot(".") < 0)
- return -errno;
-
- if (chdir("/") < 0)
- return -errno;
-
- return 0;
-}
-
static int mount_dev(BindMount *m) {
static const char devnodes[] =
"/dev/null\0"
@@ -240,7 +224,7 @@ static int mount_dev(BindMount *m) {
}
}
- dev_setup(temporary_mount);
+ dev_setup(temporary_mount, UID_INVALID, GID_INVALID);
/* Create the /dev directory if missing. It is more likely to be
* missing when the service is started with RootDirectory. This is
@@ -515,7 +499,7 @@ int setup_namespace(
if (protect_system != PROTECT_SYSTEM_NO) {
const char *usr_dir, *boot_dir, *etc_dir;
- usr_dir = prefix_roota(root_directory, "/home");
+ usr_dir = prefix_roota(root_directory, "/usr");
boot_dir = prefix_roota(root_directory, "/boot");
boot_dir = strjoina("-", boot_dir);
etc_dir = prefix_roota(root_directory, "/etc");
diff --git a/src/core/service.c b/src/core/service.c
index 07347b99a4..c7e65772ea 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -47,6 +47,7 @@
#include "bus-kernel.h"
#include "formats-util.h"
#include "process-util.h"
+#include "signal-util.h"
static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
[SERVICE_DEAD] = UNIT_INACTIVE,
diff --git a/src/core/socket.c b/src/core/socket.c
index 17b8a5059d..fc5eb1464a 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -47,8 +47,9 @@
#include "selinux-util.h"
#include "dbus-socket.h"
#include "unit.h"
-#include "socket.h"
#include "formats-util.h"
+#include "signal-util.h"
+#include "socket.h"
static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = {
[SOCKET_DEAD] = UNIT_INACTIVE,
diff --git a/src/core/swap.c b/src/core/swap.c
index 12ebf84f62..193c8c3767 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -717,8 +717,8 @@ fail:
}
static void swap_enter_activating(Swap *s) {
- _cleanup_free_ char *discard = NULL;
- int r, priority = -1;
+ _cleanup_free_ char *opts = NULL;
+ int r;
assert(s);
@@ -726,13 +726,21 @@ static void swap_enter_activating(Swap *s) {
s->control_command = s->exec_command + SWAP_EXEC_ACTIVATE;
if (s->from_fragment) {
- fstab_filter_options(s->parameters_fragment.options, "discard\0", NULL, &discard, NULL);
+ int priority = -1;
- priority = s->parameters_fragment.priority;
- if (priority < 0) {
- r = fstab_find_pri(s->parameters_fragment.options, &priority);
+ r = fstab_find_pri(s->parameters_fragment.options, &priority);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse swap priority \"%s\", ignoring: %m", s->parameters_fragment.options);
+ else if (r == 1 && s->parameters_fragment.priority >= 0)
+ log_warning("Duplicate swap priority configuration by Priority and Options fields.");
+
+ if (r <= 0 && s->parameters_fragment.priority >= 0) {
+ if (s->parameters_fragment.options)
+ r = asprintf(&opts, "%s,pri=%i", s->parameters_fragment.options, s->parameters_fragment.priority);
+ else
+ r = asprintf(&opts, "pri=%i", s->parameters_fragment.priority);
if (r < 0)
- log_notice_errno(r, "Failed to parse swap priority \"%s\", ignoring: %m", s->parameters_fragment.options);
+ goto fail;
}
}
@@ -740,24 +748,9 @@ static void swap_enter_activating(Swap *s) {
if (r < 0)
goto fail;
- if (priority >= 0) {
- char p[DECIMAL_STR_MAX(int)];
-
- sprintf(p, "%i", priority);
- r = exec_command_append(s->control_command, "-p", p, NULL);
- if (r < 0)
- goto fail;
- }
-
- if (discard && !streq(discard, "none")) {
- const char *discard_arg;
-
- if (streq(discard, "all"))
- discard_arg = "--discard";
- else
- discard_arg = strjoina("--discard=", discard);
-
- r = exec_command_append(s->control_command, discard_arg, NULL);
+ if (s->parameters_fragment.options || opts) {
+ r = exec_command_append(s->control_command, "-o",
+ opts ? : s->parameters_fragment.options, NULL);
if (r < 0)
goto fail;
}
diff --git a/src/core/transaction.c b/src/core/transaction.c
index d1f7b7f2bf..090103fbda 100644
--- a/src/core/transaction.c
+++ b/src/core/transaction.c
@@ -861,8 +861,7 @@ int transaction_add_job_and_dependencies(
/* by ? job_type_to_string(by->type) : "NA"); */
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);
+ 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)
@@ -921,9 +920,7 @@ int transaction_add_job_and_dependencies(
r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, override, false, false, ignore_order, e);
if (r < 0) {
log_unit_warning(dep, "Cannot add dependency job for, ignoring: %s", bus_error_message(e, r));
-
- if (e)
- sd_bus_error_free(e);
+ sd_bus_error_free(e);
}
}
@@ -938,8 +935,7 @@ int transaction_add_job_and_dependencies(
if (r != -EBADR)
goto fail;
- if (e)
- sd_bus_error_free(e);
+ sd_bus_error_free(e);
}
}
@@ -949,8 +945,7 @@ int transaction_add_job_and_dependencies(
if (r != -EBADR)
goto fail;
- if (e)
- sd_bus_error_free(e);
+ sd_bus_error_free(e);
}
}
@@ -961,9 +956,7 @@ int transaction_add_job_and_dependencies(
r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, r,
"Cannot add dependency job, ignoring: %s",
bus_error_message(e, r));
-
- if (e)
- sd_bus_error_free(e);
+ sd_bus_error_free(e);
}
}
@@ -974,9 +967,7 @@ int transaction_add_job_and_dependencies(
r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, r,
"Cannot add dependency job, ignoring: %s",
bus_error_message(e, r));
-
- if (e)
- sd_bus_error_free(e);
+ sd_bus_error_free(e);
}
}
@@ -986,8 +977,7 @@ int transaction_add_job_and_dependencies(
if (r != -EBADR)
goto fail;
- if (e)
- sd_bus_error_free(e);
+ sd_bus_error_free(e);
}
}
@@ -998,9 +988,7 @@ int transaction_add_job_and_dependencies(
r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, r,
"Cannot add dependency job, ignoring: %s",
bus_error_message(e, r));
-
- if (e)
- sd_bus_error_free(e);
+ sd_bus_error_free(e);
}
}
@@ -1010,8 +998,7 @@ int transaction_add_job_and_dependencies(
if (r != -EBADR)
goto fail;
- if (e)
- sd_bus_error_free(e);
+ sd_bus_error_free(e);
}
}
@@ -1021,49 +1008,44 @@ int transaction_add_job_and_dependencies(
log_unit_warning(dep,
"Cannot add dependency job, ignoring: %s",
bus_error_message(e, r));
-
- if (e)
- sd_bus_error_free(e);
+ sd_bus_error_free(e);
}
}
}
if (type == JOB_STOP || type == JOB_RESTART) {
+ static const UnitDependency propagate_deps[] = {
+ UNIT_REQUIRED_BY,
+ UNIT_REQUISITE_OF,
+ UNIT_BOUND_BY,
+ UNIT_CONSISTS_OF,
+ };
+
+ JobType ptype;
+ unsigned j;
+
+ /* We propagate STOP as STOP, but RESTART only
+ * as TRY_RESTART, in order not to start
+ * dependencies that are not around. */
+ ptype = type == JOB_RESTART ? JOB_TRY_RESTART : type;
+
+ for (j = 0; j < ELEMENTSOF(propagate_deps); j++)
+ SET_FOREACH(dep, ret->unit->dependencies[propagate_deps[j]], i) {
+ JobType nt;
+
+ nt = job_type_collapse(ptype, dep);
+ if (nt == JOB_NOP)
+ continue;
+
+ r = transaction_add_job_and_dependencies(tr, nt, dep, ret, true, override, false, false, ignore_order, e);
+ if (r < 0) {
+ if (r != -EBADR)
+ goto fail;
- SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRED_BY], i) {
- r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e);
- if (r < 0) {
- if (r != -EBADR)
- goto fail;
-
- if (e)
sd_bus_error_free(e);
+ }
}
- }
-
- SET_FOREACH(dep, ret->unit->dependencies[UNIT_BOUND_BY], i) {
- r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e);
- if (r < 0) {
- if (r != -EBADR)
- goto fail;
-
- if (e)
- sd_bus_error_free(e);
- }
- }
-
- SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONSISTS_OF], i) {
- r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e);
- if (r < 0) {
- if (r != -EBADR)
- goto fail;
-
- if (e)
- sd_bus_error_free(e);
- }
- }
-
}
if (type == JOB_RELOAD) {
@@ -1074,9 +1056,7 @@ int transaction_add_job_and_dependencies(
log_unit_warning(dep,
"Cannot add dependency reload job, ignoring: %s",
bus_error_message(e, r));
-
- if (e)
- sd_bus_error_free(e);
+ sd_bus_error_free(e);
}
}
}
diff --git a/src/core/unit.c b/src/core/unit.c
index 1c1d9cf896..e380276d49 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -85,12 +85,13 @@ Unit *unit_new(Manager *m, size_t size) {
u->manager = m;
u->type = _UNIT_TYPE_INVALID;
- 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;
+ RATELIMIT_INIT(u->auto_stop_ratelimit, 10 * USEC_PER_SEC, 16);
+
return u;
}
@@ -1092,6 +1093,8 @@ static int unit_add_target_dependencies(Unit *u) {
static const UnitDependency deps[] = {
UNIT_REQUIRED_BY,
UNIT_REQUIRED_BY_OVERRIDABLE,
+ UNIT_REQUISITE_OF,
+ UNIT_REQUISITE_OF_OVERRIDABLE,
UNIT_WANTED_BY,
UNIT_BOUND_BY
};
@@ -1589,8 +1592,20 @@ bool unit_can_reload(Unit *u) {
}
static void unit_check_unneeded(Unit *u) {
- Iterator i;
+
+ static const UnitDependency needed_dependencies[] = {
+ UNIT_REQUIRED_BY,
+ UNIT_REQUIRED_BY_OVERRIDABLE,
+ UNIT_REQUISITE,
+ UNIT_REQUISITE_OF_OVERRIDABLE,
+ UNIT_WANTED_BY,
+ UNIT_BOUND_BY,
+ };
+
Unit *other;
+ Iterator i;
+ unsigned j;
+ int r;
assert(u);
@@ -1603,32 +1618,32 @@ static void unit_check_unneeded(Unit *u) {
if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(u)))
return;
- SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY], i)
- if (unit_active_or_pending(other))
- return;
-
- SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i)
- if (unit_active_or_pending(other))
- return;
+ for (j = 0; j < ELEMENTSOF(needed_dependencies); j++)
+ SET_FOREACH(other, u->dependencies[needed_dependencies[j]], i)
+ if (unit_active_or_pending(other))
+ return;
- SET_FOREACH(other, u->dependencies[UNIT_WANTED_BY], i)
- if (unit_active_or_pending(other))
- return;
-
- SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i)
- if (unit_active_or_pending(other))
- return;
+ /* If stopping a unit fails continously we might enter a stop
+ * loop here, hence stop acting on the service being
+ * unnecessary after a while. */
+ if (!ratelimit_test(&u->auto_stop_ratelimit)) {
+ log_unit_warning(u, "Unit not needed anymore, but not stopping since we tried this too often recently.");
+ return;
+ }
log_unit_info(u, "Unit not needed anymore. Stopping.");
/* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */
- manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL);
+ r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL);
+ if (r < 0)
+ log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %m");
}
static void unit_check_binds_to(Unit *u) {
bool stop = false;
Unit *other;
Iterator i;
+ int r;
assert(u);
@@ -1652,11 +1667,21 @@ static void unit_check_binds_to(Unit *u) {
if (!stop)
return;
+ /* If stopping a unit fails continously we might enter a stop
+ * loop here, hence stop acting on the service being
+ * unnecessary after a while. */
+ if (!ratelimit_test(&u->auto_stop_ratelimit)) {
+ log_unit_warning(u, "Unit is bound to inactive unit %s, but not stopping since we tried this too often recently.", other->id);
+ return;
+ }
+
assert(other);
log_unit_info(u, "Unit is bound to inactive unit %s. Stopping, too.", other->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);
+ r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, true, NULL, NULL);
+ if (r < 0)
+ log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %m");
}
static void retroactively_start_dependencies(Unit *u) {
@@ -2163,13 +2188,15 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen
[UNIT_REQUIRES] = UNIT_REQUIRED_BY,
[UNIT_REQUIRES_OVERRIDABLE] = UNIT_REQUIRED_BY_OVERRIDABLE,
[UNIT_WANTS] = UNIT_WANTED_BY,
- [UNIT_REQUISITE] = UNIT_REQUIRED_BY,
- [UNIT_REQUISITE_OVERRIDABLE] = UNIT_REQUIRED_BY_OVERRIDABLE,
+ [UNIT_REQUISITE] = UNIT_REQUISITE_OF,
+ [UNIT_REQUISITE_OVERRIDABLE] = UNIT_REQUISITE_OF_OVERRIDABLE,
[UNIT_BINDS_TO] = UNIT_BOUND_BY,
[UNIT_PART_OF] = UNIT_CONSISTS_OF,
- [UNIT_REQUIRED_BY] = _UNIT_DEPENDENCY_INVALID,
- [UNIT_REQUIRED_BY_OVERRIDABLE] = _UNIT_DEPENDENCY_INVALID,
- [UNIT_WANTED_BY] = _UNIT_DEPENDENCY_INVALID,
+ [UNIT_REQUIRED_BY] = UNIT_REQUIRES,
+ [UNIT_REQUIRED_BY_OVERRIDABLE] = UNIT_REQUIRES_OVERRIDABLE,
+ [UNIT_REQUISITE_OF] = UNIT_REQUISITE,
+ [UNIT_REQUISITE_OF_OVERRIDABLE] = UNIT_REQUISITE_OVERRIDABLE,
+ [UNIT_WANTED_BY] = UNIT_WANTS,
[UNIT_BOUND_BY] = UNIT_BINDS_TO,
[UNIT_CONSISTS_OF] = UNIT_PART_OF,
[UNIT_CONFLICTS] = UNIT_CONFLICTED_BY,
@@ -2713,16 +2740,8 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
job_free(j);
return r;
}
- } else {
- /* legacy */
- JobType type;
-
- type = job_type_from_string(v);
- if (type < 0)
- log_unit_debug(u, "Failed to parse job type value: %s", v);
- else
- u->deserialized_job = type;
- }
+ } else /* legacy for pre-44 */
+ log_unit_warning(u, "Update from too old systemd versions are unsupported, cannot deserialize job: %s", v);
continue;
} else if (streq(l, "inactive-exit-timestamp")) {
dual_timestamp_deserialize(v, &u->inactive_exit_timestamp);
@@ -2871,13 +2890,6 @@ int unit_coldplug(Unit *u) {
r = job_coldplug(u->job);
if (r < 0)
return r;
- } else if (u->deserialized_job >= 0) {
- /* legacy */
- r = manager_add_job(u->manager, u->deserialized_job, u, JOB_IGNORE_REQUIREMENTS, false, NULL, NULL);
- if (r < 0)
- return r;
-
- u->deserialized_job = _JOB_TYPE_INVALID;
}
return 0;
diff --git a/src/core/unit.h b/src/core/unit.h
index 62257c403e..9491ef64f9 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -166,15 +166,12 @@ struct Unit {
/* Used during GC sweeps */
unsigned gc_marker;
- /* When deserializing, temporarily store the job type for this
- * unit here, if there was a job scheduled.
- * Only for deserializing from a legacy version. New style uses full
- * serialized jobs. */
- int deserialized_job; /* This is actually of type JobType */
-
/* Error code when we didn't manage to load the unit (negative) */
int load_error;
+ /* Make sure we never enter endless loops with the check unneeded logic, or the BindsTo= logic */
+ RateLimit auto_stop_ratelimit;
+
/* Cached unit file state and preset */
UnitFileState unit_file_state;
int unit_file_preset;
@@ -236,7 +233,7 @@ struct Unit {
bool cgroup_subtree_mask_valid:1;
/* Did we already invoke unit_coldplug() for this unit? */
- bool coldplugged;
+ bool coldplugged:1;
};
struct UnitStatusMessageFormats {
diff --git a/src/efi-boot-generator/efi-boot-generator.c b/src/efi-boot-generator/efi-boot-generator.c
index 42b21f57de..e6b15c9bb0 100644
--- a/src/efi-boot-generator/efi-boot-generator.c
+++ b/src/efi-boot-generator/efi-boot-generator.c
@@ -68,7 +68,7 @@ int main(int argc, char *argv[]) {
return EXIT_SUCCESS;
}
- r = path_is_mount_point("/boot", true);
+ r = path_is_mount_point("/boot", AT_SYMLINK_FOLLOW);
if (r > 0) {
log_debug("/boot is already a mount point, exiting.");
return EXIT_SUCCESS;
diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c
index 9bbe9ff673..f0e5c5f239 100644
--- a/src/fsck/fsck.c
+++ b/src/fsck/fsck.c
@@ -34,6 +34,7 @@
#include "util.h"
#include "process-util.h"
+#include "signal-util.h"
#include "special.h"
#include "bus-util.h"
#include "bus-error.h"
diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c
index 302fad37bb..a88b68e2c0 100644
--- a/src/fstab-generator/fstab-generator.c
+++ b/src/fstab-generator/fstab-generator.c
@@ -53,10 +53,9 @@ static int add_swap(
bool noauto,
bool nofail) {
- _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL, *filtered = NULL;
+ _cleanup_free_ char *name = NULL, *unit = NULL, *lnk = NULL;
_cleanup_fclose_ FILE *f = NULL;
- int r, pri = -1;
- const char *opts;
+ int r;
assert(what);
assert(me);
@@ -71,18 +70,6 @@ static int add_swap(
return 0;
}
- opts = me->mnt_opts;
- r = fstab_find_pri(opts, &pri);
- if (r < 0) {
- log_error_errno(r, "Failed to parse priority, ignoring: %m");
-
- /* Remove invalid pri field */
- r = fstab_filter_options(opts, "pri\0", NULL, NULL, &filtered);
- if (r < 0)
- return log_error_errno(r, "Failed to parse options: %m");
- opts = filtered;
- }
-
r = unit_name_from_path(what, ".swap", &name);
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
@@ -109,20 +96,15 @@ static int add_swap(
"What=%s\n",
what);
- /* Note that we currently pass the priority field twice, once
- * in Priority=, and once in Options= */
- if (pri >= 0)
- fprintf(f, "Priority=%i\n", pri);
-
- if (!isempty(opts) && !streq(opts, "defaults"))
- fprintf(f, "Options=%s\n", opts);
+ if (!isempty(me->mnt_opts) && !streq(me->mnt_opts, "defaults"))
+ fprintf(f, "Options=%s\n", me->mnt_opts);
r = fflush_and_check(f);
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, opts, NULL);
+ r = generator_write_timeouts(arg_dest, what, what, me->mnt_opts, NULL);
if (r < 0)
return r;
diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c
index b192526186..b46e160888 100644
--- a/src/gpt-auto-generator/gpt-auto-generator.c
+++ b/src/gpt-auto-generator/gpt-auto-generator.c
@@ -297,7 +297,7 @@ static int probe_and_add_mount(
assert(where);
assert(description);
- if (path_is_mount_point(where, true) <= 0 &&
+ if (path_is_mount_point(where, AT_SYMLINK_FOLLOW) <= 0 &&
dir_is_empty(where) <= 0) {
log_debug("%s already populated, ignoring.", where);
return 0;
diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c
index ab9ddc706a..7ff3a4e224 100644
--- a/src/hostname/hostnamed.c
+++ b/src/hostname/hostnamed.c
@@ -96,7 +96,7 @@ static int context_read_data(Context *c) {
if (!c->data[PROP_HOSTNAME])
return -ENOMEM;
- r = read_one_line_file("/etc/hostname", &c->data[PROP_STATIC_HOSTNAME]);
+ r = read_hostname_config("/etc/hostname", &c->data[PROP_STATIC_HOSTNAME]);
if (r < 0 && r != -ENOENT)
return r;
diff --git a/src/import/export.c b/src/import/export.c
index 201c5ab356..d4bc88e010 100644
--- a/src/import/export.c
+++ b/src/import/export.c
@@ -23,6 +23,7 @@
#include "sd-event.h"
#include "event-util.h"
+#include "signal-util.h"
#include "verbs.h"
#include "build.h"
#include "machine-image.h"
diff --git a/src/import/import-common.c b/src/import/import-common.c
index aede2f9b36..9711614000 100644
--- a/src/import/import-common.c
+++ b/src/import/import-common.c
@@ -26,6 +26,7 @@
#include "util.h"
#include "btrfs-util.h"
#include "capability.h"
+#include "signal-util.h"
#include "import-common.h"
int import_make_read_only_fd(int fd) {
diff --git a/src/import/import.c b/src/import/import.c
index f3072b3775..fff5a104b1 100644
--- a/src/import/import.c
+++ b/src/import/import.c
@@ -25,6 +25,7 @@
#include "event-util.h"
#include "verbs.h"
#include "build.h"
+#include "signal-util.h"
#include "machine-image.h"
#include "import-util.h"
#include "import-tar.h"
diff --git a/src/import/importd.c b/src/import/importd.c
index 45d1d93343..50566a6e5c 100644
--- a/src/import/importd.c
+++ b/src/import/importd.c
@@ -34,6 +34,7 @@
#include "path-util.h"
#include "import-util.h"
#include "process-util.h"
+#include "signal-util.h"
typedef struct Transfer Transfer;
typedef struct Manager Manager;
diff --git a/src/import/pull-common.c b/src/import/pull-common.c
index 59091ba7cb..d2588d4fa0 100644
--- a/src/import/pull-common.c
+++ b/src/import/pull-common.c
@@ -30,6 +30,7 @@
#include "pull-job.h"
#include "pull-common.h"
#include "process-util.h"
+#include "signal-util.h"
#define FILENAME_ESCAPE "/.#\"\'"
diff --git a/src/import/pull-dkr.c b/src/import/pull-dkr.c
index f89eb88669..d7476dc340 100644
--- a/src/import/pull-dkr.c
+++ b/src/import/pull-dkr.c
@@ -52,6 +52,9 @@ struct DkrPull {
sd_event *event;
CurlGlue *glue;
+ char *index_protocol;
+ char *index_address;
+
char *index_url;
char *image_root;
@@ -62,9 +65,10 @@ struct DkrPull {
PullJob *layer_job;
char *name;
- char *tag;
+ char *reference;
char *id;
+ char *response_digest;
char *response_token;
char **response_registries;
@@ -88,9 +92,9 @@ struct DkrPull {
#define PROTOCOL_PREFIX "https://"
#define HEADER_TOKEN "X-Do" /* the HTTP header for the auth token */ "cker-Token:"
-#define HEADER_REGISTRY "X-Do" /*the HTTP header for the registry */ "cker-Endpoints:"
-
-#define LAYERS_MAX 2048
+#define HEADER_REGISTRY "X-Do" /* the HTTP header for the registry */ "cker-Endpoints:"
+#define HEADER_DIGEST "Do" /* the HTTP header for the manifest digest */ "cker-Content-Digest:"
+#define LAYERS_MAX 127
static void dkr_pull_job_on_finished(PullJob *j);
@@ -118,12 +122,13 @@ DkrPull* dkr_pull_unref(DkrPull *i) {
}
free(i->name);
- free(i->tag);
+ free(i->reference);
free(i->id);
free(i->response_token);
- free(i->response_registries);
strv_free(i->ancestry);
free(i->final_path);
+ free(i->index_address);
+ free(i->index_protocol);
free(i->index_url);
free(i->image_root);
free(i->local);
@@ -417,10 +422,27 @@ static int dkr_pull_add_token(DkrPull *i, PullJob *j) {
return 0;
}
+static int dkr_pull_add_bearer_token(DkrPull *i, PullJob *j) {
+ const char *t = NULL;
+
+ assert(i);
+ assert(j);
+
+ if (i->response_token)
+ t = strjoina("Authorization: Bearer ", i->response_token);
+ else
+ return -EINVAL;
+
+ j->request_header = curl_slist_new("Accept: application/json", t, NULL);
+ if (!j->request_header)
+ return -ENOMEM;
+
+ return 0;
+}
+
static bool dkr_pull_is_done(DkrPull *i) {
assert(i);
assert(i->images_job);
-
if (i->images_job->state != PULL_JOB_DONE)
return false;
@@ -430,7 +452,7 @@ static bool dkr_pull_is_done(DkrPull *i) {
if (!i->ancestry_job || i->ancestry_job->state != PULL_JOB_DONE)
return false;
- if (!i->json_job || i->json_job->state != PULL_JOB_DONE)
+ if (i->json_job && i->json_job->state != PULL_JOB_DONE)
return false;
if (i->layer_job && i->layer_job->state != PULL_JOB_DONE)
@@ -442,8 +464,9 @@ static bool dkr_pull_is_done(DkrPull *i) {
return true;
}
-static int dkr_pull_make_local_copy(DkrPull *i) {
+static int dkr_pull_make_local_copy(DkrPull *i, DkrPullVersion version) {
int r;
+ _cleanup_free_ char *p = NULL;
assert(i);
@@ -456,10 +479,30 @@ static int dkr_pull_make_local_copy(DkrPull *i) {
return log_oom();
}
- r = pull_make_local_copy(i->final_path, i->image_root, i->local, i->force_local);
+ if (version == DKR_PULL_V2) {
+ r = path_get_parent(i->image_root, &p);
+ if (r < 0)
+ return r;
+ }
+
+ r = pull_make_local_copy(i->final_path, p ?: i->image_root, i->local, i->force_local);
if (r < 0)
return r;
+ if (version == DKR_PULL_V2) {
+ char **k = NULL;
+ STRV_FOREACH(k, i->ancestry) {
+ _cleanup_free_ char *d = strjoin(i->image_root, "/.dkr-", *k, NULL);
+ r = btrfs_subvol_remove(d, false);
+ if (r < 0)
+ return r;
+ }
+
+ r = rmdir(i->image_root);
+ if (r < 0)
+ return r;
+ }
+
return 0;
}
@@ -517,6 +560,68 @@ static void dkr_pull_job_on_progress(PullJob *j) {
DKR_DOWNLOADING);
}
+static void dkr_pull_job_on_finished_v2(PullJob *j);
+
+static int dkr_pull_pull_layer_v2(DkrPull *i) {
+ _cleanup_free_ char *path = NULL;
+ const char *url, *layer = NULL;
+ int r;
+
+ assert(i);
+ assert(!i->layer_job);
+ assert(!i->temp_path);
+ assert(!i->final_path);
+
+ for (;;) {
+ layer = dkr_pull_current_layer(i);
+ if (!layer)
+ return 0; /* no more layers */
+
+ path = strjoin(i->image_root, "/.dkr-", layer, NULL);
+ if (!path)
+ return log_oom();
+
+ if (laccess(path, F_OK) < 0) {
+ if (errno == ENOENT)
+ break;
+
+ return log_error_errno(errno, "Failed to check for container: %m");
+ }
+
+ log_info("Layer %s already exists, skipping.", layer);
+
+ i->current_ancestry++;
+
+ free(path);
+ path = NULL;
+ }
+
+ log_info("Pulling layer %s...", layer);
+
+ i->final_path = path;
+ path = NULL;
+
+ url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v2/", i->name, "/blobs/", layer);
+ r = pull_job_new(&i->layer_job, url, i->glue, i);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate layer job: %m");
+
+ r = dkr_pull_add_bearer_token(i, i->layer_job);
+ if (r < 0)
+ return log_oom();
+
+ i->layer_job->on_finished = dkr_pull_job_on_finished_v2;
+ i->layer_job->on_open_disk = dkr_pull_job_on_open_disk;
+ i->layer_job->on_progress = dkr_pull_job_on_progress;
+ i->layer_job->grow_machine_directory = i->grow_machine_directory;
+
+ r = pull_job_begin(i->layer_job);
+ if (r < 0)
+ return log_error_errno(r, "Failed to start layer job: %m");
+
+ return 0;
+}
+
static int dkr_pull_pull_layer(DkrPull *i) {
_cleanup_free_ char *path = NULL;
const char *url, *layer = NULL;
@@ -577,6 +682,342 @@ static int dkr_pull_pull_layer(DkrPull *i) {
return 0;
}
+static int dkr_pull_job_on_header(PullJob *j, const char *header, size_t sz) {
+ _cleanup_free_ char *registry = NULL;
+ char *token, *digest;
+ DkrPull *i;
+ int r;
+
+ assert(j);
+ assert(j->userdata);
+
+ i = j->userdata;
+ r = curl_header_strdup(header, sz, HEADER_TOKEN, &token);
+ if (r < 0)
+ return log_oom();
+ if (r > 0) {
+ free(i->response_token);
+ i->response_token = token;
+ return 0;
+ }
+
+ r = curl_header_strdup(header, sz, HEADER_DIGEST, &digest);
+ if (r < 0)
+ return log_oom();
+ if (r > 0) {
+ free(i->response_digest);
+ i->response_digest = digest;
+ return 0;
+ }
+
+ r = curl_header_strdup(header, sz, HEADER_REGISTRY, &registry);
+ if (r < 0)
+ return log_oom();
+ if (r > 0) {
+ char **l, **k;
+
+ l = strv_split(registry, ",");
+ if (!l)
+ return log_oom();
+
+ STRV_FOREACH(k, l) {
+ if (!hostname_is_valid(*k)) {
+ log_error("Registry hostname is not valid.");
+ strv_free(l);
+ return -EBADMSG;
+ }
+ }
+
+ strv_free(i->response_registries);
+ i->response_registries = l;
+ }
+
+ return 0;
+}
+
+static void dkr_pull_job_on_finished_v2(PullJob *j) {
+ DkrPull *i;
+ int r;
+
+ assert(j);
+ assert(j->userdata);
+
+ i = j->userdata;
+ if (j->error != 0) {
+ if (j == i->images_job)
+ log_error_errno(j->error, "Failed to retrieve images list. (Wrong index URL?)");
+ else if (j == i->ancestry_job)
+ log_error_errno(j->error, "Failed to retrieve manifest.");
+ else if (j == i->json_job)
+ log_error_errno(j->error, "Failed to retrieve json data.");
+ else
+ log_error_errno(j->error, "Failed to retrieve layer data.");
+
+ r = j->error;
+ goto finish;
+ }
+
+ if (i->images_job == j) {
+ const char *url;
+
+ assert(!i->tags_job);
+ assert(!i->ancestry_job);
+ assert(!i->json_job);
+ assert(!i->layer_job);
+
+ if (strv_isempty(i->response_registries)) {
+ r = -EBADMSG;
+ log_error("Didn't get registry information.");
+ goto finish;
+ }
+
+ log_info("Index lookup succeeded, directed to registry %s.", i->response_registries[0]);
+ dkr_pull_report_progress(i, DKR_RESOLVING);
+
+ url = strjoina(i->index_protocol, "auth.", i->index_address, "/v2/token/?scope=repository:",
+ i->name, ":pull&service=registry.", i->index_address);
+ r = pull_job_new(&i->tags_job, url, i->glue, i);
+ if (r < 0) {
+ log_error_errno(r, "Failed to allocate tags job: %m");
+ goto finish;
+ }
+
+ i->tags_job->on_finished = dkr_pull_job_on_finished_v2;
+ i->tags_job->on_progress = dkr_pull_job_on_progress;
+
+ r = pull_job_begin(i->tags_job);
+ if (r < 0) {
+ log_error_errno(r, "Failed to start tags job: %m");
+ goto finish;
+ }
+
+ } else if (i->tags_job == j) {
+ const char *url;
+ _cleanup_free_ const char *buf;
+ _cleanup_json_variant_unref_ JsonVariant *doc = NULL;
+ JsonVariant *e = NULL;
+
+ assert(!i->ancestry_job);
+ assert(!i->json_job);
+ assert(!i->layer_job);
+
+ buf = strndup((const char *)j->payload, j->payload_size);
+ if (!buf) {
+ r = -ENOMEM;
+ log_oom();
+ goto finish;
+ }
+
+ r = json_parse(buf, &doc);
+ if (r < 0) {
+ log_error("Unable to parse bearer token\n%s", j->payload);
+ goto finish;
+ }
+
+ e = json_variant_value(doc, "token");
+ if (!e || e->type != JSON_VARIANT_STRING) {
+ r = -EBADMSG;
+ log_error("Invalid JSON format for Bearer token");
+ goto finish;
+ }
+
+ r = free_and_strdup(&i->response_token, json_variant_string(e));
+ if (r < 0) {
+ log_oom();
+ goto finish;
+ }
+
+ url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v2/", i->name, "/manifests/", i->reference);
+ r = pull_job_new(&i->ancestry_job, url, i->glue, i);
+ if (r < 0) {
+ log_error_errno(r, "Failed to allocate ancestry job: %m");
+ goto finish;
+ }
+
+ r = dkr_pull_add_bearer_token(i, i->ancestry_job);
+ if (r < 0)
+ goto finish;
+
+ i->ancestry_job->on_finished = dkr_pull_job_on_finished_v2;
+ i->ancestry_job->on_progress = dkr_pull_job_on_progress;
+ i->ancestry_job->on_header = dkr_pull_job_on_header;
+
+
+ r = pull_job_begin(i->ancestry_job);
+ if (r < 0) {
+ log_error_errno(r, "Failed to start ancestry job: %m");
+ goto finish;
+ }
+
+ } else if (i->ancestry_job == j) {
+
+ _cleanup_json_variant_unref_ JsonVariant *doc = NULL, *compat = NULL;
+ JsonVariant *e = NULL;
+ _cleanup_strv_free_ char **ancestry = NULL;
+ size_t allocated = 0, size = 0;
+ char *path = NULL, **k = NULL;
+
+ r = json_parse((const char *)j->payload, &doc);
+ if (r < 0) {
+ log_error("Invalid JSON Manifest");
+ goto finish;
+ }
+
+ e = json_variant_value(doc, "fsLayers");
+ if (!e || e->type != JSON_VARIANT_ARRAY || e->size == 0) {
+ r = -EBADMSG;
+ goto finish;
+ }
+
+ log_info("JSON manifest with schema v%"PRIi64" for %s parsed!",
+ json_variant_integer(json_variant_value(doc, "schemaVersion")),
+ json_variant_string(json_variant_value(doc, "name")));
+
+ for (unsigned z = 0; z < e->size; z++) {
+ JsonVariant *f = json_variant_element(e, z), *g = NULL;
+ const char *layer;
+ if (f->type != JSON_VARIANT_OBJECT) {
+ r = -EBADMSG;
+ goto finish;
+ }
+
+ g = json_variant_value(f, "blobSum");
+
+ layer = json_variant_string(g);
+ if (!dkr_digest_is_valid(layer)) {
+ r = -EBADMSG;
+ goto finish;
+ }
+
+ if (!GREEDY_REALLOC(ancestry, allocated, size + 2)) {
+ r = -ENOMEM;
+ log_oom();
+ goto finish;
+ }
+
+ ancestry[size] = strdup(layer);
+ if (!ancestry[size]) {
+ r = -ENOMEM;
+ log_oom();
+ goto finish;
+ }
+
+ ancestry[size+1] = NULL;
+ size += 1;
+ }
+
+ e = json_variant_value(doc, "history");
+ if (!e || e->type != JSON_VARIANT_ARRAY) {
+ r = -EBADMSG;
+ goto finish;
+ }
+
+ e = json_variant_element(e, 0);
+ e = json_variant_value(e, "v1Compatibility");
+ r = json_parse(json_variant_string(e), &compat);
+ if (r < 0) {
+ log_error("Invalid v1Compatibility JSON");
+ goto finish;
+ }
+
+ e = json_variant_value(compat, "id");
+
+ strv_free(i->ancestry);
+ i->ancestry = strv_reverse(strv_uniq(ancestry));
+ i->n_ancestry = strv_length(i->ancestry);
+ i->current_ancestry = 0;
+ i->id = strdup(i->ancestry[i->n_ancestry - 1]);
+ if (!i->id) {
+ r = -ENOMEM;
+ log_oom();
+ goto finish;
+ }
+ path = strjoin(i->image_root, "/.dkr-", json_variant_string(e), NULL);
+ if (!path) {
+ r = -ENOMEM;
+ log_oom();
+ goto finish;
+ }
+ free(i->image_root);
+ i->image_root = path;
+ ancestry = NULL;
+
+ log_info("Required layers:\n");
+ STRV_FOREACH(k, i->ancestry)
+ log_info("\t%s", *k);
+ log_info("\nProvenance:\n\tImageID: %s\n\tDigest: %s", json_variant_string(e), i->response_digest);
+
+ dkr_pull_report_progress(i, DKR_DOWNLOADING);
+
+ r = dkr_pull_pull_layer_v2(i);
+ if (r < 0)
+ goto finish;
+
+ } else if (i->layer_job == j) {
+ assert(i->temp_path);
+ assert(i->final_path);
+
+ j->disk_fd = safe_close(j->disk_fd);
+
+ if (i->tar_pid > 0) {
+ r = wait_for_terminate_and_warn("tar", i->tar_pid, true);
+ i->tar_pid = 0;
+ if (r < 0)
+ goto finish;
+ }
+
+ r = aufs_resolve(i->temp_path);
+ if (r < 0) {
+ log_error_errno(r, "Failed to resolve aufs whiteouts: %m");
+ goto finish;
+ }
+
+ r = btrfs_subvol_set_read_only(i->temp_path, true);
+ if (r < 0) {
+ log_error_errno(r, "Failed to mark snapshot read-only: %m");
+ goto finish;
+ }
+
+ if (rename(i->temp_path, i->final_path) < 0) {
+ log_error_errno(errno, "Failed to rename snaphsot: %m");
+ goto finish;
+ }
+
+ log_info("Completed writing to layer %s.", i->final_path);
+
+ i->layer_job = pull_job_unref(i->layer_job);
+ free(i->temp_path);
+ i->temp_path = NULL;
+ free(i->final_path);
+ i->final_path = NULL;
+
+ i->current_ancestry ++;
+ r = dkr_pull_pull_layer_v2(i);
+ if (r < 0)
+ goto finish;
+
+ } else if (i->json_job != j)
+ assert_not_reached("Got finished event for unknown curl object");
+
+ if (!dkr_pull_is_done(i))
+ return;
+
+ dkr_pull_report_progress(i, DKR_COPYING);
+
+ r = dkr_pull_make_local_copy(i, DKR_PULL_V2);
+ if (r < 0)
+ goto finish;
+
+ r = 0;
+
+finish:
+ if (i->on_finished)
+ i->on_finished(i, r, i->userdata);
+ else
+ sd_event_exit(i->event, r);
+
+}
+
static void dkr_pull_job_on_finished(PullJob *j) {
DkrPull *i;
int r;
@@ -618,7 +1059,7 @@ static void dkr_pull_job_on_finished(PullJob *j) {
log_info("Index lookup succeeded, directed to registry %s.", i->response_registries[0]);
dkr_pull_report_progress(i, DKR_RESOLVING);
- url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/repositories/", i->name, "/tags/", i->tag);
+ url = strjoina(PROTOCOL_PREFIX, i->response_registries[0], "/v1/repositories/", i->name, "/tags/", i->reference);
r = pull_job_new(&i->tags_job, url, i->glue, i);
if (r < 0) {
log_error_errno(r, "Failed to allocate tags job: %m");
@@ -790,12 +1231,11 @@ static void dkr_pull_job_on_finished(PullJob *j) {
dkr_pull_report_progress(i, DKR_COPYING);
- r = dkr_pull_make_local_copy(i);
+ r = dkr_pull_make_local_copy(i, DKR_PULL_V1);
if (r < 0)
goto finish;
r = 0;
-
finish:
if (i->on_finished)
i->on_finished(i, r, i->userdata);
@@ -803,52 +1243,38 @@ finish:
sd_event_exit(i->event, r);
}
-static int dkr_pull_job_on_header(PullJob *j, const char *header, size_t sz) {
- _cleanup_free_ char *registry = NULL;
- char *token;
- DkrPull *i;
- int r;
-
- assert(j);
- assert(j->userdata);
+static int get_protocol_address(char **protocol, char **address, const char *url) {
+ const char *sep, *dot;
+ _cleanup_free_ char *a = NULL, *p = NULL;
- i = j->userdata;
+ sep = strstr(url, "://");
+ if (!sep)
+ return -EINVAL;
- r = curl_header_strdup(header, sz, HEADER_TOKEN, &token);
- if (r < 0)
- return log_oom();
- if (r > 0) {
- free(i->response_token);
- i->response_token = token;
- return 0;
- }
+ dot = strrchr(url, '.');
+ if (!dot)
+ return -EINVAL;
+ dot--;
- r = curl_header_strdup(header, sz, HEADER_REGISTRY, &registry);
- if (r < 0)
+ p = strndup(url, (sep - url) + 3);
+ if (!p)
return log_oom();
- if (r > 0) {
- char **l, **k;
- l = strv_split(registry, ",");
- if (!l)
- return log_oom();
+ while (dot > (sep + 3) && *dot != '.')
+ dot--;
- STRV_FOREACH(k, l) {
- if (!hostname_is_valid(*k)) {
- log_error("Registry hostname is not valid.");
- strv_free(l);
- return -EBADMSG;
- }
- }
+ a = strdup(dot + 1);
+ if (!a)
+ return log_oom();
- strv_free(i->response_registries);
- i->response_registries = l;
- }
+ *address = a;
+ *protocol = p;
+ a = p = NULL;
return 0;
}
-int dkr_pull_start(DkrPull *i, const char *name, const char *tag, const char *local, bool force_local) {
+int dkr_pull_start(DkrPull *i, const char *name, const char *reference, const char *local, bool force_local, DkrPullVersion version) {
const char *url;
int r;
@@ -857,7 +1283,7 @@ int dkr_pull_start(DkrPull *i, const char *name, const char *tag, const char *lo
if (!dkr_name_is_valid(name))
return -EINVAL;
- if (tag && !dkr_tag_is_valid(tag))
+ if (reference && !dkr_ref_is_valid(reference))
return -EINVAL;
if (local && !machine_name_is_valid(local))
@@ -866,8 +1292,14 @@ int dkr_pull_start(DkrPull *i, const char *name, const char *tag, const char *lo
if (i->images_job)
return -EBUSY;
- if (!tag)
- tag = "latest";
+ if (!reference)
+ reference = "latest";
+
+ free(i->index_protocol);
+ free(i->index_address);
+ r = get_protocol_address(&i->index_protocol, &i->index_address, i->index_url);
+ if (r < 0)
+ return r;
r = free_and_strdup(&i->local, local);
if (r < 0)
@@ -877,7 +1309,7 @@ int dkr_pull_start(DkrPull *i, const char *name, const char *tag, const char *lo
r = free_and_strdup(&i->name, name);
if (r < 0)
return r;
- r = free_and_strdup(&i->tag, tag);
+ r = free_and_strdup(&i->reference, reference);
if (r < 0)
return r;
@@ -891,7 +1323,11 @@ int dkr_pull_start(DkrPull *i, const char *name, const char *tag, const char *lo
if (r < 0)
return r;
- i->images_job->on_finished = dkr_pull_job_on_finished;
+ if (version == DKR_PULL_V1)
+ i->images_job->on_finished = dkr_pull_job_on_finished;
+ else
+ i->images_job->on_finished = dkr_pull_job_on_finished_v2;
+
i->images_job->on_header = dkr_pull_job_on_header;
i->images_job->on_progress = dkr_pull_job_on_progress;
diff --git a/src/import/pull-dkr.h b/src/import/pull-dkr.h
index 4c4b10c7ac..33d18cb394 100644
--- a/src/import/pull-dkr.h
+++ b/src/import/pull-dkr.h
@@ -24,6 +24,7 @@
#include "sd-event.h"
#include "util.h"
+typedef enum { DKR_PULL_V1, DKR_PULL_V2 } DkrPullVersion;
typedef struct DkrPull DkrPull;
typedef void (*DkrPullFinished)(DkrPull *pull, int error, void *userdata);
@@ -33,4 +34,4 @@ DkrPull* dkr_pull_unref(DkrPull *pull);
DEFINE_TRIVIAL_CLEANUP_FUNC(DkrPull*, dkr_pull_unref);
-int dkr_pull_start(DkrPull *pull, const char *name, const char *tag, const char *local, bool force_local);
+int dkr_pull_start(DkrPull *pull, const char *name, const char *tag, const char *local, bool force_local, DkrPullVersion version);
diff --git a/src/import/pull.c b/src/import/pull.c
index ef7b0359a7..eec4583868 100644
--- a/src/import/pull.c
+++ b/src/import/pull.c
@@ -25,6 +25,7 @@
#include "event-util.h"
#include "verbs.h"
#include "build.h"
+#include "signal-util.h"
#include "machine-image.h"
#include "import-util.h"
#include "pull-tar.h"
@@ -227,7 +228,7 @@ static void on_dkr_finished(DkrPull *pull, int error, void *userdata) {
static int pull_dkr(int argc, char *argv[], void *userdata) {
_cleanup_(dkr_pull_unrefp) DkrPull *pull = NULL;
_cleanup_event_unref_ sd_event *event = NULL;
- const char *name, *tag, *local;
+ const char *name, *reference, *local, *digest;
int r;
if (!arg_dkr_index_url) {
@@ -240,13 +241,19 @@ static int pull_dkr(int argc, char *argv[], void *userdata) {
return -EINVAL;
}
- tag = strchr(argv[1], ':');
- if (tag) {
- name = strndupa(argv[1], tag - argv[1]);
- tag++;
+ digest = strchr(argv[1], '@');
+ if (digest) {
+ reference = digest + 1;
+ name = strndupa(argv[1], digest - argv[1]);
+ }
+
+ reference = strchr(argv[1], ':');
+ if (reference) {
+ name = strndupa(argv[1], reference - argv[1]);
+ reference++;
} else {
name = argv[1];
- tag = "latest";
+ reference = "latest";
}
if (!dkr_name_is_valid(name)) {
@@ -254,8 +261,8 @@ static int pull_dkr(int argc, char *argv[], void *userdata) {
return -EINVAL;
}
- if (!dkr_tag_is_valid(tag)) {
- log_error("Tag name '%s' is not valid.", tag);
+ if (!dkr_ref_is_valid(reference)) {
+ log_error("Tag name '%s' is not valid.", reference);
return -EINVAL;
}
@@ -288,9 +295,9 @@ static int pull_dkr(int argc, char *argv[], void *userdata) {
}
}
- log_info("Pulling '%s' with tag '%s', saving as '%s'.", name, tag, local);
+ log_info("Pulling '%s' with reference '%s', saving as '%s'.", name, reference, local);
} else
- log_info("Pulling '%s' with tag '%s'.", name, tag);
+ log_info("Pulling '%s' with reference '%s'.", name, reference);
r = sd_event_default(&event);
if (r < 0)
@@ -304,7 +311,7 @@ static int pull_dkr(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_error_errno(r, "Failed to allocate puller: %m");
- r = dkr_pull_start(pull, name, tag, local, arg_force);
+ r = dkr_pull_start(pull, name, reference, local, arg_force, DKR_PULL_V2);
if (r < 0)
return log_error_errno(r, "Failed to pull image: %m");
diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c
index f87a939168..911e2a178b 100644
--- a/src/journal-remote/journal-remote.c
+++ b/src/journal-remote/journal-remote.c
@@ -30,6 +30,7 @@
#include <getopt.h>
#include "sd-daemon.h"
+#include "signal-util.h"
#include "journal-file.h"
#include "journald-native.h"
#include "socket-util.h"
diff --git a/src/journal-remote/journal-upload.c b/src/journal-remote/journal-upload.c
index ddbb8731e2..ddb1ef0396 100644
--- a/src/journal-remote/journal-upload.c
+++ b/src/journal-remote/journal-upload.c
@@ -33,8 +33,9 @@
#include "mkdir.h"
#include "conf-parser.h"
#include "sigbus.h"
-#include "journal-upload.h"
#include "formats-util.h"
+#include "signal-util.h"
+#include "journal-upload.h"
#define PRIV_KEY_FILE CERTIFICATE_ROOT "/private/journal-upload.pem"
#define CERT_FILE CERTIFICATE_ROOT "/certs/journal-upload.pem"
diff --git a/src/journal/coredumpctl.c b/src/journal/coredumpctl.c
index bcb0ff9c39..381bf72776 100644
--- a/src/journal/coredumpctl.c
+++ b/src/journal/coredumpctl.c
@@ -39,6 +39,7 @@
#include "sigbus.h"
#include "process-util.h"
#include "terminal-util.h"
+#include "signal-util.h"
static enum {
ACTION_NONE,
diff --git a/src/journal/journald-audit.c b/src/journal/journald-audit.c
index 64395e1148..83c3332abf 100644
--- a/src/journal/journald-audit.c
+++ b/src/journal/journald-audit.c
@@ -534,9 +534,14 @@ int server_open_audit(Server *s) {
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");
+ if (bind(s->audit_fd, &sa.sa, sizeof(sa.nl)) < 0) {
+ log_warning_errno(errno,
+ "Failed to join audit multicast group. "
+ "The kernel is probably too old or multicast reading is not supported. "
+ "Ignoring: %m");
+ s->audit_fd = safe_close(s->audit_fd);
+ return 0;
+ }
} else
fd_nonblock(s->audit_fd, 1);
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index b3a4b53080..3353024f4e 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -25,6 +25,10 @@
#include <sys/statvfs.h>
#include <sys/mman.h>
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#endif
+
#include <libudev.h>
#include "sd-journal.h"
@@ -43,6 +47,7 @@
#include "formats-util.h"
#include "process-util.h"
#include "hostname-util.h"
+#include "signal-util.h"
#include "journal-internal.h"
#include "journal-vacuum.h"
#include "journal-authenticate.h"
@@ -54,10 +59,6 @@
#include "journald-audit.h"
#include "journald-server.h"
-#ifdef HAVE_SELINUX
-#include <selinux/selinux.h>
-#endif
-
#define USER_JOURNALS_MAX 1024
#define DEFAULT_SYNC_INTERVAL_USEC (5*USEC_PER_MINUTE)
diff --git a/src/kernel-install/90-loaderentry.install b/src/kernel-install/90-loaderentry.install
index d433e00a5c..4c9b1f0327 100644
--- a/src/kernel-install/90-loaderentry.install
+++ b/src/kernel-install/90-loaderentry.install
@@ -43,14 +43,14 @@ fi
declare -a BOOT_OPTIONS
if [[ -f /etc/kernel/cmdline ]]; then
- readarray -t BOOT_OPTIONS < /etc/kernel/cmdline
+ read -r -d '' -a BOOT_OPTIONS < /etc/kernel/cmdline
fi
if ! [[ ${BOOT_OPTIONS[*]} ]]; then
- read -a line -r < /proc/cmdline
+ read -r -d '' -a line < /proc/cmdline
for i in "${line[@]}"; do
[[ "${i#initrd=*}" != "$i" ]] && continue
- BOOT_OPTIONS[${#BOOT_OPTIONS[@]}]="$i"
+ BOOT_OPTIONS+=("$i")
done
fi
diff --git a/src/libsystemd-network/dhcp-identifier.c b/src/libsystemd-network/dhcp-identifier.c
index f7a1492363..70c68ad131 100644
--- a/src/libsystemd-network/dhcp-identifier.c
+++ b/src/libsystemd-network/dhcp-identifier.c
@@ -46,8 +46,9 @@ int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) {
if (r < 0)
return r;
- duid->type = htobe16(DHCP6_DUID_EN);
- duid->en.pen = htobe32(SYSTEMD_PEN);
+ unaligned_write_be16(&duid->type, DHCP6_DUID_EN);
+ unaligned_write_be32(&duid->en.pen, SYSTEMD_PEN);
+
*len = sizeof(duid->type) + sizeof(duid->en);
/* a bit of snake-oil perhaps, but no need to expose the machine-id
@@ -58,7 +59,7 @@ int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) {
}
-int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, uint32_t *_id) {
+int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_id) {
/* name is a pointer to memory in the udev_device struct, so must
have the same scope */
_cleanup_udev_device_unref_ struct udev_device *device = NULL;
@@ -92,7 +93,7 @@ int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, uint32_t
siphash24((uint8_t*)&id, mac, mac_len, HASH_KEY.bytes);
/* fold into 32 bits */
- *_id = (id & 0xffffffff) ^ (id >> 32);
+ unaligned_write_be32(_id, (id & 0xffffffff) ^ (id >> 32));
return 0;
}
diff --git a/src/libsystemd-network/dhcp-identifier.h b/src/libsystemd-network/dhcp-identifier.h
index 643d4970d5..95117915f4 100644
--- a/src/libsystemd-network/dhcp-identifier.h
+++ b/src/libsystemd-network/dhcp-identifier.h
@@ -24,6 +24,7 @@
#include "macro.h"
#include "sparse-endian.h"
+#include "unaligned.h"
#include "sd-id128.h"
/* RFC 3315 section 9.1:
@@ -61,4 +62,4 @@ struct duid {
} _packed_;
int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len);
-int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, uint32_t *_id);
+int dhcp_identifier_set_iaid(int ifindex, uint8_t *mac, size_t mac_len, void *_id);
diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c
index 4b8763aab0..8a4220621b 100644
--- a/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/libsystemd-network/sd-dhcp-lease.c
@@ -672,7 +672,7 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) {
if (r >= 0) {
_cleanup_free_ char *client_id_hex;
- client_id_hex = hexmem (client_id, client_id_len);
+ client_id_hex = hexmem(client_id, client_id_len);
if (!client_id_hex) {
r = -ENOMEM;
goto finish;
diff --git a/src/libsystemd-terminal/evcat.c b/src/libsystemd-terminal/evcat.c
index d274225ed0..bfa166c489 100644
--- a/src/libsystemd-terminal/evcat.c
+++ b/src/libsystemd-terminal/evcat.c
@@ -35,19 +35,20 @@
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
-#include <systemd/sd-login.h>
#include <termios.h>
#include <unistd.h>
#include <xkbcommon/xkbcommon.h>
+#include "sd-bus.h"
+#include "sd-event.h"
+#include "sd-login.h"
#include "build.h"
#include "event-util.h"
-#include "idev.h"
#include "macro.h"
+#include "signal-util.h"
+#include "util.h"
+#include "idev.h"
#include "sysview.h"
#include "term-internal.h"
-#include "util.h"
typedef struct Evcat Evcat;
diff --git a/src/libsystemd-terminal/grdev-drm.c b/src/libsystemd-terminal/grdev-drm.c
index 01a70fd320..4cee95f469 100644
--- a/src/libsystemd-terminal/grdev-drm.c
+++ b/src/libsystemd-terminal/grdev-drm.c
@@ -27,8 +27,6 @@
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/types.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
#include <unistd.h>
/* Yuck! DRM headers need system headers included first.. but we have to
@@ -37,12 +35,14 @@
#include <drm_fourcc.h>
#include <drm_mode.h>
-#include "bus-util.h"
+#include "sd-bus.h"
+#include "sd-event.h"
#include "hashmap.h"
-#include "grdev.h"
-#include "grdev-internal.h"
#include "macro.h"
#include "util.h"
+#include "bus-util.h"
+#include "grdev.h"
+#include "grdev-internal.h"
#define GRDRM_MAX_TRIES (16)
diff --git a/src/libsystemd-terminal/grdev-internal.h b/src/libsystemd-terminal/grdev-internal.h
index f455dd4172..46d65f0248 100644
--- a/src/libsystemd-terminal/grdev-internal.h
+++ b/src/libsystemd-terminal/grdev-internal.h
@@ -25,12 +25,12 @@
#include <libudev.h>
#include <stdbool.h>
#include <stdlib.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
-#include "grdev.h"
+#include "sd-bus.h"
+#include "sd-event.h"
#include "hashmap.h"
#include "list.h"
#include "util.h"
+#include "grdev.h"
typedef struct grdev_tile grdev_tile;
typedef struct grdev_display_cache grdev_display_cache;
diff --git a/src/libsystemd-terminal/grdev.c b/src/libsystemd-terminal/grdev.c
index feed579295..c386e65982 100644
--- a/src/libsystemd-terminal/grdev.c
+++ b/src/libsystemd-terminal/grdev.c
@@ -22,14 +22,14 @@
#include <libudev.h>
#include <stdbool.h>
#include <stdlib.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
-#include "grdev.h"
-#include "grdev-internal.h"
+#include "sd-bus.h"
+#include "sd-event.h"
#include "hashmap.h"
#include "login-shared.h"
#include "macro.h"
#include "util.h"
+#include "grdev.h"
+#include "grdev-internal.h"
static void pipe_enable(grdev_pipe *pipe);
static void pipe_disable(grdev_pipe *pipe);
diff --git a/src/libsystemd-terminal/grdev.h b/src/libsystemd-terminal/grdev.h
index db2a508fd8..110d24e6d5 100644
--- a/src/libsystemd-terminal/grdev.h
+++ b/src/libsystemd-terminal/grdev.h
@@ -56,8 +56,8 @@
#include <libudev.h>
#include <stdbool.h>
#include <stdlib.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
+#include "sd-bus.h"
+#include "sd-event.h"
#include "util.h"
typedef struct grdev_fb grdev_fb;
diff --git a/src/libsystemd-terminal/idev-evdev.c b/src/libsystemd-terminal/idev-evdev.c
index 64e703eb67..f1a18b91d3 100644
--- a/src/libsystemd-terminal/idev-evdev.c
+++ b/src/libsystemd-terminal/idev-evdev.c
@@ -24,13 +24,13 @@
#include <libudev.h>
#include <stdbool.h>
#include <stdlib.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
+#include "sd-bus.h"
+#include "sd-event.h"
+#include "macro.h"
+#include "util.h"
#include "bus-util.h"
#include "idev.h"
#include "idev-internal.h"
-#include "macro.h"
-#include "util.h"
typedef struct idev_evdev idev_evdev;
typedef struct unmanaged_evdev unmanaged_evdev;
diff --git a/src/libsystemd-terminal/idev-internal.h b/src/libsystemd-terminal/idev-internal.h
index a159aef211..a02a16c408 100644
--- a/src/libsystemd-terminal/idev-internal.h
+++ b/src/libsystemd-terminal/idev-internal.h
@@ -26,13 +26,13 @@
#include <linux/input.h>
#include <stdbool.h>
#include <stdlib.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
#include <xkbcommon/xkbcommon.h>
+#include "sd-bus.h"
+#include "sd-event.h"
#include "hashmap.h"
-#include "idev.h"
#include "list.h"
#include "util.h"
+#include "idev.h"
typedef struct idev_link idev_link;
typedef struct idev_device_vtable idev_device_vtable;
diff --git a/src/libsystemd-terminal/idev-keyboard.c b/src/libsystemd-terminal/idev-keyboard.c
index ef56ee2482..93f49e9458 100644
--- a/src/libsystemd-terminal/idev-keyboard.c
+++ b/src/libsystemd-terminal/idev-keyboard.c
@@ -21,17 +21,17 @@
#include <stdbool.h>
#include <stdlib.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-compose.h>
-#include "bus-util.h"
+#include "sd-bus.h"
+#include "sd-event.h"
#include "hashmap.h"
+#include "macro.h"
+#include "util.h"
+#include "bus-util.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;
diff --git a/src/libsystemd-terminal/idev.c b/src/libsystemd-terminal/idev.c
index 0ba2b28ab7..b187934977 100644
--- a/src/libsystemd-terminal/idev.c
+++ b/src/libsystemd-terminal/idev.c
@@ -22,14 +22,14 @@
#include <libudev.h>
#include <stdbool.h>
#include <stdlib.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
+#include "sd-bus.h"
+#include "sd-event.h"
#include "hashmap.h"
-#include "idev.h"
-#include "idev-internal.h"
#include "login-shared.h"
#include "macro.h"
#include "util.h"
+#include "idev.h"
+#include "idev-internal.h"
static void element_open(idev_element *e);
static void element_close(idev_element *e);
diff --git a/src/libsystemd-terminal/idev.h b/src/libsystemd-terminal/idev.h
index 0e846179e6..241677cbbe 100644
--- a/src/libsystemd-terminal/idev.h
+++ b/src/libsystemd-terminal/idev.h
@@ -28,9 +28,9 @@
#include <libudev.h>
#include <linux/input.h>
#include <stdbool.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
#include <xkbcommon/xkbcommon.h>
+#include "sd-bus.h"
+#include "sd-event.h"
typedef struct idev_data idev_data;
typedef struct idev_data_evdev idev_data_evdev;
diff --git a/src/libsystemd-terminal/modeset.c b/src/libsystemd-terminal/modeset.c
index 621d6c4fa3..f3a60e1fb0 100644
--- a/src/libsystemd-terminal/modeset.c
+++ b/src/libsystemd-terminal/modeset.c
@@ -35,18 +35,18 @@
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
-#include <systemd/sd-login.h>
#include <termios.h>
#include <unistd.h>
-
+#include "sd-bus.h"
+#include "sd-event.h"
+#include "sd-login.h"
#include "build.h"
-#include "grdev.h"
#include "macro.h"
-#include "sysview.h"
-#include "util.h"
#include "random-util.h"
+#include "signal-util.h"
+#include "util.h"
+#include "grdev.h"
+#include "sysview.h"
typedef struct Modeset Modeset;
diff --git a/src/libsystemd-terminal/subterm.c b/src/libsystemd-terminal/subterm.c
index 983a2a14ab..d10e2f549f 100644
--- a/src/libsystemd-terminal/subterm.c
+++ b/src/libsystemd-terminal/subterm.c
@@ -34,13 +34,14 @@
#include <string.h>
#include <sys/ioctl.h>
#include <termios.h>
+#include "sd-event.h"
#include "macro.h"
#include "pty.h"
#include "ring.h"
-#include "sd-event.h"
-#include "term-internal.h"
-#include "util.h"
+#include "signal-util.h"
#include "utf8.h"
+#include "util.h"
+#include "term-internal.h"
typedef struct Output Output;
typedef struct Terminal Terminal;
diff --git a/src/libsystemd-terminal/sysview-internal.h b/src/libsystemd-terminal/sysview-internal.h
index f1fd4b5f53..251c8d7300 100644
--- a/src/libsystemd-terminal/sysview-internal.h
+++ b/src/libsystemd-terminal/sysview-internal.h
@@ -25,13 +25,13 @@
#include <libudev.h>
#include <stdbool.h>
#include <stdlib.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
+#include "sd-bus.h"
+#include "sd-event.h"
#include "hashmap.h"
#include "list.h"
#include "macro.h"
-#include "sysview.h"
#include "util.h"
+#include "sysview.h"
/*
* Devices
diff --git a/src/libsystemd-terminal/sysview.c b/src/libsystemd-terminal/sysview.c
index 1e13167a79..c8bbce43d3 100644
--- a/src/libsystemd-terminal/sysview.c
+++ b/src/libsystemd-terminal/sysview.c
@@ -23,15 +23,15 @@
#include <libudev.h>
#include <stdbool.h>
#include <stdlib.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
-#include <systemd/sd-login.h>
-#include "bus-util.h"
+#include "sd-bus.h"
+#include "sd-event.h"
+#include "sd-login.h"
#include "macro.h"
-#include "sysview.h"
-#include "sysview-internal.h"
#include "udev-util.h"
#include "util.h"
+#include "bus-util.h"
+#include "sysview.h"
+#include "sysview-internal.h"
static int context_raise_session_control(sysview_context *c, sysview_session *session, int error);
diff --git a/src/libsystemd-terminal/sysview.h b/src/libsystemd-terminal/sysview.h
index 71e56e7ebf..a5e7a38df3 100644
--- a/src/libsystemd-terminal/sysview.h
+++ b/src/libsystemd-terminal/sysview.h
@@ -37,8 +37,8 @@
#pragma once
#include <stdbool.h>
-#include <systemd/sd-bus.h>
-#include <systemd/sd-event.h>
+#include "sd-bus.h"
+#include "sd-event.h"
typedef struct sysview_event sysview_event;
typedef struct sysview_device sysview_device;
diff --git a/src/libsystemd/libsystemd.sym.m4 b/src/libsystemd/libsystemd.sym.m4
index b443c3cfba..0b94a87dd6 100644
--- a/src/libsystemd/libsystemd.sym.m4
+++ b/src/libsystemd/libsystemd.sym.m4
@@ -169,8 +169,7 @@ global:
sd_peer_get_user_slice;
} LIBSYSTEMD_219;
-m4_ifdef(`ENABLE_KDBUS',
-LIBSYSTEMD_FUTURE {
+LIBSYSTEMD_221 {
global:
/* sd-bus */
sd_bus_default;
@@ -185,27 +184,36 @@ global:
sd_bus_set_address;
sd_bus_set_fd;
sd_bus_set_exec;
+ sd_bus_get_address;
sd_bus_set_bus_client;
+ sd_bus_is_bus_client;
sd_bus_set_server;
+ sd_bus_is_server;
sd_bus_set_anonymous;
+ sd_bus_is_anonymous;
sd_bus_set_trusted;
+ sd_bus_is_trusted;
sd_bus_set_monitor;
+ sd_bus_is_monitor;
sd_bus_set_description;
+ sd_bus_get_description;
+ sd_bus_negotiate_creds;
+ sd_bus_negotiate_timestamp;
+ sd_bus_negotiate_fds;
+ sd_bus_can_send;
+ sd_bus_get_creds_mask;
sd_bus_set_allow_interactive_authorization;
sd_bus_get_allow_interactive_authorization;
- sd_bus_negotiate_fds;
- sd_bus_negotiate_timestamp;
- sd_bus_negotiate_creds;
sd_bus_start;
sd_bus_close;
sd_bus_try_close;
sd_bus_ref;
sd_bus_unref;
sd_bus_is_open;
- sd_bus_can_send;
sd_bus_get_bus_id;
+ sd_bus_get_scope;
+ sd_bus_get_tid;
sd_bus_get_owner_creds;
- sd_bus_get_description;
sd_bus_send;
sd_bus_send_to;
sd_bus_call;
@@ -217,9 +225,10 @@ global:
sd_bus_process_priority;
sd_bus_wait;
sd_bus_flush;
- sd_bus_get_current_message;
sd_bus_get_current_slot;
- sd_bus_get_tid;
+ sd_bus_get_current_message;
+ sd_bus_get_current_handler;
+ sd_bus_get_current_userdata;
sd_bus_attach_event;
sd_bus_detach_event;
sd_bus_get_event;
@@ -239,6 +248,8 @@ global:
sd_bus_slot_get_description;
sd_bus_slot_set_description;
sd_bus_slot_get_current_message;
+ sd_bus_slot_get_current_handler;
+ sd_bus_slot_get_current_userdata;
sd_bus_message_new_signal;
sd_bus_message_new_method_call;
sd_bus_message_new_method_return;
@@ -248,13 +259,13 @@ global:
sd_bus_message_new_method_errnof;
sd_bus_message_ref;
sd_bus_message_unref;
- sd_bus_message_get_bus;
sd_bus_message_get_type;
sd_bus_message_get_cookie;
sd_bus_message_get_reply_cookie;
+ sd_bus_message_get_priority;
sd_bus_message_get_expect_reply;
sd_bus_message_get_auto_start;
- sd_bus_message_get_priority;
+ sd_bus_message_get_allow_interactive_authorization;
sd_bus_message_get_signature;
sd_bus_message_get_path;
sd_bus_message_get_interface;
@@ -266,13 +277,16 @@ global:
sd_bus_message_get_monotonic_usec;
sd_bus_message_get_realtime_usec;
sd_bus_message_get_seqnum;
+ sd_bus_message_get_bus;
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;
+ sd_bus_message_is_empty;
+ sd_bus_message_has_signature;
sd_bus_message_set_expect_reply;
sd_bus_message_set_auto_start;
+ sd_bus_message_set_allow_interactive_authorization;
sd_bus_message_set_destination;
sd_bus_message_set_priority;
sd_bus_message_append;
@@ -324,6 +338,7 @@ global:
sd_bus_emit_interfaces_removed_strv;
sd_bus_emit_interfaces_removed;
sd_bus_query_sender_creds;
+ sd_bus_query_sender_privilege;
sd_bus_creds_new_from_pid;
sd_bus_creds_ref;
sd_bus_creds_unref;
@@ -374,6 +389,7 @@ global:
sd_bus_error_copy;
sd_bus_error_is_set;
sd_bus_error_has_name;
+ sd_bus_error_add_map;
sd_bus_path_encode;
sd_bus_path_decode;
sd_bus_track_new;
@@ -401,9 +417,10 @@ global:
sd_event_add_signal;
sd_event_add_child;
sd_event_add_defer;
+ sd_event_add_post;
sd_event_add_exit;
- sd_event_wait;
sd_event_prepare;
+ sd_event_wait;
sd_event_dispatch;
sd_event_run;
sd_event_loop;
@@ -417,6 +434,9 @@ global:
sd_event_get_watchdog;
sd_event_source_ref;
sd_event_source_unref;
+ sd_event_source_get_event;
+ sd_event_source_get_userdata;
+ sd_event_source_set_userdata;
sd_event_source_set_description;
sd_event_source_get_description;
sd_event_source_set_prepare;
@@ -425,8 +445,6 @@ global:
sd_event_source_set_priority;
sd_event_source_get_enabled;
sd_event_source_set_enabled;
- sd_event_source_get_userdata;
- sd_event_source_set_userdata;
sd_event_source_get_io_fd;
sd_event_source_set_io_fd;
sd_event_source_get_io_events;
@@ -439,8 +457,11 @@ global:
sd_event_source_get_time_clock;
sd_event_source_get_signal;
sd_event_source_get_child_pid;
- sd_event_source_get_event;
+} LIBSYSTEMD_220;
+m4_ifdef(`ENABLE_KDBUS',
+LIBSYSTEMD_FUTURE {
+global:
/* sd-utf8 */
sd_utf8_is_valid;
sd_ascii_is_valid;
diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c
index fa4c28174d..43ddfc651d 100644
--- a/src/libsystemd/sd-bus/bus-control.c
+++ b/src/libsystemd/sd-bus/bus-control.c
@@ -429,7 +429,7 @@ static int bus_populate_creds_from_items(
c->mask |= SD_BUS_CREDS_PPID;
} else if (item->pids.pid == 1) {
/* The structure doesn't
- * really distuingish the case
+ * really distinguish the case
* where a process has no
* parent and where we don't
* know it because it could
diff --git a/src/libsystemd/sd-bus/bus-creds.c b/src/libsystemd/sd-bus/bus-creds.c
index fed66823c7..4d67619cf8 100644
--- a/src/libsystemd/sd-bus/bus-creds.c
+++ b/src/libsystemd/sd-bus/bus-creds.c
@@ -303,7 +303,7 @@ _public_ int sd_bus_creds_get_ppid(sd_bus_creds *c, pid_t *ppid) {
if (!(c->mask & SD_BUS_CREDS_PPID))
return -ENODATA;
- /* PID 1 has no parent process. Let's distuingish the case of
+ /* PID 1 has no parent process. Let's distinguish the case of
* not knowing and not having a parent process by the returned
* error code. */
if (c->ppid == 0)
@@ -989,7 +989,7 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
if (missing & SD_BUS_CREDS_EXE) {
r = get_process_exe(pid, &c->exe);
if (r == -ESRCH) {
- /* Unfortunately we cannot really distuingish
+ /* Unfortunately we cannot really distinguish
* the case here where the process does not
* exist, and /proc/$PID/exe being unreadable
* because $PID is a kernel thread. Hence,
@@ -1101,7 +1101,7 @@ int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
}
/* In case only the exe path was to be read we cannot
- * distuingish the case where the exe path was unreadable
+ * distinguish the case where the exe path was unreadable
* because the process was a kernel thread, or when the
* process didn't exist at all. Hence, let's do a final check,
* to be sure. */
diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c
index 24b9c4d117..4fffc6581d 100644
--- a/src/libsystemd/sd-bus/bus-socket.c
+++ b/src/libsystemd/sd-bus/bus-socket.c
@@ -24,12 +24,13 @@
#include <unistd.h>
#include <poll.h>
+#include "sd-daemon.h"
#include "util.h"
#include "macro.h"
#include "missing.h"
#include "utf8.h"
-#include "sd-daemon.h"
#include "formats-util.h"
+#include "signal-util.h"
#include "sd-bus.h"
#include "bus-socket.h"
@@ -492,7 +493,7 @@ static int bus_socket_auth_verify(sd_bus *b) {
static int bus_socket_read_auth(sd_bus *b) {
struct msghdr mh;
- struct iovec iov;
+ struct iovec iov = {};
size_t n;
ssize_t k;
int r;
@@ -527,7 +528,6 @@ static int bus_socket_read_auth(sd_bus *b) {
b->rbuffer = p;
- zero(iov);
iov.iov_base = (uint8_t*) b->rbuffer + b->rbuffer_size;
iov.iov_len = n - b->rbuffer_size;
@@ -808,23 +808,21 @@ int bus_socket_write_message(sd_bus *bus, sd_bus_message *m, size_t *idx) {
if (bus->prefer_writev)
k = writev(bus->output_fd, iov, m->n_iovec);
else {
- struct msghdr mh;
- zero(mh);
+ struct msghdr mh = {
+ .msg_iov = iov,
+ .msg_iovlen = m->n_iovec,
+ };
if (m->n_fds > 0) {
struct cmsghdr *control;
- control = alloca(CMSG_SPACE(sizeof(int) * m->n_fds));
- mh.msg_control = control;
+ mh.msg_control = control = alloca(CMSG_SPACE(sizeof(int) * m->n_fds));
+ mh.msg_controllen = control->cmsg_len = CMSG_LEN(sizeof(int) * m->n_fds);
control->cmsg_level = SOL_SOCKET;
control->cmsg_type = SCM_RIGHTS;
- mh.msg_controllen = control->cmsg_len = CMSG_LEN(sizeof(int) * m->n_fds);
memcpy(CMSG_DATA(control), m->fds, sizeof(int) * m->n_fds);
}
- mh.msg_iov = iov;
- mh.msg_iovlen = m->n_iovec;
-
k = sendmsg(bus->output_fd, &mh, MSG_DONTWAIT|MSG_NOSIGNAL);
if (k < 0 && errno == ENOTSOCK) {
bus->prefer_writev = true;
@@ -936,7 +934,7 @@ static int bus_socket_make_message(sd_bus *bus, size_t size) {
int bus_socket_read_message(sd_bus *bus) {
struct msghdr mh;
- struct iovec iov;
+ struct iovec iov = {};
ssize_t k;
size_t need;
int r;
@@ -966,7 +964,6 @@ int bus_socket_read_message(sd_bus *bus) {
bus->rbuffer = b;
- zero(iov);
iov.iov_base = (uint8_t*) bus->rbuffer + bus->rbuffer_size;
iov.iov_len = need - bus->rbuffer_size;
diff --git a/src/libsystemd/sd-bus/bus-util.c b/src/libsystemd/sd-bus/bus-util.c
index 5e375af206..99937799b3 100644
--- a/src/libsystemd/sd-bus/bus-util.c
+++ b/src/libsystemd/sd-bus/bus-util.c
@@ -30,6 +30,7 @@
#include "path-util.h"
#include "missing.h"
#include "set.h"
+#include "signal-util.h"
#include "unit-name.h"
#include "sd-bus.h"
diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c
index 214b3d04df..edc27aef87 100644
--- a/src/libsystemd/sd-bus/sd-bus.c
+++ b/src/libsystemd/sd-bus/sd-bus.c
@@ -3513,7 +3513,7 @@ _public_ int sd_bus_get_address(sd_bus *bus, const char **address) {
return -ENODATA;
}
-int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *mask) {
+_public_ 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);
@@ -3522,35 +3522,35 @@ int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *mask) {
return 0;
}
-int sd_bus_is_bus_client(sd_bus *bus) {
+_public_ 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) {
+_public_ 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) {
+_public_ 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) {
+_public_ 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) {
+_public_ int sd_bus_is_monitor(sd_bus *bus) {
assert_return(bus, -EINVAL);
assert_return(!bus_pid_changed(bus), -ECHILD);
diff --git a/src/libsystemd/sd-device/device-enumerator.c b/src/libsystemd/sd-device/device-enumerator.c
index 4de36d54e1..3692d46e06 100644
--- a/src/libsystemd/sd-device/device-enumerator.c
+++ b/src/libsystemd/sd-device/device-enumerator.c
@@ -367,11 +367,11 @@ static bool match_sysattr(sd_device_enumerator *enumerator, sd_device *device) {
assert(enumerator);
assert(device);
- HASHMAP_FOREACH_KEY(sysattr, value, enumerator->nomatch_sysattr, i)
+ HASHMAP_FOREACH_KEY(value, sysattr, enumerator->nomatch_sysattr, i)
if (match_sysattr_value(device, sysattr, value))
return false;
- HASHMAP_FOREACH_KEY(sysattr, value, enumerator->match_sysattr, i)
+ HASHMAP_FOREACH_KEY(value, sysattr, enumerator->match_sysattr, i)
if (!match_sysattr_value(device, sysattr, value))
return false;
@@ -389,7 +389,7 @@ static bool match_property(sd_device_enumerator *enumerator, sd_device *device)
if (hashmap_isempty(enumerator->match_property))
return true;
- HASHMAP_FOREACH_KEY(property, value, enumerator->match_property, i) {
+ HASHMAP_FOREACH_KEY(value, property, enumerator->match_property, i) {
const char *property_dev, *value_dev;
FOREACH_DEVICE_PROPERTY(device, property_dev, value_dev) {
@@ -764,9 +764,9 @@ static int parent_crawl_children(sd_device_enumerator *enumerator, const char *p
if (dent->d_type != DT_DIR)
continue;
- k = asprintf(&child, "%s/%s", path, dent->d_name);
- if (k < 0)
- return -errno;
+ child = strjoin(path, "/", dent->d_name, NULL);
+ if (!child)
+ return -ENOMEM;
k = parent_add_child(enumerator, child);
if (k < 0)
diff --git a/src/libsystemd/sd-device/device-private.c b/src/libsystemd/sd-device/device-private.c
index 3cadedbf4a..2e60433246 100644
--- a/src/libsystemd/sd-device/device-private.c
+++ b/src/libsystemd/sd-device/device-private.c
@@ -636,10 +636,10 @@ int device_new_from_nulstr(sd_device **ret, uint8_t *nulstr, size_t len) {
static int device_update_properties_bufs(sd_device *device) {
const char *val, *prop;
- char **buf_strv = NULL;
- uint8_t *buf_nulstr = NULL;
- size_t allocated_nulstr = 0, allocated_strv = 0;
- size_t nulstr_len = 0, strv_size = 0;
+ _cleanup_free_ char **buf_strv = NULL;
+ _cleanup_free_ uint8_t *buf_nulstr = NULL;
+ size_t allocated_nulstr = 0;
+ size_t nulstr_len = 0, num = 0, i = 0;
assert(device);
@@ -655,20 +655,29 @@ static int device_update_properties_bufs(sd_device *device) {
if (!buf_nulstr)
return -ENOMEM;
- buf_strv = GREEDY_REALLOC0(buf_strv, allocated_strv, strv_size + 2);
- if (!buf_strv)
- return -ENOMEM;
-
- buf_strv[++ strv_size] = (char *)&buf_nulstr[nulstr_len];
strscpyl((char *)buf_nulstr + nulstr_len, len + 1, prop, "=", val, NULL);
nulstr_len += len + 1;
+ ++num;
+ }
+
+ /* build buf_strv from buf_nulstr */
+ buf_strv = new0(char *, num + 1);
+ if (!buf_strv)
+ return -ENOMEM;
+
+ NULSTR_FOREACH(val, (char*) buf_nulstr) {
+ buf_strv[i] = (char *) val;
+ assert(i < num);
+ i++;
}
free(device->properties_nulstr);
- free(device->properties_strv);
device->properties_nulstr = buf_nulstr;
+ buf_nulstr = NULL;
device->properties_nulstr_len = nulstr_len;
+ free(device->properties_strv);
device->properties_strv = buf_strv;
+ buf_strv = NULL;
device->properties_buf_outdated = false;
diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c
index 691b9c6327..97da4a8eea 100644
--- a/src/libsystemd/sd-device/sd-device.c
+++ b/src/libsystemd/sd-device/sd-device.c
@@ -1193,12 +1193,12 @@ int device_get_id_filename(sd_device *device, const char **ret) {
streq(subsystem, "block") ? 'b' : 'c',
major(devnum), minor(devnum));
if (r < 0)
- return -errno;
+ return -ENOMEM;
} else if (ifindex > 0) {
/* use netdev ifindex -- n3 */
r = asprintf(&id, "n%u", ifindex);
if (r < 0)
- return -errno;
+ return -ENOMEM;
} else {
/* use $subsys:$sysname -- pci:0000:00:1f.2
* sysname() has '!' translated, get it from devpath
@@ -1211,7 +1211,7 @@ int device_get_id_filename(sd_device *device, const char **ret) {
r = asprintf(&id, "+%s:%s", subsystem, sysname);
if (r < 0)
- return -errno;
+ return -ENOMEM;
}
device->id_filename = id;
diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c
index 0dc4680376..cc8bc50c04 100644
--- a/src/libsystemd/sd-event/sd-event.c
+++ b/src/libsystemd/sd-event/sd-event.c
@@ -33,6 +33,7 @@
#include "missing.h"
#include "set.h"
#include "list.h"
+#include "signal-util.h"
#include "sd-event.h"
diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c
index 721700be7b..94e98e0077 100644
--- a/src/libsystemd/sd-event/test-event.c
+++ b/src/libsystemd/sd-event/test-event.c
@@ -23,6 +23,7 @@
#include "log.h"
#include "util.h"
#include "macro.h"
+#include "signal-util.h"
static int prepare_handler(sd_event_source *s, void *userdata) {
log_info("preparing %c", PTR_TO_INT(userdata));
diff --git a/src/login/loginctl.c b/src/login/loginctl.c
index 4a5a618472..02d240c704 100644
--- a/src/login/loginctl.c
+++ b/src/login/loginctl.c
@@ -43,6 +43,7 @@
#include "verbs.h"
#include "process-util.h"
#include "terminal-util.h"
+#include "signal-util.h"
static char **arg_property = NULL;
static bool arg_all = false;
diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c
index 1f5cf865b1..10a9df0961 100644
--- a/src/login/logind-dbus.c
+++ b/src/login/logind-dbus.c
@@ -1964,6 +1964,11 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd
m->scheduled_shutdown_type = NULL;
m->scheduled_shutdown_timeout = 0;
+ if (m->unlink_nologin) {
+ (void) unlink("/run/nologin");
+ m->unlink_nologin = false;
+ }
+
if (cancelled) {
_cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
const char *tty = NULL;
diff --git a/src/login/logind-user.c b/src/login/logind-user.c
index 71bff96728..dc3db9abda 100644
--- a/src/login/logind-user.c
+++ b/src/login/logind-user.c
@@ -320,7 +320,7 @@ static int user_mkdir_runtime_path(User *u) {
} else
p = u->runtime_path;
- if (path_is_mount_point(p, false) <= 0) {
+ if (path_is_mount_point(p, 0) <= 0) {
_cleanup_free_ char *t = NULL;
(void) mkdir(p, 0700);
diff --git a/src/login/logind.c b/src/login/logind.c
index 7520f1345b..00f8dbdab2 100644
--- a/src/login/logind.c
+++ b/src/login/logind.c
@@ -30,9 +30,10 @@
#include "conf-parser.h"
#include "bus-util.h"
#include "bus-error.h"
-#include "logind.h"
#include "udev-util.h"
#include "formats-util.h"
+#include "signal-util.h"
+#include "logind.h"
static void manager_free(Manager *m);
@@ -170,7 +171,7 @@ static void manager_free(Manager *m) {
udev_unref(m->udev);
if (m->unlink_nologin)
- unlink("/run/nologin");
+ (void) unlink("/run/nologin");
bus_verify_polkit_async_registry_free(m->polkit_registry);
diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
index b21a33941a..c86c36c2de 100644
--- a/src/machine/machinectl.c
+++ b/src/machine/machinectl.c
@@ -54,6 +54,7 @@
#include "import-util.h"
#include "process-util.h"
#include "terminal-util.h"
+#include "signal-util.h"
static char **arg_property = NULL;
static bool arg_all = false;
diff --git a/src/machine/machined.c b/src/machine/machined.c
index 1e862ad8f2..754c770040 100644
--- a/src/machine/machined.c
+++ b/src/machine/machined.c
@@ -28,9 +28,10 @@
#include "bus-util.h"
#include "bus-error.h"
#include "label.h"
+#include "formats-util.h"
+#include "signal-util.h"
#include "machine-image.h"
#include "machined.h"
-#include "formats-util.h"
Manager *manager_new(void) {
Manager *m;
diff --git a/src/network/networkctl.c b/src/network/networkctl.c
index 69b4ab4a5c..3454394977 100644
--- a/src/network/networkctl.c
+++ b/src/network/networkctl.c
@@ -62,7 +62,7 @@ static int link_get_type_string(int iftype, sd_device *d, char **ret) {
assert(ret);
if (iftype == ARPHRD_ETHER && d) {
- const char *devtype, *id = NULL;
+ const char *devtype = NULL, *id = NULL;
/* WLANs have iftype ARPHRD_ETHER, but we want
* to show a more useful type string for
* them */
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 8b0de1f741..f039a2d687 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -1010,27 +1010,30 @@ static int link_up(Link *link) {
if (r < 0)
return log_link_error_errno(link, r, "Could not open IFLA_AF_SPEC container: %m");
- r = sd_rtnl_message_open_container(req, AF_INET6);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not open AF_INET6 container: %m");
+ if (socket_ipv6_is_supported()) {
+ /* if the kernel lacks ipv6 support setting IFF_UP fails if any ipv6 options are passed */
+ r = sd_rtnl_message_open_container(req, AF_INET6);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not open AF_INET6 container: %m");
- ipv6ll_mode = link_ipv6ll_enabled(link) ? IN6_ADDR_GEN_MODE_EUI64 : IN6_ADDR_GEN_MODE_NONE;
- r = sd_rtnl_message_append_u8(req, IFLA_INET6_ADDR_GEN_MODE, ipv6ll_mode);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not append IFLA_INET6_ADDR_GEN_MODE: %m");
+ ipv6ll_mode = link_ipv6ll_enabled(link) ? IN6_ADDR_GEN_MODE_EUI64 : IN6_ADDR_GEN_MODE_NONE;
+ r = sd_rtnl_message_append_u8(req, IFLA_INET6_ADDR_GEN_MODE, ipv6ll_mode);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFLA_INET6_ADDR_GEN_MODE: %m");
+
+ if (!in_addr_is_null(AF_INET6, &link->network->ipv6_token)) {
+ r = sd_rtnl_message_append_in6_addr(req, IFLA_INET6_TOKEN, &link->network->ipv6_token.in6);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not append IFLA_INET6_TOKEN: %m");
+ }
- if (!in_addr_is_null(AF_INET6, &link->network->ipv6_token)) {
- r = sd_rtnl_message_append_in6_addr(req, IFLA_INET6_TOKEN, &link->network->ipv6_token.in6);
+ r = sd_rtnl_message_close_container(req);
if (r < 0)
- return log_link_error_errno(link, r, "Could not append IFLA_INET6_TOKEN: %m");
+ return log_link_error_errno(link, r, "Could not close AF_INET6 container: %m");
}
r = sd_rtnl_message_close_container(req);
if (r < 0)
- return log_link_error_errno(link, r, "Could not close AF_INET6 container: %m");
-
- r = sd_rtnl_message_close_container(req);
- if (r < 0)
return log_link_error_errno(link, r, "Could not close IFLA_AF_SPEC container: %m");
r = sd_rtnl_call_async(link->manager->rtnl, req, link_up_handler, link, 0, NULL);
diff --git a/src/network/networkd-wait-online.c b/src/network/networkd-wait-online.c
index f0ca6def87..6a96f1de55 100644
--- a/src/network/networkd-wait-online.c
+++ b/src/network/networkd-wait-online.c
@@ -21,11 +21,10 @@
#include <getopt.h>
#include "sd-daemon.h"
-
-#include "networkd-wait-online.h"
-
#include "strv.h"
#include "build.h"
+#include "signal-util.h"
+#include "networkd-wait-online.h"
static bool arg_quiet = false;
static usec_t arg_timeout = 120 * USEC_PER_SEC;
diff --git a/src/network/networkd.c b/src/network/networkd.c
index 543a4e4d95..41ec7cf904 100644
--- a/src/network/networkd.c
+++ b/src/network/networkd.c
@@ -19,9 +19,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include "capability.h"
#include "sd-daemon.h"
-
+#include "capability.h"
+#include "signal-util.h"
#include "networkd.h"
int main(int argc, char *argv[]) {
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index a38f47dd0a..4211a3d779 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -96,6 +96,7 @@
#include "process-util.h"
#include "terminal-util.h"
#include "hostname-util.h"
+#include "signal-util.h"
#ifdef HAVE_SECCOMP
#include "seccomp-util.h"
@@ -195,7 +196,7 @@ static char **arg_network_macvlan = NULL;
static char **arg_network_ipvlan = NULL;
static bool arg_network_veth = false;
static const char *arg_network_bridge = NULL;
-static unsigned long arg_personality = 0xffffffffLU;
+static unsigned long arg_personality = PERSONALITY_INVALID;
static char *arg_image = NULL;
static Volatile arg_volatile = VOLATILE_NO;
static ExposePort *arg_expose_ports = NULL;
@@ -222,6 +223,8 @@ static void help(void) {
" --uuid=UUID Set a specific machine UUID for the container\n"
" -S --slice=SLICE Place the container in the specified slice\n"
" --property=NAME=VALUE Set scope unit property\n"
+ " --private-users[=UIDBASE[:NUIDS]]\n"
+ " Run within user namespace\n"
" --private-network Disable network in container\n"
" --network-interface=INTERFACE\n"
" Assign an existing network interface to the\n"
@@ -238,8 +241,6 @@ static void help(void) {
" Add a virtual ethernet connection between host\n"
" and container and add it to an existing bridge on\n"
" the host\n"
- " --private-users[=UIDBASE[:NUIDS]]\n"
- " Run within user namespace\n"
" -p --port=[PROTOCOL:]HOSTPORT[:CONTAINERPORT]\n"
" Expose a container IP port on the host\n"
" -Z --selinux-context=SECLABEL\n"
@@ -823,7 +824,7 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_PERSONALITY:
arg_personality = personality_from_string(optarg);
- if (arg_personality == 0xffffffffLU) {
+ if (arg_personality == PERSONALITY_INVALID) {
log_error("Unknown or unsupported personality '%s'.", optarg);
return -EINVAL;
}
@@ -1020,7 +1021,44 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
-static int mount_all(const char *dest) {
+static int tmpfs_patch_options(const char *options, char **ret) {
+ char *buf = NULL;
+
+ if (arg_userns && arg_uid_shift != 0) {
+
+ if (options)
+ (void) asprintf(&buf, "%s,uid=" UID_FMT ",gid=" UID_FMT, options, arg_uid_shift, arg_uid_shift);
+ else
+ (void) asprintf(&buf, "uid=" UID_FMT ",gid=" UID_FMT, arg_uid_shift, arg_uid_shift);
+ if (!buf)
+ return -ENOMEM;
+
+ options = buf;
+ }
+
+#ifdef HAVE_SELINUX
+ if (arg_selinux_apifs_context) {
+ char *t;
+
+ if (options)
+ t = strjoin(options, ",context=\"", arg_selinux_apifs_context, "\"", NULL);
+ else
+ t = strjoin("context=\"", arg_selinux_apifs_context, "\"", NULL);
+ if (!t) {
+ free(buf);
+ return -ENOMEM;
+ }
+
+ free(buf);
+ buf = t;
+ }
+#endif
+
+ *ret = buf;
+ return !!buf;
+}
+
+static int mount_all(const char *dest, bool userns) {
typedef struct MountPoint {
const char *what;
@@ -1029,88 +1067,63 @@ static int mount_all(const char *dest) {
const char *options;
unsigned long flags;
bool fatal;
+ bool userns;
} MountPoint;
static const MountPoint mount_table[] = {
- { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true },
- { "/proc/sys", "/proc/sys", NULL, NULL, MS_BIND, true }, /* Bind mount first */
- { NULL, "/proc/sys", NULL, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, true }, /* Then, make it r/o */
- { "sysfs", "/sys", "sysfs", NULL, MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, true },
- { "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, true },
- { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, true },
- { "devpts", "/dev/pts", "devpts", "newinstance,ptmxmode=0666,mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC, true },
- { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true },
- { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true },
- { "tmpfs", "/tmp", "tmpfs", "mode=1777", MS_STRICTATIME, true },
+ { "proc", "/proc", "proc", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, true, true },
+ { "/proc/sys", "/proc/sys", NULL, NULL, MS_BIND, true, true }, /* Bind mount first */
+ { NULL, "/proc/sys", NULL, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, true, true }, /* Then, make it r/o */
+ { "sysfs", "/sys", "sysfs", NULL, MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV, true, false },
+ { "tmpfs", "/sys/fs/cgroup", "tmpfs", "mode=755", MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, true, false },
+ { "tmpfs", "/dev", "tmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME, true, false },
+ { "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true, false },
+ { "tmpfs", "/run", "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true, false },
+ { "tmpfs", "/tmp", "tmpfs", "mode=1777", MS_STRICTATIME, true, false },
#ifdef HAVE_SELINUX
- { "/sys/fs/selinux", "/sys/fs/selinux", NULL, NULL, MS_BIND, false }, /* Bind mount first */
- { NULL, "/sys/fs/selinux", NULL, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, false }, /* Then, make it r/o */
+ { "/sys/fs/selinux", "/sys/fs/selinux", NULL, NULL, MS_BIND, false, false }, /* Bind mount first */
+ { NULL, "/sys/fs/selinux", NULL, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, false, false }, /* Then, make it r/o */
#endif
};
unsigned k;
- int r = 0;
+ int r;
for (k = 0; k < ELEMENTSOF(mount_table); k++) {
_cleanup_free_ char *where = NULL, *options = NULL;
const char *o;
- int t;
- where = strjoin(dest, "/", mount_table[k].where, NULL);
+ if (userns != mount_table[k].userns)
+ continue;
+
+ where = prefix_root(dest, mount_table[k].where);
if (!where)
return log_oom();
- t = path_is_mount_point(where, true);
- if (t < 0 && t != -ENOENT) {
- log_error_errno(t, "Failed to detect whether %s is a mount point: %m", where);
-
- if (r == 0)
- r = t;
-
- continue;
- }
+ r = path_is_mount_point(where, AT_SYMLINK_FOLLOW);
+ if (r < 0 && r != -ENOENT)
+ return log_error_errno(r, "Failed to detect whether %s is a mount point: %m", where);
/* Skip this entry if it is not a remount. */
- if (mount_table[k].what && t > 0)
+ if (mount_table[k].what && r > 0)
continue;
- t = mkdir_p(where, 0755);
- if (t < 0) {
- if (mount_table[k].fatal) {
- log_error_errno(t, "Failed to create directory %s: %m", where);
-
- if (r == 0)
- r = t;
- } else
- log_warning_errno(t, "Failed to create directory %s: %m", where);
+ r = mkdir_p(where, 0755);
+ if (r < 0) {
+ if (mount_table[k].fatal)
+ return log_error_errno(r, "Failed to create directory %s: %m", where);
+ log_warning_errno(r, "Failed to create directory %s: %m", where);
continue;
}
-#ifdef HAVE_SELINUX
- if (arg_selinux_apifs_context &&
- (streq_ptr(mount_table[k].what, "tmpfs") || streq_ptr(mount_table[k].what, "devpts"))) {
- options = strjoin(mount_table[k].options, ",context=\"", arg_selinux_apifs_context, "\"", NULL);
- if (!options)
- return log_oom();
-
- o = options;
- } else
-#endif
- o = mount_table[k].options;
-
- if (arg_userns && arg_uid_shift != UID_INVALID && streq_ptr(mount_table[k].type, "tmpfs")) {
- char *uid_options = NULL;
-
- if (o)
- asprintf(&uid_options, "%s,uid=" UID_FMT ",gid=" UID_FMT, o, arg_uid_shift, arg_uid_shift);
- else
- asprintf(&uid_options, "uid=" UID_FMT ",gid=" UID_FMT, arg_uid_shift, arg_uid_shift);
- if (!uid_options)
+ o = mount_table[k].options;
+ if (streq_ptr(mount_table[k].type, "tmpfs")) {
+ r = tmpfs_patch_options(o, &options);
+ if (r < 0)
return log_oom();
-
- free(options);
- o = options = uid_options;
+ if (r > 0)
+ o = options;
}
if (mount(mount_table[k].what,
@@ -1119,34 +1132,29 @@ static int mount_all(const char *dest) {
mount_table[k].flags,
o) < 0) {
- if (mount_table[k].fatal) {
- log_error_errno(errno, "mount(%s) failed: %m", where);
+ if (mount_table[k].fatal)
+ return log_error_errno(errno, "mount(%s) failed: %m", where);
- if (r == 0)
- r = -errno;
- } else
- log_warning_errno(errno, "mount(%s) failed: %m", where);
+ log_warning_errno(errno, "mount(%s) failed, ignoring: %m", where);
}
}
- return r;
+ return 0;
}
static int mount_bind(const char *dest, CustomMount *m) {
struct stat source_st, dest_st;
- char *where;
+ const char *where;
int r;
- assert(dest);
assert(m);
if (stat(m->source, &source_st) < 0)
return log_error_errno(errno, "Failed to stat %s: %m", m->source);
- where = strjoina(dest, m->destination);
+ where = prefix_roota(dest, m->destination);
- r = stat(where, &dest_st);
- if (r >= 0) {
+ if (stat(where, &dest_st) >= 0) {
if (S_ISDIR(source_st.st_mode) && !S_ISDIR(dest_st.st_mode)) {
log_error("Cannot bind mount directory %s on file %s.", m->source, where);
return -EINVAL;
@@ -1190,19 +1198,25 @@ static int mount_bind(const char *dest, CustomMount *m) {
}
static int mount_tmpfs(const char *dest, CustomMount *m) {
- char *where;
+ const char *where, *options;
+ _cleanup_free_ char *buf = NULL;
int r;
assert(dest);
assert(m);
- where = strjoina(dest, m->destination);
+ where = prefix_roota(dest, m->destination);
- r = mkdir_label(where, 0755);
+ r = mkdir_p_label(where, 0755);
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, m->options) < 0)
+ r = tmpfs_patch_options(m->options, &buf);
+ if (r < 0)
+ return log_oom();
+ options = r > 0 ? buf : m->options;
+
+ if (mount("tmpfs", where, "tmpfs", MS_NODEV|MS_STRICTATIME, options) < 0)
return log_error_errno(errno, "tmpfs mount to %s failed: %m", where);
return 0;
@@ -1210,13 +1224,13 @@ static int mount_tmpfs(const char *dest, CustomMount *m) {
static int mount_overlay(const char *dest, CustomMount *m) {
_cleanup_free_ char *lower = NULL;
- char *where, *options;
+ const char *where, *options;
int r;
assert(dest);
assert(m);
- where = strjoina(dest, m->destination);
+ where = prefix_roota(dest, m->destination);
r = mkdir_label(where, 0755);
if (r < 0 && r != -EEXIST)
@@ -1227,7 +1241,6 @@ static int mount_overlay(const char *dest, CustomMount *m) {
strv_reverse(m->lower);
lower = strv_join(m->lower, ":");
strv_reverse(m->lower);
-
if (!lower)
return log_oom();
@@ -1286,7 +1299,7 @@ static int mount_cgroup_hierarchy(const char *dest, const char *controller, cons
to = strjoina(dest, "/sys/fs/cgroup/", hierarchy);
- r = path_is_mount_point(to, false);
+ r = path_is_mount_point(to, 0);
if (r < 0 && r != -ENOENT)
return log_error_errno(r, "Failed to determine if %s is mounted already: %m", to);
if (r > 0)
@@ -1310,8 +1323,7 @@ static int mount_cgroup_hierarchy(const char *dest, const char *controller, cons
static int mount_cgroup(const char *dest) {
_cleanup_set_free_free_ Set *controllers = NULL;
- _cleanup_free_ char *own_cgroup_path = NULL;
- const char *cgroup_root, *systemd_root, *systemd_own;
+ const char *cgroup_root;
int r;
controllers = set_new(&string_hash_ops);
@@ -1322,10 +1334,6 @@ static int mount_cgroup(const char *dest) {
if (r < 0)
return log_error_errno(r, "Failed to determine cgroup controllers: %m");
- r = cg_pid_get_path(NULL, 0, &own_cgroup_path);
- if (r < 0)
- return log_error_errno(r, "Failed to determine our own cgroup path: %m");
-
for (;;) {
_cleanup_free_ char *controller = NULL, *origin = NULL, *combined = NULL;
@@ -1333,7 +1341,7 @@ static int mount_cgroup(const char *dest) {
if (!controller)
break;
- origin = strappend("/sys/fs/cgroup/", controller);
+ origin = prefix_root("/sys/fs/cgroup/", controller);
if (!origin)
return log_oom();
@@ -1350,7 +1358,7 @@ static int mount_cgroup(const char *dest) {
else {
_cleanup_free_ char *target = NULL;
- target = strjoin(dest, "/sys/fs/cgroup/", controller, NULL);
+ target = prefix_root(dest, origin);
if (!target)
return log_oom();
@@ -1379,25 +1387,82 @@ static int mount_cgroup(const char *dest) {
if (r < 0)
return r;
+ cgroup_root = prefix_roota(dest, "/sys/fs/cgroup");
+ if (mount(NULL, cgroup_root, NULL, MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755") < 0)
+ return log_error_errno(errno, "Failed to remount %s read-only: %m", cgroup_root);
+
+ return 0;
+}
+
+static int mount_systemd_cgroup_writable(const char *dest) {
+ _cleanup_free_ char *own_cgroup_path = NULL;
+ const char *systemd_root, *systemd_own;
+ int r;
+
+ assert(dest);
+
+ r = cg_pid_get_path(NULL, 0, &own_cgroup_path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine our own cgroup path: %m");
+
/* Make our own cgroup a (writable) bind mount */
systemd_own = strjoina(dest, "/sys/fs/cgroup/systemd", own_cgroup_path);
if (mount(systemd_own, systemd_own, NULL, MS_BIND, NULL) < 0)
return log_error_errno(errno, "Failed to turn %s into a bind mount: %m", own_cgroup_path);
/* And then remount the systemd cgroup root read-only */
- systemd_root = strjoina(dest, "/sys/fs/cgroup/systemd");
+ systemd_root = prefix_roota(dest, "/sys/fs/cgroup/systemd");
if (mount(NULL, systemd_root, NULL, MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL) < 0)
return log_error_errno(errno, "Failed to mount cgroup root read-only: %m");
- cgroup_root = strjoina(dest, "/sys/fs/cgroup");
- if (mount(NULL, cgroup_root, NULL, MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755") < 0)
- return log_error_errno(errno, "Failed to remount %s read-only: %m", cgroup_root);
+ return 0;
+}
+
+static int userns_lchown(const char *p, uid_t uid, gid_t gid) {
+ assert(p);
+
+ if (!arg_userns)
+ return 0;
+
+ if (uid == UID_INVALID && gid == GID_INVALID)
+ return 0;
+
+ if (uid != UID_INVALID) {
+ uid += arg_uid_shift;
+
+ if (uid < arg_uid_shift || uid >= arg_uid_shift + arg_uid_range)
+ return -EOVERFLOW;
+ }
+
+ if (gid != GID_INVALID) {
+ gid += (gid_t) arg_uid_shift;
+
+ if (gid < (gid_t) arg_uid_shift || gid >= (gid_t) (arg_uid_shift + arg_uid_range))
+ return -EOVERFLOW;
+ }
+
+ if (lchown(p, uid, gid) < 0)
+ return -errno;
return 0;
}
+static int userns_mkdir(const char *root, const char *path, mode_t mode, uid_t uid, gid_t gid) {
+ const char *q;
+
+ q = prefix_roota(root, path);
+ if (mkdir(q, mode) < 0) {
+ if (errno == EEXIST)
+ return 0;
+ return -errno;
+ }
+
+ return userns_lchown(q, uid, gid);
+}
+
static int setup_timezone(const char *dest) {
- _cleanup_free_ char *where = NULL, *p = NULL, *q = NULL, *check = NULL, *what = NULL;
+ _cleanup_free_ char *p = NULL, *q = NULL;
+ const char *where, *check, *what;
char *z, *y;
int r;
@@ -1418,10 +1483,7 @@ static int setup_timezone(const char *dest) {
return 0;
}
- where = strappend(dest, "/etc/localtime");
- if (!where)
- return log_oom();
-
+ where = prefix_roota(dest, "/etc/localtime");
r = readlink_malloc(where, &q);
if (r >= 0) {
y = path_startswith(q, "../usr/share/zoneinfo/");
@@ -1433,43 +1495,34 @@ static int setup_timezone(const char *dest) {
return 0;
}
- check = strjoin(dest, "/usr/share/zoneinfo/", z, NULL);
- if (!check)
- return log_oom();
-
- if (access(check, F_OK) < 0) {
+ check = strjoina("/usr/share/zoneinfo/", z);
+ check = prefix_root(dest, check);
+ if (laccess(check, F_OK) < 0) {
log_warning("Timezone %s does not exist in container, not updating container timezone.", z);
return 0;
}
- what = strappend("../usr/share/zoneinfo/", z);
- if (!what)
- return log_oom();
-
- r = mkdir_parents(where, 0755);
- if (r < 0) {
- 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_errno(errno, "Failed to remove existing timezone info %s in container: %m", where);
-
return 0;
}
+ what = strjoina("../usr/share/zoneinfo/", z);
if (symlink(what, where) < 0) {
log_error_errno(errno, "Failed to correct timezone of container: %m");
return 0;
}
+ r = userns_lchown(where, 0, 0);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to chown /etc/localtime: %m");
+
return 0;
}
static int setup_resolv_conf(const char *dest) {
- _cleanup_free_ char *where = NULL;
+ const char *where = NULL;
int r;
assert(dest);
@@ -1478,31 +1531,24 @@ static int setup_resolv_conf(const char *dest) {
return 0;
/* Fix resolv.conf, if possible */
- where = strappend(dest, "/etc/resolv.conf");
- if (!where)
- return log_oom();
-
- /* We don't really care for the results of this really. If it
- * fails, it fails, but meh... */
- r = mkdir_parents(where, 0755);
- if (r < 0) {
- log_warning_errno(r, "Failed to create parent directory for resolv.conf %s: %m", where);
-
- return 0;
- }
+ where = prefix_roota(dest, "/etc/resolv.conf");
r = copy_file("/etc/resolv.conf", where, O_TRUNC|O_NOFOLLOW, 0644, 0);
if (r < 0) {
log_warning_errno(r, "Failed to copy /etc/resolv.conf to %s: %m", where);
-
return 0;
}
+ r = userns_lchown(where, 0, 0);
+ if (r < 0)
+ log_warning_errno(r, "Failed to chown /etc/resolv.conf: %m");
+
return 0;
}
static int setup_volatile_state(const char *directory) {
- const char *p;
+ _cleanup_free_ char *buf = NULL;
+ const char *p, *options;
int r;
assert(directory);
@@ -1517,12 +1563,19 @@ static int setup_volatile_state(const char *directory) {
if (r < 0)
return log_error_errno(r, "Failed to remount %s read-only: %m", directory);
- p = strjoina(directory, "/var");
+ p = prefix_roota(directory, "/var");
r = mkdir(p, 0755);
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)
+ options = "mode=755";
+ r = tmpfs_patch_options(options, &buf);
+ if (r < 0)
+ return log_oom();
+ if (r > 0)
+ options = buf;
+
+ if (mount("tmpfs", p, "tmpfs", MS_STRICTATIME, options) < 0)
return log_error_errno(errno, "Failed to mount tmpfs to /var: %m");
return 0;
@@ -1531,7 +1584,8 @@ static int setup_volatile_state(const char *directory) {
static int setup_volatile(const char *directory) {
bool tmpfs_mounted = false, bind_mounted = false;
char template[] = "/tmp/nspawn-volatile-XXXXXX";
- const char *f, *t;
+ _cleanup_free_ char *buf = NULL;
+ const char *f, *t, *options;
int r;
assert(directory);
@@ -1545,27 +1599,31 @@ static int setup_volatile(const char *directory) {
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_errno(errno, "Failed to mount tmpfs for root directory: %m");
- r = -errno;
+ options = "mode=755";
+ r = tmpfs_patch_options(options, &buf);
+ if (r < 0)
+ return log_oom();
+ if (r > 0)
+ options = buf;
+
+ if (mount("tmpfs", template, "tmpfs", MS_STRICTATIME, options) < 0) {
+ r = log_error_errno(errno, "Failed to mount tmpfs for root directory: %m");
goto fail;
}
tmpfs_mounted = true;
- f = strjoina(directory, "/usr");
- t = strjoina(template, "/usr");
+ f = prefix_roota(directory, "/usr");
+ t = prefix_roota(template, "/usr");
r = mkdir(t, 0755);
if (r < 0 && errno != EEXIST) {
- log_error_errno(errno, "Failed to create %s: %m", t);
- r = -errno;
+ r = log_error_errno(errno, "Failed to create %s: %m", t);
goto fail;
}
if (mount(f, t, NULL, MS_BIND|MS_REC, NULL) < 0) {
- log_error_errno(errno, "Failed to create /usr bind mount: %m");
- r = -errno;
+ r = log_error_errno(errno, "Failed to create /usr bind mount: %m");
goto fail;
}
@@ -1578,25 +1636,26 @@ static int setup_volatile(const char *directory) {
}
if (mount(template, directory, NULL, MS_MOVE, NULL) < 0) {
- log_error_errno(errno, "Failed to move root mount: %m");
- r = -errno;
+ r = log_error_errno(errno, "Failed to move root mount: %m");
goto fail;
}
- rmdir(template);
+ (void) rmdir(template);
return 0;
fail:
if (bind_mounted)
- umount(t);
+ (void) umount(t);
+
if (tmpfs_mounted)
- umount(template);
- rmdir(template);
+ (void) umount(template);
+ (void) rmdir(template);
return r;
}
static char* id128_format_as_uuid(sd_id128_t id, char s[37]) {
+ assert(s);
snprintf(s, 37,
"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
@@ -1606,23 +1665,19 @@ static char* id128_format_as_uuid(sd_id128_t id, char s[37]) {
}
static int setup_boot_id(const char *dest) {
- _cleanup_free_ char *from = NULL, *to = NULL;
+ const char *from, *to;
sd_id128_t rnd = {};
char as_uuid[37];
int r;
- assert(dest);
-
if (arg_share_system)
return 0;
/* Generate a new randomized boot ID, so that each boot-up of
* the container gets a new one */
- from = strappend(dest, "/dev/proc-sys-kernel-random-boot-id");
- to = strappend(dest, "/proc/sys/kernel/random/boot_id");
- if (!from || !to)
- return log_oom();
+ from = prefix_roota(dest, "/run/proc-sys-kernel-random-boot-id");
+ to = prefix_roota(dest, "/proc/sys/kernel/random/boot_id");
r = sd_id128_randomize(&rnd);
if (r < 0)
@@ -1634,10 +1689,9 @@ static int setup_boot_id(const char *dest) {
if (r < 0)
return log_error_errno(r, "Failed to write boot id: %m");
- if (mount(from, to, NULL, MS_BIND, NULL) < 0) {
- log_error_errno(errno, "Failed to bind mount boot id: %m");
- r = -errno;
- } else if (mount(from, to, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL))
+ if (mount(from, to, NULL, MS_BIND, NULL) < 0)
+ r = log_error_errno(errno, "Failed to bind mount boot id: %m");
+ else if (mount(NULL, to, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NOSUID|MS_NODEV, NULL) < 0)
log_warning_errno(errno, "Failed to make boot id read-only: %m");
unlink(from);
@@ -1663,14 +1717,16 @@ static int copy_devnodes(const char *dest) {
u = umask(0000);
+ /* Create /dev/net, so that we can create /dev/net/tun in it */
+ if (userns_mkdir(dest, "/dev/net", 0755, 0, 0) < 0)
+ return log_error_errno(r, "Failed to create /dev/net directory: %m");
+
NULSTR_FOREACH(d, devnodes) {
_cleanup_free_ char *from = NULL, *to = NULL;
struct stat st;
from = strappend("/dev/", d);
- to = strjoin(dest, "/dev/", d, NULL);
- if (!from || !to)
- return log_oom();
+ to = prefix_root(dest, from);
if (stat(from, &st) < 0) {
@@ -1679,16 +1735,10 @@ static int copy_devnodes(const char *dest) {
} else if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
- log_error("%s is not a char or block device, cannot copy", from);
+ log_error("%s is not a char or block device, cannot copy.", from);
return -EIO;
} else {
- r = mkdir_parents(to, 0775);
- if (r < 0) {
- 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) {
if (errno != EPERM)
return log_error_errno(errno, "mknod(%s) failed: %m", to);
@@ -1702,28 +1752,56 @@ static int copy_devnodes(const char *dest) {
return log_error_errno(errno, "Both mknod and bind mount (%s) failed: %m", to);
}
- if (arg_userns && arg_uid_shift != UID_INVALID)
- if (lchown(to, arg_uid_shift, arg_uid_shift) < 0)
- return log_error_errno(errno, "chown() of device node %s failed: %m", to);
+ r = userns_lchown(to, 0, 0);
+ if (r < 0)
+ return log_error_errno(r, "chown() of device node %s failed: %m", to);
}
}
return r;
}
-static int setup_ptmx(const char *dest) {
- _cleanup_free_ char *p = NULL;
+static int setup_pts(const char *dest) {
+ _cleanup_free_ char *options = NULL;
+ const char *p;
+
+#ifdef HAVE_SELINUX
+ if (arg_selinux_apifs_context)
+ (void) asprintf(&options,
+ "newinstance,ptmxmode=0666,mode=620,uid=" UID_FMT ",gid=" GID_FMT ",context=\"%s\"",
+ arg_uid_shift,
+ arg_uid_shift + TTY_GID,
+ arg_selinux_apifs_context);
+ else
+#endif
+ (void) asprintf(&options,
+ "newinstance,ptmxmode=0666,mode=620,uid=" UID_FMT ",gid=" GID_FMT,
+ arg_uid_shift,
+ arg_uid_shift + TTY_GID);
- p = strappend(dest, "/dev/ptmx");
- if (!p)
+ if (!options)
return log_oom();
+ /* Mount /dev/pts itself */
+ p = prefix_roota(dest, "/dev/pts");
+ if (mkdir(p, 0755) < 0)
+ return log_error_errno(errno, "Failed to create /dev/pts: %m");
+ if (mount("devpts", p, "devpts", MS_NOSUID|MS_NOEXEC, options) < 0)
+ return log_error_errno(errno, "Failed to mount /dev/pts: %m");
+ if (userns_lchown(p, 0, 0) < 0)
+ return log_error_errno(errno, "Failed to chown /dev/pts: %m");
+
+ /* Create /dev/ptmx symlink */
+ p = prefix_roota(dest, "/dev/ptmx");
if (symlink("pts/ptmx", p) < 0)
return log_error_errno(errno, "Failed to create /dev/ptmx symlink: %m");
+ if (userns_lchown(p, 0, 0) < 0)
+ return log_error_errno(errno, "Failed to chown /dev/ptmx: %m");
- if (arg_userns && arg_uid_shift != UID_INVALID)
- if (lchown(p, arg_uid_shift, arg_uid_shift) < 0)
- return log_error_errno(errno, "lchown() of symlink %s failed: %m", p);
+ /* And fix /dev/pts/ptmx ownership */
+ p = prefix_roota(dest, "/dev/pts/ptmx");
+ if (userns_lchown(p, 0, 0) < 0)
+ return log_error_errno(errno, "Failed to chown /dev/pts/ptmx: %m");
return 0;
}
@@ -1738,7 +1816,7 @@ static int setup_dev_console(const char *dest, const char *console) {
u = umask(0000);
- r = chmod_and_chown(console, 0600, 0, 0);
+ r = chmod_and_chown(console, 0600, arg_uid_shift, arg_uid_shift);
if (r < 0)
return log_error_errno(r, "Failed to correct access mode for TTY: %m");
@@ -1746,7 +1824,7 @@ static int setup_dev_console(const char *dest, const char *console) {
* ptys can only exist on pts file systems. To have something
* to bind mount things on we create a empty regular file. */
- to = strjoina(dest, "/dev/console");
+ to = prefix_roota(dest, "/dev/console");
r = touch(to);
if (r < 0)
return log_error_errno(r, "touch() for /dev/console failed: %m");
@@ -1758,9 +1836,9 @@ static int setup_dev_console(const char *dest, const char *console) {
}
static int setup_kmsg(const char *dest, int kmsg_socket) {
- _cleanup_free_ char *from = NULL, *to = NULL;
+ const char *from, *to;
_cleanup_umask_ mode_t u;
- int r, fd, k;
+ int fd, k;
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(int))];
@@ -1771,29 +1849,22 @@ static int setup_kmsg(const char *dest, int kmsg_socket) {
};
struct cmsghdr *cmsg;
- assert(dest);
assert(kmsg_socket >= 0);
u = umask(0000);
- /* We create the kmsg FIFO as /dev/kmsg, but immediately
+ /* We create the kmsg FIFO as /run/kmsg, but immediately
* delete it after bind mounting it to /proc/kmsg. While FIFOs
* on the reading side behave very similar to /proc/kmsg,
* their writing side behaves differently from /dev/kmsg in
* that writing blocks when nothing is reading. In order to
* avoid any problems with containers deadlocking due to this
* we simply make /dev/kmsg unavailable to the container. */
- if (asprintf(&from, "%s/dev/kmsg", dest) < 0 ||
- asprintf(&to, "%s/proc/kmsg", dest) < 0)
- return log_oom();
+ from = prefix_roota(dest, "/run/kmsg");
+ to = prefix_roota(dest, "/proc/kmsg");
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)
- return log_error_errno(r, "Failed to correct access mode for /dev/kmsg: %m");
-
+ return log_error_errno(errno, "mkfifo() for /run/kmsg failed: %m");
if (mount(from, to, NULL, MS_BIND, NULL) < 0)
return log_error_errno(errno, "Bind mount for /proc/kmsg failed: %m");
@@ -1817,8 +1888,9 @@ static int setup_kmsg(const char *dest, int kmsg_socket) {
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);
+ /* And now make the FIFO unavailable as /run/kmsg... */
+ (void) unlink(from);
+
return 0;
}
@@ -1842,7 +1914,7 @@ static int send_rtnl(int send_fd) {
fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_ROUTE);
if (fd < 0)
- return log_error_errno(errno, "failed to allocate container netlink: %m");
+ return log_error_errno(errno, "Failed to allocate container netlink: %m");
cmsg = CMSG_FIRSTHDR(&mh);
cmsg->cmsg_level = SOL_SOCKET;
@@ -2027,7 +2099,8 @@ static int setup_hostname(void) {
static int setup_journal(const char *directory) {
sd_id128_t machine_id, this_id;
- _cleanup_free_ char *p = NULL, *b = NULL, *q = NULL, *d = NULL;
+ _cleanup_free_ char *b = NULL, *d = NULL;
+ const char *etc_machine_id, *p, *q;
char *id;
int r;
@@ -2035,15 +2108,13 @@ static int setup_journal(const char *directory) {
if (arg_ephemeral)
return 0;
- p = strappend(directory, "/etc/machine-id");
- if (!p)
- return log_oom();
+ etc_machine_id = prefix_roota(directory, "/etc/machine-id");
- r = read_one_line_file(p, &b);
+ r = read_one_line_file(etc_machine_id, &b);
if (r == -ENOENT && arg_link_journal == LINK_AUTO)
return 0;
else if (r < 0)
- return log_error_errno(r, "Failed to read machine ID from %s: %m", p);
+ return log_error_errno(r, "Failed to read machine ID from %s: %m", etc_machine_id);
id = strstrip(b);
if (isempty(id) && arg_link_journal == LINK_AUTO)
@@ -2052,7 +2123,7 @@ static int setup_journal(const char *directory) {
/* Verify validity */
r = sd_id128_from_string(id, &machine_id);
if (r < 0)
- return log_error_errno(r, "Failed to parse machine ID from %s: %m", p);
+ return log_error_errno(r, "Failed to parse machine ID from %s: %m", etc_machine_id);
r = sd_id128_get_machine(&this_id);
if (r < 0)
@@ -2069,13 +2140,22 @@ static int setup_journal(const char *directory) {
if (arg_link_journal == LINK_NO)
return 0;
- free(p);
- p = strappend("/var/log/journal/", id);
- q = strjoin(directory, "/var/log/journal/", id, NULL);
- if (!p || !q)
- return log_oom();
+ r = userns_mkdir(directory, "/var", 0755, 0, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create /var: %m");
+
+ r = userns_mkdir(directory, "/var/log", 0755, 0, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create /var/log: %m");
+
+ r = userns_mkdir(directory, "/var/log/journal", 0755, 0, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create /var/log/journal: %m");
+
+ p = strjoina("/var/log/journal/", id);
+ q = prefix_roota(directory, p);
- if (path_is_mount_point(p, false) > 0) {
+ if (path_is_mount_point(p, 0) > 0) {
if (arg_link_journal != LINK_AUTO) {
log_error("%s: already a mount point, refusing to use for journal", p);
return -EEXIST;
@@ -2084,7 +2164,7 @@ static int setup_journal(const char *directory) {
return 0;
}
- if (path_is_mount_point(q, false) > 0) {
+ if (path_is_mount_point(q, 0) > 0) {
if (arg_link_journal != LINK_AUTO) {
log_error("%s: already a mount point, refusing to use for journal", q);
return -EEXIST;
@@ -2099,7 +2179,7 @@ static int setup_journal(const char *directory) {
arg_link_journal == LINK_AUTO) &&
path_equal(d, q)) {
- r = mkdir_p(q, 0755);
+ r = userns_mkdir(directory, p, 0755, 0, 0);
if (r < 0)
log_warning_errno(errno, "Failed to create directory %s: %m", q);
return 0;
@@ -2137,7 +2217,7 @@ static int setup_journal(const char *directory) {
}
}
- r = mkdir_p(q, 0755);
+ r = userns_mkdir(directory, p, 0755, 0, 0);
if (r < 0)
log_warning_errno(errno, "Failed to create directory %s: %m", q);
return 0;
@@ -2163,7 +2243,7 @@ static int setup_journal(const char *directory) {
if (dir_is_empty(q) == 0)
log_warning("%s is not empty, proceeding anyway.", q);
- r = mkdir_p(q, 0755);
+ r = userns_mkdir(directory, p, 0755, 0, 0);
if (r < 0) {
log_error_errno(errno, "Failed to create %s: %m", q);
return r;
@@ -2548,7 +2628,7 @@ static int setup_veth(pid_t pid, char iface_name[IFNAMSIZ], int *ifi) {
r = sd_rtnl_call(rtnl, m, 0, NULL);
if (r < 0)
- return log_error_errno(r, "Failed to add new veth interfaces: %m");
+ return log_error_errno(r, "Failed to add new veth interfaces (host0, %s): %m", iface_name);
i = (int) if_nametoindex(iface_name);
if (i <= 0)
@@ -2853,15 +2933,16 @@ static int setup_seccomp(void) {
uint64_t capability;
int syscall_num;
} blacklist[] = {
- { CAP_SYS_RAWIO, SCMP_SYS(iopl)},
- { CAP_SYS_RAWIO, SCMP_SYS(ioperm)},
- { CAP_SYS_BOOT, SCMP_SYS(kexec_load)},
- { CAP_SYS_ADMIN, SCMP_SYS(swapon)},
- { CAP_SYS_ADMIN, SCMP_SYS(swapoff)},
- { CAP_SYS_ADMIN, SCMP_SYS(open_by_handle_at)},
- { CAP_SYS_MODULE, SCMP_SYS(init_module)},
- { CAP_SYS_MODULE, SCMP_SYS(finit_module)},
- { CAP_SYS_MODULE, SCMP_SYS(delete_module)},
+ { CAP_SYS_RAWIO, SCMP_SYS(iopl) },
+ { CAP_SYS_RAWIO, SCMP_SYS(ioperm) },
+ { CAP_SYS_BOOT, SCMP_SYS(kexec_load) },
+ { CAP_SYS_ADMIN, SCMP_SYS(swapon) },
+ { CAP_SYS_ADMIN, SCMP_SYS(swapoff) },
+ { CAP_SYS_ADMIN, SCMP_SYS(open_by_handle_at) },
+ { CAP_SYS_MODULE, SCMP_SYS(init_module) },
+ { CAP_SYS_MODULE, SCMP_SYS(finit_module) },
+ { CAP_SYS_MODULE, SCMP_SYS(delete_module) },
+ { CAP_SYSLOG, SCMP_SYS(syslog) },
};
scmp_filter_ctx seccomp;
@@ -2941,10 +3022,16 @@ static int setup_propagate(const char *root) {
p = strjoina("/run/systemd/nspawn/propagate/", arg_machine);
(void) mkdir_p(p, 0600);
- q = strjoina(root, "/run/systemd/nspawn/incoming");
- mkdir_parents(q, 0755);
- mkdir_p(q, 0600);
+ if (userns_mkdir(root, "/run/systemd", 0755, 0, 0) < 0)
+ return log_error_errno(errno, "Failed to create /run/systemd: %m");
+ if (userns_mkdir(root, "/run/systemd/nspawn", 0755, 0, 0) < 0)
+ return log_error_errno(errno, "Failed to create /run/systemd/nspawn: %m");
+
+ if (userns_mkdir(root, "/run/systemd/nspawn/incoming", 0600, 0, 0) < 0)
+ return log_error_errno(errno, "Failed to create /run/systemd/nspawn/incoming: %m");
+
+ q = prefix_roota(root, "/run/systemd/nspawn/incoming");
if (mount(p, q, NULL, MS_BIND, NULL) < 0)
return log_error_errno(errno, "Failed to install propagation bind mount.");
@@ -3603,14 +3690,9 @@ 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)
- return log_error_errno(errno, "setgroups() failed: %m");
-
- if (setresgid(0, 0, 0) < 0)
- return log_error_errno(errno, "setregid() failed: %m");
-
- if (setresuid(0, 0, 0) < 0)
- return log_error_errno(errno, "setreuid() failed: %m");
+ r = reset_uid_gid();
+ if (r < 0)
+ return log_error_errno(r, "Failed to become root: %m");
*_home = NULL;
return 0;
@@ -3754,9 +3836,9 @@ static int change_uid_gid(char **_home) {
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);
+ (void) fchown(STDIN_FILENO, uid, gid);
+ (void) fchown(STDOUT_FILENO, uid, gid);
+ (void) fchown(STDERR_FILENO, uid, gid);
if (setgroups(n_uids, uids) < 0)
return log_error_errno(errno, "Failed to set auxiliary groups: %m");
@@ -3925,23 +4007,25 @@ static int determine_names(void) {
return 0;
}
-static int determine_uid_shift(void) {
+static int determine_uid_shift(const char *directory) {
int r;
- if (!arg_userns)
+ if (!arg_userns) {
+ arg_uid_shift = 0;
return 0;
+ }
if (arg_uid_shift == UID_INVALID) {
struct stat st;
- r = stat(arg_directory, &st);
+ r = stat(directory, &st);
if (r < 0)
- return log_error_errno(errno, "Failed to determine UID base of %s: %m", arg_directory);
+ return log_error_errno(errno, "Failed to determine UID base of %s: %m", directory);
arg_uid_shift = st.st_uid & UINT32_C(0xffff0000);
if (arg_uid_shift != (st.st_gid & UINT32_C(0xffff0000))) {
- log_error("UID and GID base of %s don't match.", arg_directory);
+ log_error("UID and GID base of %s don't match.", directory);
return -EINVAL;
}
@@ -3957,6 +4041,413 @@ static int determine_uid_shift(void) {
return 0;
}
+static int inner_child(
+ Barrier *barrier,
+ const char *directory,
+ bool secondary,
+ int kmsg_socket,
+ int rtnl_socket,
+ FDSet *fds,
+ int argc,
+ char *argv[]) {
+
+ _cleanup_free_ char *home = NULL;
+ unsigned n_env = 2;
+ const char *envp[] = {
+ "PATH=" DEFAULT_PATH_SPLIT_USR,
+ "container=systemd-nspawn", /* LXC sets container=lxc, so follow the scheme here */
+ NULL, /* TERM */
+ NULL, /* HOME */
+ NULL, /* USER */
+ NULL, /* LOGNAME */
+ NULL, /* container_uuid */
+ NULL, /* LISTEN_FDS */
+ NULL, /* LISTEN_PID */
+ NULL
+ };
+
+ _cleanup_strv_free_ char **env_use = NULL;
+ int r;
+
+ assert(barrier);
+ assert(directory);
+ assert(kmsg_socket >= 0);
+
+ if (arg_userns) {
+ /* Tell the parent, that it now can write the UID map. */
+ (void) barrier_place(barrier); /* #1 */
+
+ /* Wait until the parent wrote the UID map */
+ if (!barrier_place_and_sync(barrier)) { /* #2 */
+ log_error("Parent died too early");
+ return -ESRCH;
+ }
+ }
+
+ r = mount_all(NULL, true);
+ if (r < 0)
+ return r;
+
+ /* Wait until we are cgroup-ified, so that we
+ * can mount the right cgroup path writable */
+ if (!barrier_place_and_sync(barrier)) { /* #3 */
+ log_error("Parent died too early");
+ return -ESRCH;
+ }
+
+ r = mount_systemd_cgroup_writable("");
+ if (r < 0)
+ return r;
+
+ r = reset_uid_gid();
+ if (r < 0)
+ return log_error_errno(r, "Couldn't become new root: %m");
+
+ r = setup_boot_id(NULL);
+ if (r < 0)
+ return r;
+
+ r = setup_kmsg(NULL, kmsg_socket);
+ if (r < 0)
+ return r;
+ kmsg_socket = safe_close(kmsg_socket);
+
+ umask(0022);
+
+ if (setsid() < 0)
+ return log_error_errno(errno, "setsid() failed: %m");
+
+ if (arg_private_network)
+ loopback_setup();
+
+ r = send_rtnl(rtnl_socket);
+ if (r < 0)
+ return r;
+ rtnl_socket = safe_close(rtnl_socket);
+
+ if (drop_capabilities() < 0)
+ return log_error_errno(errno, "drop_capabilities() failed: %m");
+
+ setup_hostname();
+
+ if (arg_personality != PERSONALITY_INVALID) {
+ if (personality(arg_personality) < 0)
+ return log_error_errno(errno, "personality() failed: %m");
+ } else if (secondary) {
+ if (personality(PER_LINUX32) < 0)
+ return log_error_errno(errno, "personality() failed: %m");
+ }
+
+#ifdef HAVE_SELINUX
+ if (arg_selinux_context)
+ if (setexeccon((security_context_t) arg_selinux_context) < 0)
+ return log_error_errno(errno, "setexeccon(\"%s\") failed: %m", arg_selinux_context);
+#endif
+
+ r = change_uid_gid(&home);
+ if (r < 0)
+ return r;
+
+ envp[n_env] = strv_find_prefix(environ, "TERM=");
+ if (envp[n_env])
+ n_env ++;
+
+ if ((asprintf((char**)(envp + n_env++), "HOME=%s", home ? home: "/root") < 0) ||
+ (asprintf((char**)(envp + n_env++), "USER=%s", arg_user ? arg_user : "root") < 0) ||
+ (asprintf((char**)(envp + n_env++), "LOGNAME=%s", arg_user ? arg_user : "root") < 0))
+ return log_oom();
+
+ if (!sd_id128_equal(arg_uuid, SD_ID128_NULL)) {
+ char as_uuid[37];
+
+ if (asprintf((char**)(envp + n_env++), "container_uuid=%s", id128_format_as_uuid(arg_uuid, as_uuid)) < 0)
+ return log_oom();
+ }
+
+ if (fdset_size(fds) > 0) {
+ r = fdset_cloexec(fds, false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to unset O_CLOEXEC for file descriptors.");
+
+ if ((asprintf((char **)(envp + n_env++), "LISTEN_FDS=%u", fdset_size(fds)) < 0) ||
+ (asprintf((char **)(envp + n_env++), "LISTEN_PID=1") < 0))
+ return log_oom();
+ }
+
+ env_use = strv_env_merge(2, envp, arg_setenv);
+ if (!env_use)
+ return log_oom();
+
+ /* Let the parent know that we are ready and
+ * wait until the parent is ready with the
+ * setup, too... */
+ if (!barrier_place_and_sync(barrier)) { /* #4 */
+ log_error("Parent died too early");
+ return -ESRCH;
+ }
+
+ /* Now, explicitly close the log, so that we
+ * then can close all remaining fds. Closing
+ * the log explicitly first has the benefit
+ * that the logging subsystem knows about it,
+ * and is thus ready to be reopened should we
+ * need it again. Note that the other fds
+ * closed here are at least the locking and
+ * barrier fds. */
+ log_close();
+ (void) fdset_close_others(fds);
+
+ if (arg_boot) {
+ char **a;
+ size_t m;
+
+ /* Automatically search for the init system */
+
+ m = 1 + argc - optind;
+ a = newa(char*, m + 1);
+ memcpy(a + 1, argv + optind, m * sizeof(char*));
+
+ a[0] = (char*) "/usr/lib/systemd/systemd";
+ execve(a[0], a, env_use);
+
+ a[0] = (char*) "/lib/systemd/systemd";
+ execve(a[0], a, env_use);
+
+ a[0] = (char*) "/sbin/init";
+ execve(a[0], a, env_use);
+ } else if (argc > optind)
+ execvpe(argv[optind], argv + optind, env_use);
+ else {
+ chdir(home ? home : "/root");
+ execle("/bin/bash", "-bash", NULL, env_use);
+ execle("/bin/sh", "-sh", NULL, env_use);
+ }
+
+ (void) log_open();
+ return log_error_errno(errno, "execv() failed: %m");
+}
+
+static int outer_child(
+ Barrier *barrier,
+ const char *directory,
+ const char *console,
+ const char *root_device, bool root_device_rw,
+ const char *home_device, bool home_device_rw,
+ const char *srv_device, bool srv_device_rw,
+ bool interactive,
+ bool secondary,
+ int pid_socket,
+ int kmsg_socket,
+ int rtnl_socket,
+ FDSet *fds,
+ int argc,
+ char *argv[]) {
+
+ pid_t pid;
+ ssize_t l;
+ int r;
+
+ assert(barrier);
+ assert(directory);
+ assert(console);
+ assert(pid_socket >= 0);
+ assert(kmsg_socket >= 0);
+
+ if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0)
+ return log_error_errno(errno, "PR_SET_PDEATHSIG failed: %m");
+
+ if (interactive) {
+ close_nointr(STDIN_FILENO);
+ close_nointr(STDOUT_FILENO);
+ close_nointr(STDERR_FILENO);
+
+ r = open_terminal(console, O_RDWR);
+ if (r != STDIN_FILENO) {
+ if (r >= 0) {
+ safe_close(r);
+ r = -EINVAL;
+ }
+
+ return log_error_errno(r, "Failed to open console: %m");
+ }
+
+ if (dup2(STDIN_FILENO, STDOUT_FILENO) != STDOUT_FILENO ||
+ dup2(STDIN_FILENO, STDERR_FILENO) != STDERR_FILENO)
+ return log_error_errno(errno, "Failed to duplicate console: %m");
+ }
+
+ r = reset_audit_loginuid();
+ if (r < 0)
+ return r;
+
+ /* Mark everything as slave, so that we still
+ * 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)
+ return log_error_errno(errno, "MS_SLAVE|MS_REC failed: %m");
+
+ r = mount_devices(directory,
+ root_device, root_device_rw,
+ home_device, home_device_rw,
+ srv_device, srv_device_rw);
+ if (r < 0)
+ return r;
+
+ r = determine_uid_shift(directory);
+ if (r < 0)
+ return r;
+
+ /* Turn directory into bind mount */
+ if (mount(directory, directory, NULL, MS_BIND|MS_REC, NULL) < 0)
+ return log_error_errno(errno, "Failed to make bind mount: %m");
+
+ r = setup_volatile(directory);
+ if (r < 0)
+ return r;
+
+ r = setup_volatile_state(directory);
+ if (r < 0)
+ return r;
+
+ r = base_filesystem_create(directory, arg_uid_shift, (gid_t) arg_uid_shift);
+ if (r < 0)
+ return r;
+
+ if (arg_read_only) {
+ r = bind_remount_recursive(directory, true);
+ if (r < 0)
+ return log_error_errno(r, "Failed to make tree read-only: %m");
+ }
+
+ r = mount_all(directory, false);
+ if (r < 0)
+ return r;
+
+ if (copy_devnodes(directory) < 0)
+ return r;
+
+ dev_setup(directory, arg_uid_shift, arg_uid_shift);
+
+ if (setup_pts(directory) < 0)
+ return r;
+
+ r = setup_propagate(directory);
+ if (r < 0)
+ return r;
+
+ r = setup_dev_console(directory, console);
+ if (r < 0)
+ return r;
+
+ r = setup_seccomp();
+ if (r < 0)
+ return r;
+
+ r = setup_timezone(directory);
+ if (r < 0)
+ return r;
+
+ r = setup_resolv_conf(directory);
+ if (r < 0)
+ return r;
+
+ r = setup_journal(directory);
+ if (r < 0)
+ return r;
+
+ r = mount_custom(directory);
+ if (r < 0)
+ return r;
+
+ r = mount_cgroup(directory);
+ if (r < 0)
+ return r;
+
+ r = mount_move_root(directory);
+ if (r < 0)
+ return log_error_errno(r, "Failed to move root directory: %m");
+
+ pid = raw_clone(SIGCHLD|CLONE_NEWNS|
+ (arg_share_system ? 0 : CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS) |
+ (arg_private_network ? CLONE_NEWNET : 0) |
+ (arg_userns ? CLONE_NEWUSER : 0),
+ NULL);
+ if (pid < 0)
+ return log_error_errno(errno, "Failed to fork inner child: %m");
+
+ if (pid == 0) {
+ pid_socket = safe_close(pid_socket);
+
+ /* The inner child has all namespaces that are
+ * requested, so that we all are owned by the user if
+ * user namespaces are turned on. */
+
+ r = inner_child(barrier, directory, secondary, kmsg_socket, rtnl_socket, fds, argc, argv);
+ if (r < 0)
+ _exit(EXIT_FAILURE);
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ l = send(pid_socket, &pid, sizeof(pid), MSG_NOSIGNAL);
+ if (l < 0)
+ return log_error_errno(errno, "Failed to send PID: %m");
+ if (l != sizeof(pid)) {
+ log_error("Short write while sending PID.");
+ return -EIO;
+ }
+
+ pid_socket = safe_close(pid_socket);
+
+ return 0;
+}
+
+static int setup_uid_map(pid_t pid) {
+ char uid_map[strlen("/proc//uid_map") + DECIMAL_STR_MAX(uid_t) + 1], line[DECIMAL_STR_MAX(uid_t)*3+3+1];
+ int r;
+
+ assert(pid > 1);
+
+ xsprintf(uid_map, "/proc/" PID_FMT "/uid_map", pid);
+ xsprintf(line, UID_FMT " " UID_FMT " " UID_FMT "\n", 0, arg_uid_shift, arg_uid_range);
+ r = write_string_file(uid_map, line);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write UID map: %m");
+
+ /* We always assign the same UID and GID ranges */
+ xsprintf(uid_map, "/proc/" PID_FMT "/gid_map", pid);
+ r = write_string_file(uid_map, line);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write GID map: %m");
+
+ return 0;
+}
+
+static int chown_cgroup(pid_t pid) {
+ _cleanup_free_ char *path = NULL, *fs = NULL;
+ _cleanup_close_ int fd = -1;
+ const char *fn;
+ int r;
+
+ r = cg_pid_get_path(NULL, pid, &path);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get container cgroup path: %m");
+
+ r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, path, NULL, &fs);
+ if (r < 0)
+ return log_error_errno(r, "Failed to get file system path for container cgroup: %m");
+
+ fd = open(fs, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to open %s: %m", fs);
+
+ FOREACH_STRING(fn, ".", "tasks", "notify_on_release", "cgroup.procs", "cgroup.clone_children")
+ if (fchownat(fd, fn, arg_uid_shift, arg_uid_shift, 0) < 0)
+ log_warning_errno(errno, "Failed to chown() cgroup file %s, ignoring: %m", fn);
+
+ return 0;
+}
+
int main(int argc, char *argv[]) {
_cleanup_free_ char *device_path = NULL, *root_device = NULL, *home_device = NULL, *srv_device = NULL, *console = NULL;
@@ -4017,7 +4508,7 @@ int main(int argc, char *argv[]) {
* the specified is not a mount point we
* create the new snapshot in the parent
* directory, just next to it. */
- r = path_is_mount_point(arg_directory, false);
+ r = path_is_mount_point(arg_directory, 0);
if (r < 0) {
log_error_errno(r, "Failed to determine whether directory %s is mount point: %m", arg_directory);
goto finish;
@@ -4136,15 +4627,13 @@ int main(int argc, char *argv[]) {
goto finish;
}
- r = determine_uid_shift();
- if (r < 0)
- goto finish;
-
r = custom_mounts_prepare();
if (r < 0)
goto finish;
- interactive = isatty(STDIN_FILENO) > 0 && isatty(STDOUT_FILENO) > 0;
+ interactive =
+ isatty(STDIN_FILENO) > 0 &&
+ isatty(STDOUT_FILENO) > 0;
master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY);
if (master < 0) {
@@ -4174,14 +4663,25 @@ int main(int argc, char *argv[]) {
assert_se(sigemptyset(&mask_chld) == 0);
assert_se(sigaddset(&mask_chld, SIGCHLD) == 0);
+ if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0) {
+ r = log_error_errno(errno, "Failed to become subreaper: %m");
+ goto finish;
+ }
+
for (;;) {
- _cleanup_close_pair_ int kmsg_socket_pair[2] = { -1, -1 }, rtnl_socket_pair[2] = { -1, -1 };
+ _cleanup_close_pair_ int kmsg_socket_pair[2] = { -1, -1 }, rtnl_socket_pair[2] = { -1, -1 }, pid_socket_pair[2] = { -1, -1 };
ContainerStatus container_status;
_cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL;
- struct sigaction sa = {
+ static const struct sigaction sa = {
.sa_handler = nop_handler,
.sa_flags = SA_NOCLDSTOP,
};
+ int ifi = 0;
+ ssize_t l;
+ _cleanup_event_unref_ sd_event *event = NULL;
+ _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
+ _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
+ char last_char = 0;
r = barrier_create(&barrier);
if (r < 0) {
@@ -4199,6 +4699,11 @@ int main(int argc, char *argv[]) {
goto finish;
}
+ if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, pid_socket_pair) < 0) {
+ r = log_error_errno(errno, "Failed to create pid socket pair: %m");
+ goto finish;
+ }
+
/* Child can be killed before execv(), so handle SIGCHLD
* in order to interrupt parent's blocking calls and
* give it a chance to call wait() and terminate. */
@@ -4214,9 +4719,7 @@ int main(int argc, char *argv[]) {
goto finish;
}
- pid = raw_clone(SIGCHLD|CLONE_NEWNS|
- (arg_share_system ? 0 : CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS)|
- (arg_private_network ? CLONE_NEWNET : 0), NULL);
+ pid = raw_clone(SIGCHLD|CLONE_NEWNS, NULL);
if (pid < 0) {
if (errno == EINVAL)
r = 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");
@@ -4227,471 +4730,191 @@ int main(int argc, char *argv[]) {
}
if (pid == 0) {
- /* child */
- _cleanup_free_ char *home = NULL;
- unsigned n_env = 2;
- const char *envp[] = {
- "PATH=" DEFAULT_PATH_SPLIT_USR,
- "container=systemd-nspawn", /* LXC sets container=lxc, so follow the scheme here */
- NULL, /* TERM */
- NULL, /* HOME */
- NULL, /* USER */
- NULL, /* LOGNAME */
- NULL, /* container_uuid */
- NULL, /* LISTEN_FDS */
- NULL, /* LISTEN_PID */
- NULL
- };
- char **env_use;
-
+ /* The outer child only has a file system namespace. */
barrier_set_role(&barrier, BARRIER_CHILD);
- envp[n_env] = strv_find_prefix(environ, "TERM=");
- if (envp[n_env])
- n_env ++;
-
master = safe_close(master);
kmsg_socket_pair[0] = safe_close(kmsg_socket_pair[0]);
rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]);
+ pid_socket_pair[0] = safe_close(pid_socket_pair[0]);
reset_all_signal_handlers();
reset_signal_mask();
- if (interactive) {
- close_nointr(STDIN_FILENO);
- close_nointr(STDOUT_FILENO);
- close_nointr(STDERR_FILENO);
-
- r = open_terminal(console, O_RDWR);
- if (r != STDIN_FILENO) {
- if (r >= 0) {
- safe_close(r);
- r = -EINVAL;
- }
-
- log_error_errno(r, "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_errno(errno, "Failed to duplicate console: %m");
- _exit(EXIT_FAILURE);
- }
- }
-
- if (setsid() < 0) {
- log_error_errno(errno, "setsid() failed: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (reset_audit_loginuid() < 0)
- _exit(EXIT_FAILURE);
-
- if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0) {
- log_error_errno(errno, "PR_SET_PDEATHSIG failed: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (arg_private_network)
- loopback_setup();
-
- /* Mark everything as slave, so that we still
- * 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_errno(errno, "MS_SLAVE|MS_REC failed: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (mount_devices(arg_directory,
- root_device, root_device_rw,
- home_device, home_device_rw,
- srv_device, srv_device_rw) < 0)
- _exit(EXIT_FAILURE);
-
- /* Turn directory into bind mount */
- if (mount(arg_directory, arg_directory, NULL, MS_BIND|MS_REC, NULL) < 0) {
- log_error_errno(errno, "Failed to make bind mount: %m");
- _exit(EXIT_FAILURE);
- }
-
- r = setup_volatile(arg_directory);
- if (r < 0)
- _exit(EXIT_FAILURE);
-
- if (setup_volatile_state(arg_directory) < 0)
- _exit(EXIT_FAILURE);
-
- r = base_filesystem_create(arg_directory);
- if (r < 0)
- _exit(EXIT_FAILURE);
-
- if (arg_read_only) {
- r = bind_remount_recursive(arg_directory, true);
- if (r < 0) {
- log_error_errno(r, "Failed to make tree read-only: %m");
- _exit(EXIT_FAILURE);
- }
- }
-
- if (mount_all(arg_directory) < 0)
- _exit(EXIT_FAILURE);
-
- if (copy_devnodes(arg_directory) < 0)
- _exit(EXIT_FAILURE);
-
- if (setup_ptmx(arg_directory) < 0)
- _exit(EXIT_FAILURE);
-
- dev_setup(arg_directory);
-
- if (setup_propagate(arg_directory) < 0)
- _exit(EXIT_FAILURE);
-
- if (setup_seccomp() < 0)
- _exit(EXIT_FAILURE);
-
- if (setup_dev_console(arg_directory, console) < 0)
- _exit(EXIT_FAILURE);
-
- if (setup_kmsg(arg_directory, kmsg_socket_pair[1]) < 0)
- _exit(EXIT_FAILURE);
- kmsg_socket_pair[1] = safe_close(kmsg_socket_pair[1]);
-
- if (send_rtnl(rtnl_socket_pair[1]) < 0)
- _exit(EXIT_FAILURE);
- rtnl_socket_pair[1] = safe_close(rtnl_socket_pair[1]);
-
- /* Tell the parent that we are ready, and that
- * it can cgroupify us to that we lack access
- * to certain devices and resources. */
- (void) barrier_place(&barrier); /* #1 */
-
- if (setup_boot_id(arg_directory) < 0)
- _exit(EXIT_FAILURE);
-
- if (setup_timezone(arg_directory) < 0)
- _exit(EXIT_FAILURE);
-
- if (setup_resolv_conf(arg_directory) < 0)
- _exit(EXIT_FAILURE);
-
- if (setup_journal(arg_directory) < 0)
- _exit(EXIT_FAILURE);
-
- if (mount_custom(arg_directory) < 0)
- _exit(EXIT_FAILURE);
-
- /* Wait until we are cgroup-ified, so that we
- * can mount the right cgroup path writable */
- (void) barrier_place_and_sync(&barrier); /* #2 */
-
- if (mount_cgroup(arg_directory) < 0)
- _exit(EXIT_FAILURE);
-
- if (chdir(arg_directory) < 0) {
- log_error_errno(errno, "chdir(%s) failed: %m", arg_directory);
- _exit(EXIT_FAILURE);
- }
-
- if (mount(arg_directory, "/", NULL, MS_MOVE, NULL) < 0) {
- log_error_errno(errno, "mount(MS_MOVE) failed: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (chroot(".") < 0) {
- log_error_errno(errno, "chroot() failed: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (chdir("/") < 0) {
- log_error_errno(errno, "chdir() failed: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (arg_userns) {
- if (unshare(CLONE_NEWUSER) < 0) {
- log_error_errno(errno, "unshare(CLONE_NEWUSER) failed: %m");
- _exit(EXIT_FAILURE);
- }
-
- /* Tell the parent, that it now can
- * write the UID map. */
- (void) barrier_place(&barrier); /* #3 */
-
- /* Wait until the parent wrote the UID
- * map */
- (void) barrier_place_and_sync(&barrier); /* #4 */
- }
-
- umask(0022);
-
- if (drop_capabilities() < 0) {
- log_error_errno(errno, "drop_capabilities() failed: %m");
- _exit(EXIT_FAILURE);
- }
-
- setup_hostname();
-
- if (arg_personality != 0xffffffffLU) {
- if (personality(arg_personality) < 0) {
- log_error_errno(errno, "personality() failed: %m");
- _exit(EXIT_FAILURE);
- }
- } else if (secondary) {
- if (personality(PER_LINUX32) < 0) {
- log_error_errno(errno, "personality() failed: %m");
- _exit(EXIT_FAILURE);
- }
- }
-
-#ifdef HAVE_SELINUX
- if (arg_selinux_context)
- if (setexeccon((security_context_t) arg_selinux_context) < 0) {
- log_error_errno(errno, "setexeccon(\"%s\") failed: %m", arg_selinux_context);
- _exit(EXIT_FAILURE);
- }
-#endif
-
- r = change_uid_gid(&home);
+ r = outer_child(&barrier,
+ arg_directory,
+ console,
+ root_device, root_device_rw,
+ home_device, home_device_rw,
+ srv_device, srv_device_rw,
+ interactive,
+ secondary,
+ pid_socket_pair[1],
+ kmsg_socket_pair[1],
+ rtnl_socket_pair[1],
+ fds,
+ argc, argv);
if (r < 0)
_exit(EXIT_FAILURE);
- if ((asprintf((char**)(envp + n_env++), "HOME=%s", home ? home: "/root") < 0) ||
- (asprintf((char**)(envp + n_env++), "USER=%s", arg_user ? arg_user : "root") < 0) ||
- (asprintf((char**)(envp + n_env++), "LOGNAME=%s", arg_user ? arg_user : "root") < 0)) {
- log_oom();
- _exit(EXIT_FAILURE);
- }
-
- if (!sd_id128_equal(arg_uuid, SD_ID128_NULL)) {
- char as_uuid[37];
-
- if (asprintf((char**)(envp + n_env++), "container_uuid=%s", id128_format_as_uuid(arg_uuid, as_uuid)) < 0) {
- log_oom();
- _exit(EXIT_FAILURE);
- }
- }
-
- if (fdset_size(fds) > 0) {
- r = fdset_cloexec(fds, false);
- if (r < 0) {
- log_error_errno(r, "Failed to unset O_CLOEXEC for file descriptors.");
- _exit(EXIT_FAILURE);
- }
-
- if ((asprintf((char **)(envp + n_env++), "LISTEN_FDS=%u", n_fd_passed) < 0) ||
- (asprintf((char **)(envp + n_env++), "LISTEN_PID=1") < 0)) {
- log_oom();
- _exit(EXIT_FAILURE);
- }
- }
-
- if (!strv_isempty(arg_setenv)) {
- char **n;
-
- n = strv_env_merge(2, envp, arg_setenv);
- if (!n) {
- log_oom();
- _exit(EXIT_FAILURE);
- }
-
- env_use = n;
- } else
- env_use = (char**) envp;
-
- /* Let the parent know that we are ready and
- * wait until the parent is ready with the
- * setup, too... */
- (void) barrier_place_and_sync(&barrier); /* #5 */
-
- /* Now, explicitly close the log, so that we
- * then can close all remaining fds. Closing
- * the log explicitly first has the benefit
- * that the logging subsystem knows about it,
- * and is thus ready to be reopened should we
- * need it again. Note that the other fds
- * closed here are at least the locking and
- * barrier fds. */
- log_close();
- (void) fdset_close_others(fds);
-
- if (arg_boot) {
- char **a;
- size_t l;
-
- /* Automatically search for the init system */
-
- l = 1 + argc - optind;
- a = newa(char*, l + 1);
- memcpy(a + 1, argv + optind, l * sizeof(char*));
-
- a[0] = (char*) "/usr/lib/systemd/systemd";
- execve(a[0], a, env_use);
-
- a[0] = (char*) "/lib/systemd/systemd";
- execve(a[0], a, env_use);
-
- a[0] = (char*) "/sbin/init";
- execve(a[0], a, env_use);
- } else if (argc > optind)
- execvpe(argv[optind], argv + optind, env_use);
- else {
- chdir(home ? home : "/root");
- execle("/bin/bash", "-bash", NULL, env_use);
- execle("/bin/sh", "-sh", NULL, env_use);
- }
-
- (void) log_open();
- log_error_errno(errno, "execv() failed: %m");
- _exit(EXIT_FAILURE);
+ _exit(EXIT_SUCCESS);
}
barrier_set_role(&barrier, BARRIER_PARENT);
+
fdset_free(fds);
fds = NULL;
kmsg_socket_pair[1] = safe_close(kmsg_socket_pair[1]);
rtnl_socket_pair[1] = safe_close(rtnl_socket_pair[1]);
+ pid_socket_pair[1] = safe_close(pid_socket_pair[1]);
- (void) barrier_place(&barrier); /* #1 */
+ /* Wait for the outer child. */
+ r = wait_for_terminate_and_warn("namespace helper", pid, NULL);
+ if (r < 0)
+ goto finish;
+ if (r != 0) {
+ r = -EIO;
+ goto finish;
+ }
+ pid = 0;
- /* Wait for the most basic Child-setup to be done,
- * before we add hardware to it, and place it in a
- * cgroup. */
- if (barrier_sync(&barrier)) { /* #1 */
- int ifi = 0;
+ /* And now retrieve the PID of the inner child. */
+ l = recv(pid_socket_pair[0], &pid, sizeof(pid), 0);
+ if (l < 0) {
+ r = log_error_errno(errno, "Failed to read inner child PID: %m");
+ goto finish;
+ }
+ if (l != sizeof(pid)) {
+ log_error("Short read while reading inner child PID: %m");
+ r = EIO;
+ goto finish;
+ }
- r = move_network_interfaces(pid);
- if (r < 0)
- goto finish;
+ log_debug("Init process invoked as PID " PID_FMT, pid);
- r = setup_veth(pid, veth_name, &ifi);
- if (r < 0)
+ if (arg_userns) {
+ if (!barrier_place_and_sync(&barrier)) { /* #1 */
+ log_error("Child died too early.");
+ r = -ESRCH;
goto finish;
+ }
- r = setup_bridge(veth_name, &ifi);
+ r = setup_uid_map(pid);
if (r < 0)
goto finish;
- r = setup_macvlan(pid);
- if (r < 0)
- goto finish;
+ (void) barrier_place(&barrier); /* #2 */
+ }
- r = setup_ipvlan(pid);
- if (r < 0)
- goto finish;
+ r = move_network_interfaces(pid);
+ if (r < 0)
+ goto finish;
- r = register_machine(pid, ifi);
- if (r < 0)
- goto finish;
+ r = setup_veth(pid, veth_name, &ifi);
+ if (r < 0)
+ goto finish;
- /* 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. */
- (void) barrier_place(&barrier); /* #2 */
+ r = setup_bridge(veth_name, &ifi);
+ if (r < 0)
+ goto finish;
- if (arg_userns) {
- char uid_map[strlen("/proc//uid_map") + DECIMAL_STR_MAX(uid_t) + 1], line[DECIMAL_STR_MAX(uid_t)*3+3+1];
+ r = setup_macvlan(pid);
+ if (r < 0)
+ goto finish;
- (void) barrier_place_and_sync(&barrier); /* #3 */
+ r = setup_ipvlan(pid);
+ if (r < 0)
+ goto finish;
- xsprintf(uid_map, "/proc/" PID_FMT "/uid_map", pid);
- xsprintf(line, UID_FMT " " UID_FMT " " UID_FMT "\n", 0, arg_uid_shift, arg_uid_range);
- r = write_string_file(uid_map, line);
- if (r < 0) {
- log_error_errno(r, "Failed to write UID map: %m");
- goto finish;
- }
+ r = register_machine(pid, ifi);
+ if (r < 0)
+ goto finish;
- /* We always assign the same UID and GID ranges */
- xsprintf(uid_map, "/proc/" PID_FMT "/gid_map", pid);
- r = write_string_file(uid_map, line);
- if (r < 0) {
- log_error_errno(r, "Failed to write GID map: %m");
- goto finish;
- }
+ r = chown_cgroup(pid);
+ if (r < 0)
+ goto finish;
- (void) barrier_place(&barrier); /* #4 */
- }
+ /* Notify the child that the parent is ready with all
+ * its setup (including cgroup-ification), and that
+ * the child can now hand over control to the code to
+ * run inside the container. */
+ (void) barrier_place(&barrier); /* #3 */
- /* Block SIGCHLD here, before notifying child.
- * process_pty() will handle it with the other signals. */
- r = sigprocmask(SIG_BLOCK, &mask_chld, NULL);
- if (r < 0)
- goto finish;
+ /* Block SIGCHLD here, before notifying child.
+ * process_pty() will handle it with the other signals. */
+ assert_se(sigprocmask(SIG_BLOCK, &mask_chld, NULL) >= 0);
- /* Reset signal to default */
- r = default_signals(SIGCHLD, -1);
- if (r < 0)
- goto finish;
+ /* Reset signal to default */
+ r = default_signals(SIGCHLD, -1);
+ if (r < 0) {
+ log_error_errno(r, "Failed to reset SIGCHLD: %m");
+ goto finish;
+ }
- /* Let the child know that we are ready and wait that the child is completely ready now. */
- if (barrier_place_and_sync(&barrier)) { /* #5 */
- _cleanup_event_unref_ sd_event *event = NULL;
- _cleanup_(pty_forward_freep) PTYForward *forward = NULL;
- _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
- char last_char = 0;
+ /* Let the child know that we are ready and wait that the child is completely ready now. */
+ if (!barrier_place_and_sync(&barrier)) { /* #5 */
+ log_error("Client died too early.");
+ r = -ESRCH;
+ goto finish;
+ }
- sd_notifyf(false,
- "READY=1\n"
- "STATUS=Container running.\n"
- "X_NSPAWN_LEADER_PID=" PID_FMT, pid);
+ sd_notifyf(false,
+ "READY=1\n"
+ "STATUS=Container running.\n"
+ "X_NSPAWN_LEADER_PID=" PID_FMT, pid);
- r = sd_event_new(&event);
- if (r < 0) {
- log_error_errno(r, "Failed to get default event source: %m");
- goto finish;
- }
+ r = sd_event_new(&event);
+ if (r < 0) {
+ log_error_errno(r, "Failed to get default event source: %m");
+ goto finish;
+ }
- if (arg_kill_signal > 0) {
- /* 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);
- }
+ if (arg_kill_signal > 0) {
+ /* 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);
+ /* simply exit on sigchld */
+ sd_event_add_signal(event, NULL, SIGCHLD, NULL, NULL);
- if (arg_expose_ports) {
- r = watch_rtnl(event, rtnl_socket_pair[0], &exposed, &rtnl);
- if (r < 0)
- goto finish;
+ if (arg_expose_ports) {
+ r = watch_rtnl(event, rtnl_socket_pair[0], &exposed, &rtnl);
+ if (r < 0)
+ goto finish;
- (void) expose_ports(rtnl, &exposed);
- }
+ (void) expose_ports(rtnl, &exposed);
+ }
- rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]);
+ rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]);
- r = pty_forward_new(event, master, true, !interactive, &forward);
- if (r < 0) {
- log_error_errno(r, "Failed to create PTY forwarder: %m");
- goto finish;
- }
+ r = pty_forward_new(event, master, true, !interactive, &forward);
+ if (r < 0) {
+ log_error_errno(r, "Failed to create PTY forwarder: %m");
+ goto finish;
+ }
- r = sd_event_loop(event);
- if (r < 0) {
- log_error_errno(r, "Failed to run event loop: %m");
- goto finish;
- }
+ r = sd_event_loop(event);
+ if (r < 0) {
+ log_error_errno(r, "Failed to run event loop: %m");
+ goto finish;
+ }
- pty_forward_get_last_char(forward, &last_char);
+ pty_forward_get_last_char(forward, &last_char);
- forward = pty_forward_free(forward);
+ forward = pty_forward_free(forward);
- if (!arg_quiet && last_char != '\n')
- putc('\n', stdout);
+ if (!arg_quiet && last_char != '\n')
+ putc('\n', stdout);
- /* Kill if it is not dead yet anyway */
- terminate_machine(pid);
- }
- }
+ /* Kill if it is not dead yet anyway */
+ terminate_machine(pid);
/* Normally redundant, but better safe than sorry */
kill(pid, SIGKILL);
@@ -4737,11 +4960,11 @@ finish:
"STOPPING=1\n"
"STATUS=Terminating...");
- loop_remove(loop_nr, &image_fd);
-
if (pid > 0)
kill(pid, SIGKILL);
+ loop_remove(loop_nr, &image_fd);
+
if (remove_subvol && arg_directory) {
int k;
diff --git a/src/remount-fs/remount-fs.c b/src/remount-fs/remount-fs.c
index a09531b26f..e701fc9fae 100644
--- a/src/remount-fs/remount-fs.c
+++ b/src/remount-fs/remount-fs.c
@@ -29,6 +29,7 @@
#include "log.h"
#include "util.h"
#include "path-util.h"
+#include "signal-util.h"
#include "mount-setup.h"
#include "exit-status.h"
diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c
index 271247ca3f..e283d8a749 100644
--- a/src/resolve/resolved.c
+++ b/src/resolve/resolved.c
@@ -24,6 +24,7 @@
#include "mkdir.h"
#include "capability.h"
#include "selinux-util.h"
+#include "signal-util.h"
#include "resolved-manager.h"
#include "resolved-conf.h"
diff --git a/src/run/run.c b/src/run/run.c
index fcd6b06f7d..5b9f31c4aa 100644
--- a/src/run/run.c
+++ b/src/run/run.c
@@ -35,6 +35,7 @@
#include "calendarspec.h"
#include "ptyfwd.h"
#include "formats-util.h"
+#include "signal-util.h"
static bool arg_scope = false;
static bool arg_remain_after_exit = false;
diff --git a/src/shared/architecture.c b/src/shared/architecture.c
index 884abdd3ea..8e72e7a36a 100644
--- a/src/shared/architecture.c
+++ b/src/shared/architecture.c
@@ -35,7 +35,7 @@ int uname_architecture(void) {
* 1:1. Instead we try to clean it up and break down the
* confusion on x86 and arm in particular.
*
- * We do not try to distuingish CPUs not CPU features, but
+ * We do not try to distinguish CPUs not CPU features, but
* actual architectures, i.e. that have genuinely different
* code. */
diff --git a/src/shared/architecture.h b/src/shared/architecture.h
index cb82418a5e..f5bbf65a90 100644
--- a/src/shared/architecture.h
+++ b/src/shared/architecture.h
@@ -27,7 +27,7 @@
/* A cleaned up architecture definition. We don't want to get lost in
* processor features, models, generations or even ABIs. Hence we
- * focus on general family, and distuignish word width and
+ * focus on general family, and distinguish word width and
* endianness. */
enum {
diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c
index a3a2e51bb9..ef3788be68 100644
--- a/src/shared/ask-password-api.c
+++ b/src/shared/ask-password-api.c
@@ -37,7 +37,7 @@
#include "strv.h"
#include "random-util.h"
#include "terminal-util.h"
-
+#include "signal-util.h"
#include "ask-password-api.h"
static void backspace_chars(int ttyfd, size_t p) {
diff --git a/src/shared/base-filesystem.c b/src/shared/base-filesystem.c
index 11e0947407..ab6fc171b0 100644
--- a/src/shared/base-filesystem.c
+++ b/src/shared/base-filesystem.c
@@ -41,13 +41,16 @@ static const BaseFilesystem table[] = {
{ "lib", 0, "usr/lib\0", NULL },
{ "root", 0755, NULL, NULL },
{ "sbin", 0, "usr/sbin\0", NULL },
+ { "usr", 0755, NULL, NULL },
+ { "var", 0755, NULL, NULL },
+ { "etc", 0755, NULL, NULL },
#if defined(__i386__) || defined(__x86_64__)
{ "lib64", 0, "usr/lib/x86_64-linux-gnu\0"
"usr/lib64\0", "ld-linux-x86-64.so.2" },
#endif
};
-int base_filesystem_create(const char *root) {
+int base_filesystem_create(const char *root, uid_t uid, gid_t gid) {
_cleanup_close_ int fd = -1;
unsigned i;
int r = 0;
@@ -90,6 +93,12 @@ int base_filesystem_create(const char *root) {
r = symlinkat(target, fd, table[i].dir);
if (r < 0 && errno != EEXIST)
return log_error_errno(errno, "Failed to create symlink at %s/%s: %m", root, table[i].dir);
+
+ if (uid != UID_INVALID || gid != UID_INVALID) {
+ if (fchownat(fd, table[i].dir, uid, gid, AT_SYMLINK_NOFOLLOW) < 0)
+ return log_error_errno(errno, "Failed to chown symlink at %s/%s: %m", root, table[i].dir);
+ }
+
continue;
}
@@ -97,6 +106,11 @@ int base_filesystem_create(const char *root) {
r = mkdirat(fd, table[i].dir, table[i].mode);
if (r < 0 && errno != EEXIST)
return log_error_errno(errno, "Failed to create directory at %s/%s: %m", root, table[i].dir);
+
+ if (uid != UID_INVALID || gid != UID_INVALID) {
+ if (fchownat(fd, table[i].dir, uid, gid, AT_SYMLINK_NOFOLLOW) < 0)
+ return log_error_errno(errno, "Failed to chown directory at %s/%s: %m", root, table[i].dir);
+ }
}
return 0;
diff --git a/src/shared/base-filesystem.h b/src/shared/base-filesystem.h
index 03201f7083..39a496090f 100644
--- a/src/shared/base-filesystem.h
+++ b/src/shared/base-filesystem.h
@@ -21,4 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-int base_filesystem_create(const char *root);
+#include <sys/types.h>
+
+int base_filesystem_create(const char *root, uid_t uid, gid_t gid);
diff --git a/src/shared/capability.h b/src/shared/capability.h
index 8260ae1a81..4eb5c2a835 100644
--- a/src/shared/capability.h
+++ b/src/shared/capability.h
@@ -31,7 +31,7 @@ int have_effective_cap(int value);
int capability_bounding_set_drop(uint64_t drop, bool right_now);
int capability_bounding_set_drop_usermode(uint64_t drop);
-int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilites);
+int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities);
int drop_capability(cap_value_t cv);
diff --git a/src/shared/cgroup-util.c b/src/shared/cgroup-util.c
index c0b0ca4cf2..9988e5c574 100644
--- a/src/shared/cgroup-util.c
+++ b/src/shared/cgroup-util.c
@@ -489,7 +489,7 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch
if (_unlikely_(!good)) {
int r;
- r = path_is_mount_point("/sys/fs/cgroup", false);
+ r = path_is_mount_point("/sys/fs/cgroup", 0);
if (r < 0)
return r;
if (r == 0)
diff --git a/src/shared/condition.c b/src/shared/condition.c
index 9f2574c2f6..24871b0dae 100644
--- a/src/shared/condition.c
+++ b/src/shared/condition.c
@@ -349,7 +349,7 @@ static int condition_test_path_is_mount_point(Condition *c) {
assert(c->parameter);
assert(c->type == CONDITION_PATH_IS_MOUNT_POINT);
- return path_is_mount_point(c->parameter, true) > 0;
+ return path_is_mount_point(c->parameter, AT_SYMLINK_FOLLOW) > 0;
}
static int condition_test_path_is_read_write(Condition *c) {
diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c
index 2c855157a9..7370c786f9 100644
--- a/src/shared/conf-parser.c
+++ b/src/shared/conf-parser.c
@@ -444,7 +444,7 @@ int config_parse_many(const char *conf_file,
if (r < 0) \
log_syntax(unit, LOG_ERR, filename, line, -r, \
"Failed to parse %s value, ignoring: %s", \
- #vartype, rvalue); \
+ #type, rvalue); \
\
return 0; \
}
diff --git a/src/shared/dev-setup.c b/src/shared/dev-setup.c
index cb15da8a5d..25ad918b85 100644
--- a/src/shared/dev-setup.c
+++ b/src/shared/dev-setup.c
@@ -23,13 +23,12 @@
#include <stdlib.h>
#include <unistd.h>
-#include "dev-setup.h"
#include "util.h"
#include "label.h"
+#include "path-util.h"
+#include "dev-setup.h"
-int dev_setup(const char *prefix) {
- const char *j, *k;
-
+int dev_setup(const char *prefix, uid_t uid, gid_t gid) {
static const char symlinks[] =
"-/proc/kcore\0" "/dev/core\0"
"/proc/self/fd\0" "/dev/fd\0"
@@ -37,7 +36,13 @@ int dev_setup(const char *prefix) {
"/proc/self/fd/1\0" "/dev/stdout\0"
"/proc/self/fd/2\0" "/dev/stderr\0";
+ const char *j, *k;
+ int r;
+
NULSTR_FOREACH_PAIR(j, k, symlinks) {
+ _cleanup_free_ char *link_name = NULL;
+ const char *n;
+
if (j[0] == '-') {
j++;
@@ -46,15 +51,21 @@ int dev_setup(const char *prefix) {
}
if (prefix) {
- _cleanup_free_ char *link_name = NULL;
-
- link_name = strjoin(prefix, "/", k, NULL);
+ link_name = prefix_root(prefix, k);
if (!link_name)
return -ENOMEM;
- symlink_label(j, link_name);
+ n = link_name;
} else
- symlink_label(j, k);
+ n = k;
+
+ r = symlink_label(j, n);
+ if (r < 0)
+ log_debug_errno(r, "Failed to symlink %s to %s: %m", j, n);
+
+ if (uid != UID_INVALID || gid != GID_INVALID)
+ if (lchown(n, uid, gid) < 0)
+ log_debug_errno(errno, "Failed to chown %s: %m", n);
}
return 0;
diff --git a/src/shared/dev-setup.h b/src/shared/dev-setup.h
index d41b6eefba..ab2748db7f 100644
--- a/src/shared/dev-setup.h
+++ b/src/shared/dev-setup.h
@@ -21,4 +21,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-int dev_setup(const char *pathprefix);
+#include <sys/types.h>
+
+int dev_setup(const char *prefix, uid_t uid, gid_t gid);
diff --git a/src/shared/fdset.c b/src/shared/fdset.c
index 31849272bd..6101b628ec 100644
--- a/src/shared/fdset.c
+++ b/src/shared/fdset.c
@@ -32,7 +32,7 @@
#define MAKE_SET(s) ((Set*) s)
#define MAKE_FDSET(s) ((FDSet*) s)
-/* Make sure we can distuingish fd 0 and NULL */
+/* Make sure we can distinguish fd 0 and NULL */
#define FD_TO_PTR(fd) INT_TO_PTR((fd)+1)
#define PTR_TO_FD(p) (PTR_TO_INT(p)-1)
diff --git a/src/shared/generator.c b/src/shared/generator.c
index 81284995f5..807569a1b8 100644
--- a/src/shared/generator.c
+++ b/src/shared/generator.c
@@ -61,7 +61,7 @@ static int write_fsck_sysroot_service(const char *dir, const char *what) {
"[Service]\n"
"Type=oneshot\n"
"RemainAfterExit=yes\n"
- "ExecStart=/usr/lib/systemd/systemd-fsck %2$s\n"
+ "ExecStart=" SYSTEMD_FSCK_PATH " %2$s\n"
"TimeoutSec=0\n",
program_invocation_short_name,
what,
diff --git a/src/shared/hostname-util.c b/src/shared/hostname-util.c
index 2998fdf2c7..e336f269fa 100644
--- a/src/shared/hostname-util.c
+++ b/src/shared/hostname-util.c
@@ -158,3 +158,36 @@ int sethostname_idempotent(const char *s) {
return 1;
}
+
+int read_hostname_config(const char *path, char **hostname) {
+ _cleanup_fclose_ FILE *f = NULL;
+ char l[LINE_MAX];
+ char *name = NULL;
+
+ assert(path);
+ assert(hostname);
+
+ f = fopen(path, "re");
+ if (!f)
+ return -errno;
+
+ /* may have comments, ignore them */
+ FOREACH_LINE(l, f, return -errno) {
+ truncate_nl(l);
+ if (l[0] != '\0' && l[0] != '#') {
+ /* found line with value */
+ name = hostname_cleanup(l, false);
+ name = strdup(name);
+ if (!name)
+ return -ENOMEM;
+ break;
+ }
+ }
+
+ if (!name)
+ /* no non-empty line found */
+ return -ENOENT;
+
+ *hostname = name;
+ return 0;
+}
diff --git a/src/shared/hostname-util.h b/src/shared/hostname-util.h
index f2821c3078..0c4763cf5a 100644
--- a/src/shared/hostname-util.h
+++ b/src/shared/hostname-util.h
@@ -35,3 +35,5 @@ char* hostname_cleanup(char *s, bool lowercase);
bool is_localhost(const char *hostname);
int sethostname_idempotent(const char *s);
+
+int read_hostname_config(const char *path, char **hostname);
diff --git a/src/shared/import-util.c b/src/shared/import-util.c
index 660d92ac5d..001a8a37e8 100644
--- a/src/shared/import-util.c
+++ b/src/shared/import-util.c
@@ -150,6 +150,27 @@ int raw_strip_suffixes(const char *p, char **ret) {
return 0;
}
+bool dkr_digest_is_valid(const char *digest) {
+ /* 7 chars for prefix, 64 chars for the digest itself */
+ if (strlen(digest) != 71)
+ return false;
+
+ return startswith(digest, "sha256:") && in_charset(digest + 7, "0123456789abcdef");
+}
+
+bool dkr_ref_is_valid(const char *ref) {
+ const char *colon;
+
+ if (isempty(ref))
+ return false;
+
+ colon = strchr(ref, ':');
+ if (!colon)
+ return filename_is_valid(ref);
+
+ return dkr_digest_is_valid(ref);
+}
+
bool dkr_name_is_valid(const char *name) {
const char *slash, *p;
diff --git a/src/shared/import-util.h b/src/shared/import-util.h
index ff155b0ff2..7bf7d4ca40 100644
--- a/src/shared/import-util.h
+++ b/src/shared/import-util.h
@@ -44,4 +44,6 @@ int raw_strip_suffixes(const char *name, char **ret);
bool dkr_name_is_valid(const char *name);
bool dkr_id_is_valid(const char *id);
+bool dkr_ref_is_valid(const char *ref);
+bool dkr_digest_is_valid(const char *digest);
#define dkr_tag_is_valid(tag) filename_is_valid(tag)
diff --git a/src/shared/json.c b/src/shared/json.c
index 45c8ecedb3..be40a0d203 100644
--- a/src/shared/json.c
+++ b/src/shared/json.c
@@ -21,17 +21,178 @@
#include <sys/types.h>
#include <math.h>
-
#include "macro.h"
-#include "util.h"
#include "utf8.h"
#include "json.h"
-enum {
- STATE_NULL,
- STATE_VALUE,
- STATE_VALUE_POST,
-};
+int json_variant_new(JsonVariant **ret, JsonVariantType type) {
+ JsonVariant *v;
+
+ v = new0(JsonVariant, 1);
+ if (!v)
+ return -ENOMEM;
+ v->type = type;
+ *ret = v;
+ return 0;
+}
+
+static int json_variant_deep_copy(JsonVariant *ret, JsonVariant *variant) {
+ int r;
+
+ assert(ret);
+ assert(variant);
+
+ ret->type = variant->type;
+ ret->size = variant->size;
+
+ if (variant->type == JSON_VARIANT_STRING) {
+ ret->string = memdup(variant->string, variant->size+1);
+ if (!ret->string)
+ return -ENOMEM;
+ } else if (variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT) {
+ size_t i;
+
+ ret->objects = new0(JsonVariant, variant->size);
+ if (!ret->objects)
+ return -ENOMEM;
+
+ for (i = 0; i < variant->size; ++i) {
+ r = json_variant_deep_copy(&ret->objects[i], &variant->objects[i]);
+ if (r < 0)
+ return r;
+ }
+ } else
+ ret->value = variant->value;
+
+ return 0;
+}
+
+static JsonVariant *json_object_unref(JsonVariant *variant);
+
+static JsonVariant *json_variant_unref_inner(JsonVariant *variant) {
+ if (!variant)
+ return NULL;
+
+ if (variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT)
+ return json_object_unref(variant);
+ else if (variant->type == JSON_VARIANT_STRING)
+ free(variant->string);
+
+ return NULL;
+}
+
+static JsonVariant *json_raw_unref(JsonVariant *variant, size_t size) {
+ if (!variant)
+ return NULL;
+
+ for (size_t i = 0; i < size; ++i)
+ json_variant_unref_inner(&variant[i]);
+
+ free(variant);
+ return NULL;
+}
+
+static JsonVariant *json_object_unref(JsonVariant *variant) {
+ size_t i;
+
+ assert(variant);
+
+ if (!variant->objects)
+ return NULL;
+
+ for (i = 0; i < variant->size; ++i)
+ json_variant_unref_inner(&variant->objects[i]);
+
+ free(variant->objects);
+ return NULL;
+}
+
+static JsonVariant **json_variant_array_unref(JsonVariant **variant) {
+ size_t i = 0;
+ JsonVariant *p = NULL;
+
+ if (!variant)
+ return NULL;
+
+ while((p = (variant[i++])) != NULL) {
+ if (p->type == JSON_VARIANT_STRING)
+ free(p->string);
+ free(p);
+ }
+
+ free(variant);
+
+ return NULL;
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(JsonVariant **, json_variant_array_unref);
+
+JsonVariant *json_variant_unref(JsonVariant *variant) {
+ if (!variant)
+ return NULL;
+
+ if (variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT)
+ json_object_unref(variant);
+ else if (variant->type == JSON_VARIANT_STRING)
+ free(variant->string);
+
+ free(variant);
+
+ return NULL;
+}
+
+char *json_variant_string(JsonVariant *variant){
+ assert(variant);
+ assert(variant->type == JSON_VARIANT_STRING);
+
+ return variant->string;
+}
+
+bool json_variant_bool(JsonVariant *variant) {
+ assert(variant);
+ assert(variant->type == JSON_VARIANT_BOOLEAN);
+
+ return variant->value.boolean;
+}
+
+intmax_t json_variant_integer(JsonVariant *variant) {
+ assert(variant);
+ assert(variant->type == JSON_VARIANT_INTEGER);
+
+ return variant->value.integer;
+}
+
+double json_variant_real(JsonVariant *variant) {
+ assert(variant);
+ assert(variant->type == JSON_VARIANT_REAL);
+
+ return variant->value.real;
+}
+
+JsonVariant *json_variant_element(JsonVariant *variant, unsigned index) {
+ assert(variant);
+ assert(variant->type == JSON_VARIANT_ARRAY || variant->type == JSON_VARIANT_OBJECT);
+ assert(index < variant->size);
+ assert(variant->objects);
+
+ return &variant->objects[index];
+}
+
+JsonVariant *json_variant_value(JsonVariant *variant, const char *key) {
+ size_t i;
+
+ assert(variant);
+ assert(variant->type == JSON_VARIANT_OBJECT);
+ assert(variant->objects);
+
+ for (i = 0; i < variant->size; i += 2) {
+ JsonVariant *p = &variant->objects[i];
+ if (p->type == JSON_VARIANT_STRING && streq(key, p->string))
+ return &variant->objects[i + 1];
+ }
+
+ return NULL;
+}
static void inc_lines(unsigned *line, const char *s, size_t n) {
const char *p = s;
@@ -285,9 +446,6 @@ static int json_parse_number(const char **p, union json_value *ret) {
} while (strchr("0123456789", *c) && *c != 0);
}
- if (*c != 0)
- return -EINVAL;
-
*p = c;
if (is_double) {
@@ -310,6 +468,12 @@ int json_tokenize(
int t;
int r;
+ enum {
+ STATE_NULL,
+ STATE_VALUE,
+ STATE_VALUE_POST,
+ };
+
assert(p);
assert(*p);
assert(ret_string);
@@ -443,3 +607,260 @@ int json_tokenize(
}
}
+
+static bool json_is_value(JsonVariant *var) {
+ assert(var);
+
+ return var->type != JSON_VARIANT_CONTROL;
+}
+
+static int json_scoped_parse(JsonVariant **tokens, size_t *i, size_t n, JsonVariant *scope) {
+ bool arr = scope->type == JSON_VARIANT_ARRAY;
+ int terminator = arr ? JSON_ARRAY_CLOSE : JSON_OBJECT_CLOSE;
+ size_t allocated = 0, size = 0;
+ JsonVariant *key = NULL, *value = NULL, *var = NULL, *items = NULL;
+ enum {
+ STATE_KEY,
+ STATE_COLON,
+ STATE_COMMA,
+ STATE_VALUE
+ } state = arr ? STATE_VALUE : STATE_KEY;
+
+ assert(tokens);
+ assert(i);
+ assert(scope);
+
+ while((var = *i < n ? tokens[(*i)++] : NULL) != NULL) {
+ bool stopper;
+ int r;
+
+ stopper = !json_is_value(var) && var->value.integer == terminator;
+
+ if (stopper) {
+ if (state != STATE_COMMA && size > 0)
+ goto error;
+
+ goto out;
+ }
+
+ if (state == STATE_KEY) {
+ if (var->type != JSON_VARIANT_STRING)
+ goto error;
+ else {
+ key = var;
+ state = STATE_COLON;
+ }
+ }
+ else if (state == STATE_COLON) {
+ if (key == NULL)
+ goto error;
+
+ if (json_is_value(var))
+ goto error;
+
+ if (var->value.integer != JSON_COLON)
+ goto error;
+
+ state = STATE_VALUE;
+ }
+ else if (state == STATE_VALUE) {
+ _cleanup_json_variant_unref_ JsonVariant *v = NULL;
+ size_t toadd = arr ? 1 : 2;
+
+ if (!json_is_value(var)) {
+ int type = (var->value.integer == JSON_ARRAY_OPEN) ? JSON_VARIANT_ARRAY : JSON_VARIANT_OBJECT;
+
+ r = json_variant_new(&v, type);
+ if (r < 0)
+ goto error;
+
+ r = json_scoped_parse(tokens, i, n, v);
+ if (r < 0)
+ goto error;
+
+ value = v;
+ }
+ else
+ value = var;
+
+ if(!GREEDY_REALLOC(items, allocated, size + toadd))
+ goto error;
+
+ if (arr) {
+ r = json_variant_deep_copy(&items[size], value);
+ if (r < 0)
+ goto error;
+ } else {
+ r = json_variant_deep_copy(&items[size], key);
+ if (r < 0)
+ goto error;
+
+ r = json_variant_deep_copy(&items[size+1], value);
+ if (r < 0)
+ goto error;
+ }
+
+ size += toadd;
+ state = STATE_COMMA;
+ }
+ else if (state == STATE_COMMA) {
+ if (json_is_value(var))
+ goto error;
+
+ if (var->value.integer != JSON_COMMA)
+ goto error;
+
+ key = NULL;
+ value = NULL;
+
+ state = arr ? STATE_VALUE : STATE_KEY;
+ }
+ }
+
+error:
+ json_raw_unref(items, size);
+ return -EBADMSG;
+
+out:
+ scope->size = size;
+ scope->objects = items;
+
+ return scope->type;
+}
+
+static int json_parse_tokens(JsonVariant **tokens, size_t ntokens, JsonVariant **rv) {
+ size_t it = 0;
+ int r;
+ JsonVariant *e;
+ _cleanup_json_variant_unref_ JsonVariant *p = NULL;
+
+ assert(tokens);
+ assert(ntokens);
+
+ e = tokens[it++];
+ r = json_variant_new(&p, JSON_VARIANT_OBJECT);
+ if (r < 0)
+ return r;
+
+ if (e->type != JSON_VARIANT_CONTROL && e->value.integer != JSON_OBJECT_OPEN)
+ return -EBADMSG;
+
+ r = json_scoped_parse(tokens, &it, ntokens, p);
+ if (r < 0)
+ return r;
+
+ *rv = p;
+ p = NULL;
+
+ return 0;
+}
+
+static int json_tokens(const char *string, size_t size, JsonVariant ***tokens, size_t *n) {
+ _cleanup_free_ char *buf = NULL;
+ _cleanup_(json_variant_array_unrefp) JsonVariant **items = NULL;
+ union json_value v = {};
+ void *json_state = NULL;
+ const char *p;
+ int t, r;
+ size_t allocated = 0, s = 0;
+
+ assert(string);
+ assert(n);
+
+ if (size <= 0)
+ return -EBADMSG;
+
+ buf = strndup(string, size);
+ if (!buf)
+ return -ENOMEM;
+
+ p = buf;
+ for (;;) {
+ _cleanup_json_variant_unref_ JsonVariant *var = NULL;
+ _cleanup_free_ char *rstr = NULL;
+
+ t = json_tokenize(&p, &rstr, &v, &json_state, NULL);
+
+ if (t < 0)
+ return t;
+ else if (t == JSON_END)
+ break;
+
+ if (t <= JSON_ARRAY_CLOSE) {
+ r = json_variant_new(&var, JSON_VARIANT_CONTROL);
+ if (r < 0)
+ return r;
+ var->value.integer = t;
+ } else {
+ switch (t) {
+ case JSON_STRING:
+ r = json_variant_new(&var, JSON_VARIANT_STRING);
+ if (r < 0)
+ return r;
+ var->size = strlen(rstr);
+ var->string = strdup(rstr);
+ if (!var->string) {
+ return -ENOMEM;
+ }
+ break;
+ case JSON_INTEGER:
+ r = json_variant_new(&var, JSON_VARIANT_INTEGER);
+ if (r < 0)
+ return r;
+ var->value = v;
+ break;
+ case JSON_REAL:
+ r = json_variant_new(&var, JSON_VARIANT_REAL);
+ if (r < 0)
+ return r;
+ var->value = v;
+ break;
+ case JSON_BOOLEAN:
+ r = json_variant_new(&var, JSON_VARIANT_BOOLEAN);
+ if (r < 0)
+ return r;
+ var->value = v;
+ break;
+ case JSON_NULL:
+ r = json_variant_new(&var, JSON_VARIANT_NULL);
+ if (r < 0)
+ return r;
+ break;
+ }
+ }
+
+ if (!GREEDY_REALLOC(items, allocated, s+2))
+ return -ENOMEM;
+
+ items[s++] = var;
+ items[s] = NULL;
+ var = NULL;
+ }
+
+ *n = s;
+ *tokens = items;
+ items = NULL;
+
+ return 0;
+}
+
+int json_parse(const char *string, JsonVariant **rv) {
+ _cleanup_(json_variant_array_unrefp) JsonVariant **s = NULL;
+ JsonVariant *v = NULL;
+ size_t n = 0;
+ int r;
+
+ assert(string);
+ assert(rv);
+
+ r = json_tokens(string, strlen(string), &s, &n);
+ if (r < 0)
+ return r;
+
+ r = json_parse_tokens(s, n, &v);
+ if (r < 0)
+ return r;
+
+ *rv = v;
+ return 0;
+}
diff --git a/src/shared/json.h b/src/shared/json.h
index 55976d513b..e0b4d810b5 100644
--- a/src/shared/json.h
+++ b/src/shared/json.h
@@ -22,6 +22,7 @@
***/
#include <stdbool.h>
+#include "util.h"
enum {
JSON_END,
@@ -38,12 +39,50 @@ enum {
JSON_NULL,
};
+typedef enum {
+ JSON_VARIANT_CONTROL,
+ JSON_VARIANT_STRING,
+ JSON_VARIANT_INTEGER,
+ JSON_VARIANT_BOOLEAN,
+ JSON_VARIANT_REAL,
+ JSON_VARIANT_ARRAY,
+ JSON_VARIANT_OBJECT,
+ JSON_VARIANT_NULL
+} JsonVariantType;
+
union json_value {
bool boolean;
double real;
intmax_t integer;
};
+typedef struct JsonVariant {
+ JsonVariantType type;
+ size_t size;
+ union {
+ char *string;
+ struct JsonVariant *objects;
+ union json_value value;
+ };
+} JsonVariant;
+
+int json_variant_new(JsonVariant **ret, JsonVariantType type);
+JsonVariant *json_variant_unref(JsonVariant *v);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(JsonVariant *, json_variant_unref);
+#define _cleanup_json_variant_unref_ _cleanup_(json_variant_unrefp)
+
+char *json_variant_string(JsonVariant *v);
+bool json_variant_bool(JsonVariant *v);
+intmax_t json_variant_integer(JsonVariant *v);
+double json_variant_real(JsonVariant *v);
+
+JsonVariant *json_variant_element(JsonVariant *v, unsigned index);
+JsonVariant *json_variant_value(JsonVariant *v, const char *key);
+
#define JSON_VALUE_NULL ((union json_value) {})
int json_tokenize(const char **p, char **ret_string, union json_value *ret_value, void **state, unsigned *line);
+
+int json_parse(const char *string, JsonVariant **rv);
+int json_parse_measure(const char *string, size_t *size);
diff --git a/src/shared/log.c b/src/shared/log.c
index 6168a2955d..b96afc4de4 100644
--- a/src/shared/log.c
+++ b/src/shared/log.c
@@ -38,6 +38,7 @@
#include "formats-util.h"
#include "process-util.h"
#include "terminal-util.h"
+#include "signal-util.h"
#define SNDBUF_SIZE (8*1024*1024)
diff --git a/src/shared/machine-pool.c b/src/shared/machine-pool.c
index 9920d150ab..d27931cb4a 100644
--- a/src/shared/machine-pool.c
+++ b/src/shared/machine-pool.c
@@ -30,6 +30,7 @@
#include "mkdir.h"
#include "btrfs-util.h"
#include "path-util.h"
+#include "signal-util.h"
#include "machine-pool.h"
#define VAR_LIB_MACHINES_SIZE_START (1024UL*1024UL*500UL)
@@ -198,7 +199,7 @@ int setup_machine_directory(uint64_t size, sd_bus_error *error) {
return 0;
}
- if (path_is_mount_point("/var/lib/machines", true) > 0 ||
+ if (path_is_mount_point("/var/lib/machines", AT_SYMLINK_FOLLOW) > 0 ||
dir_is_empty("/var/lib/machines") == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "/var/lib/machines is not a btrfs file system. Operation is not supported on legacy file systems.");
diff --git a/src/shared/missing.h b/src/shared/missing.h
index 8ca6f8edb6..9194009491 100644
--- a/src/shared/missing.h
+++ b/src/shared/missing.h
@@ -713,7 +713,7 @@ static inline int setns(int fd, int nstype) {
#define IFLA_VLAN_MAX (__IFLA_VLAN_MAX - 1)
#endif
-#if !HAVE_DECL_IFLA_VXLAN_LOCAL6
+#if !HAVE_DECL_IFLA_VXLAN_REMCSUM_NOPARTIAL
#define IFLA_VXLAN_UNSPEC 0
#define IFLA_VXLAN_ID 1
#define IFLA_VXLAN_GROUP 2
@@ -732,7 +732,14 @@ static inline int setns(int fd, int nstype) {
#define IFLA_VXLAN_PORT 15
#define IFLA_VXLAN_GROUP6 16
#define IFLA_VXLAN_LOCAL6 17
-#define __IFLA_VXLAN_MAX 18
+#define IFLA_VXLAN_UDP_CSUM 18
+#define IFLA_VXLAN_UDP_ZERO_CSUM6_TX 19
+#define IFLA_VXLAN_UDP_ZERO_CSUM6_RX 20
+#define IFLA_VXLAN_REMCSUM_TX 21
+#define IFLA_VXLAN_REMCSUM_RX 22
+#define IFLA_VXLAN_GBP 23
+#define IFLA_VXLAN_REMCSUM_NOPARTIAL 24
+#define __IFLA_VXLAN_MAX 25
#define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1)
#endif
diff --git a/src/shared/path-util.c b/src/shared/path-util.c
index 7090989fcb..be50a1865d 100644
--- a/src/shared/path-util.c
+++ b/src/shared/path-util.c
@@ -509,7 +509,7 @@ static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id
return safe_atoi(p, mnt_id);
}
-int fd_is_mount_point(int fd) {
+int fd_is_mount_point(int fd, const char *filename, int flags) {
union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT;
int mount_id = -1, mount_id_parent = -1;
bool nosupp = false, check_st_dev = true;
@@ -517,6 +517,7 @@ int fd_is_mount_point(int fd) {
int r;
assert(fd >= 0);
+ assert(filename);
/* First we will try the name_to_handle_at() syscall, which
* tells us the mount id and an opaque file "handle". It is
@@ -541,7 +542,7 @@ int fd_is_mount_point(int fd) {
* subvolumes have different st_dev, even though they aren't
* real mounts of their own. */
- r = name_to_handle_at(fd, "", &h.handle, &mount_id, AT_EMPTY_PATH);
+ r = name_to_handle_at(fd, filename, &h.handle, &mount_id, flags);
if (r < 0) {
if (errno == ENOSYS)
/* This kernel does not support name_to_handle_at()
@@ -558,7 +559,7 @@ int fd_is_mount_point(int fd) {
return -errno;
}
- r = name_to_handle_at(fd, "..", &h_parent.handle, &mount_id_parent, 0);
+ r = name_to_handle_at(fd, "", &h_parent.handle, &mount_id_parent, AT_EMPTY_PATH);
if (r < 0) {
if (errno == EOPNOTSUPP) {
if (nosupp)
@@ -593,13 +594,13 @@ int fd_is_mount_point(int fd) {
return mount_id != mount_id_parent;
fallback_fdinfo:
- r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id);
+ r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
if (r == -EOPNOTSUPP)
goto fallback_fstat;
if (r < 0)
return r;
- r = fd_fdinfo_mnt_id(fd, "..", 0, &mount_id_parent);
+ r = fd_fdinfo_mnt_id(fd, "", AT_EMPTY_PATH, &mount_id_parent);
if (r < 0)
return r;
@@ -615,10 +616,16 @@ fallback_fdinfo:
check_st_dev = false;
fallback_fstat:
- if (fstatat(fd, "", &a, AT_EMPTY_PATH) < 0)
+ /* yay for fstatat() taking a different set of flags than the other
+ * _at() above */
+ if (flags & AT_SYMLINK_FOLLOW)
+ flags &= ~AT_SYMLINK_FOLLOW;
+ else
+ flags |= AT_SYMLINK_NOFOLLOW;
+ if (fstatat(fd, filename, &a, flags) < 0)
return -errno;
- if (fstatat(fd, "..", &b, 0) < 0)
+ if (fstatat(fd, "", &b, AT_EMPTY_PATH) < 0)
return -errno;
/* A directory with same device and inode as its parent? Must
@@ -630,19 +637,26 @@ fallback_fstat:
return check_st_dev && (a.st_dev != b.st_dev);
}
-int path_is_mount_point(const char *t, bool allow_symlink) {
+/* flags can be AT_SYMLINK_FOLLOW or 0 */
+int path_is_mount_point(const char *t, int flags) {
_cleanup_close_ int fd = -1;
+ _cleanup_free_ char *parent = NULL;
+ int r;
assert(t);
if (path_equal(t, "/"))
return 1;
- fd = openat(AT_FDCWD, t, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|(allow_symlink ? 0 : O_PATH));
+ r = path_get_parent(t, &parent);
+ if (r < 0)
+ return r;
+
+ fd = openat(AT_FDCWD, parent, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_PATH);
if (fd < 0)
return -errno;
- return fd_is_mount_point(fd);
+ return fd_is_mount_point(fd, basename(t), flags);
}
int path_is_read_only_fs(const char *path) {
diff --git a/src/shared/path-util.h b/src/shared/path-util.h
index 4f45cfd2b7..1eac89c51b 100644
--- a/src/shared/path-util.h
+++ b/src/shared/path-util.h
@@ -53,8 +53,8 @@ char** path_strv_make_absolute_cwd(char **l);
char** path_strv_resolve(char **l, const char *prefix);
char** path_strv_resolve_uniq(char **l, const char *prefix);
-int fd_is_mount_point(int fd);
-int path_is_mount_point(const char *path, bool allow_symlink);
+int fd_is_mount_point(int fd, const char *filename, int flags);
+int path_is_mount_point(const char *path, int flags);
int path_is_read_only_fs(const char *path);
int path_is_os_tree(const char *path);
diff --git a/src/shared/process-util.c b/src/shared/process-util.c
index 92a69f50e7..cfc876567d 100644
--- a/src/shared/process-util.c
+++ b/src/shared/process-util.c
@@ -28,10 +28,11 @@
#include <signal.h>
#include <ctype.h>
-#include "process-util.h"
#include "fileio.h"
#include "util.h"
#include "log.h"
+#include "signal-util.h"
+#include "process-util.h"
int get_process_state(pid_t pid) {
const char *p;
diff --git a/src/shared/pty.c b/src/shared/pty.c
index 0f80f4863b..119d66e9a2 100644
--- a/src/shared/pty.c
+++ b/src/shared/pty.c
@@ -57,9 +57,10 @@
#include "barrier.h"
#include "macro.h"
-#include "pty.h"
#include "ring.h"
#include "util.h"
+#include "signal-util.h"
+#include "pty.h"
#define PTY_BUFSIZE 4096
diff --git a/src/shared/random-util.c b/src/shared/random-util.c
index 88f5182508..b230044f50 100644
--- a/src/shared/random-util.c
+++ b/src/shared/random-util.c
@@ -23,7 +23,9 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
+#ifdef HAVE_SYS_AUXV_H
#include <sys/auxv.h>
+#endif
#include <linux/random.h>
#include "random-util.h"
diff --git a/src/shared/rm-rf.c b/src/shared/rm-rf.c
index a89e8afc2a..bafd483be2 100644
--- a/src/shared/rm-rf.c
+++ b/src/shared/rm-rf.c
@@ -103,7 +103,7 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
}
/* Stop at mount points */
- r = fd_is_mount_point(subdir_fd);
+ r = fd_is_mount_point(fd, de->d_name, 0);
if (r < 0) {
if (ret == 0 && r != -ENOENT)
ret = r;
diff --git a/src/shared/signal-util.c b/src/shared/signal-util.c
new file mode 100644
index 0000000000..9a2973b6fd
--- /dev/null
+++ b/src/shared/signal-util.c
@@ -0,0 +1,228 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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 "signal-util.h"
+
+int reset_all_signal_handlers(void) {
+ int sig, r = 0;
+
+ for (sig = 1; sig < _NSIG; sig++) {
+ static const struct sigaction sa = {
+ .sa_handler = SIG_DFL,
+ .sa_flags = SA_RESTART,
+ };
+
+ /* These two cannot be caught... */
+ if (sig == SIGKILL || sig == SIGSTOP)
+ continue;
+
+ /* On Linux the first two RT signals are reserved by
+ * glibc, and sigaction() will return EINVAL for them. */
+ if ((sigaction(sig, &sa, NULL) < 0))
+ if (errno != EINVAL && r == 0)
+ r = -errno;
+ }
+
+ return r;
+}
+
+int reset_signal_mask(void) {
+ sigset_t ss;
+
+ if (sigemptyset(&ss) < 0)
+ return -errno;
+
+ if (sigprocmask(SIG_SETMASK, &ss, NULL) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int sigaction_many(const struct sigaction *sa, ...) {
+ va_list ap;
+ int r = 0, sig;
+
+ va_start(ap, sa);
+ while ((sig = va_arg(ap, int)) > 0)
+ if (sigaction(sig, sa, NULL) < 0)
+ r = -errno;
+ va_end(ap);
+
+ return r;
+}
+
+int ignore_signals(int sig, ...) {
+ static const struct sigaction sa = {
+ .sa_handler = SIG_IGN,
+ .sa_flags = SA_RESTART,
+ };
+ va_list ap;
+ int r = 0;
+
+ if (sigaction(sig, &sa, NULL) < 0)
+ r = -errno;
+
+ va_start(ap, sig);
+ while ((sig = va_arg(ap, int)) > 0)
+ if (sigaction(sig, &sa, NULL) < 0)
+ r = -errno;
+ va_end(ap);
+
+ return r;
+}
+
+int default_signals(int sig, ...) {
+ static const struct sigaction sa = {
+ .sa_handler = SIG_DFL,
+ .sa_flags = SA_RESTART,
+ };
+ va_list ap;
+ int r = 0;
+
+ if (sigaction(sig, &sa, NULL) < 0)
+ r = -errno;
+
+ va_start(ap, sig);
+ while ((sig = va_arg(ap, int)) > 0)
+ if (sigaction(sig, &sa, NULL) < 0)
+ r = -errno;
+ va_end(ap);
+
+ return r;
+}
+
+void sigset_add_many(sigset_t *ss, ...) {
+ va_list ap;
+ int sig;
+
+ assert(ss);
+
+ va_start(ap, ss);
+ while ((sig = va_arg(ap, int)) > 0)
+ assert_se(sigaddset(ss, sig) == 0);
+ va_end(ap);
+}
+
+int sigprocmask_many(int how, ...) {
+ va_list ap;
+ sigset_t ss;
+ int sig;
+
+ assert_se(sigemptyset(&ss) == 0);
+
+ va_start(ap, how);
+ while ((sig = va_arg(ap, int)) > 0)
+ assert_se(sigaddset(&ss, sig) == 0);
+ va_end(ap);
+
+ if (sigprocmask(how, &ss, NULL) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static const char *const __signal_table[] = {
+ [SIGHUP] = "HUP",
+ [SIGINT] = "INT",
+ [SIGQUIT] = "QUIT",
+ [SIGILL] = "ILL",
+ [SIGTRAP] = "TRAP",
+ [SIGABRT] = "ABRT",
+ [SIGBUS] = "BUS",
+ [SIGFPE] = "FPE",
+ [SIGKILL] = "KILL",
+ [SIGUSR1] = "USR1",
+ [SIGSEGV] = "SEGV",
+ [SIGUSR2] = "USR2",
+ [SIGPIPE] = "PIPE",
+ [SIGALRM] = "ALRM",
+ [SIGTERM] = "TERM",
+#ifdef SIGSTKFLT
+ [SIGSTKFLT] = "STKFLT", /* Linux on SPARC doesn't know SIGSTKFLT */
+#endif
+ [SIGCHLD] = "CHLD",
+ [SIGCONT] = "CONT",
+ [SIGSTOP] = "STOP",
+ [SIGTSTP] = "TSTP",
+ [SIGTTIN] = "TTIN",
+ [SIGTTOU] = "TTOU",
+ [SIGURG] = "URG",
+ [SIGXCPU] = "XCPU",
+ [SIGXFSZ] = "XFSZ",
+ [SIGVTALRM] = "VTALRM",
+ [SIGPROF] = "PROF",
+ [SIGWINCH] = "WINCH",
+ [SIGIO] = "IO",
+ [SIGPWR] = "PWR",
+ [SIGSYS] = "SYS"
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int);
+
+const char *signal_to_string(int signo) {
+ static thread_local char buf[sizeof("RTMIN+")-1 + DECIMAL_STR_MAX(int) + 1];
+ const char *name;
+
+ name = __signal_to_string(signo);
+ if (name)
+ return name;
+
+ if (signo >= SIGRTMIN && signo <= SIGRTMAX)
+ snprintf(buf, sizeof(buf), "RTMIN+%d", signo - SIGRTMIN);
+ else
+ snprintf(buf, sizeof(buf), "%d", signo);
+
+ return buf;
+}
+
+int signal_from_string(const char *s) {
+ int signo;
+ int offset = 0;
+ unsigned u;
+
+ signo = __signal_from_string(s);
+ if (signo > 0)
+ return signo;
+
+ if (startswith(s, "RTMIN+")) {
+ s += 6;
+ offset = SIGRTMIN;
+ }
+ if (safe_atou(s, &u) >= 0) {
+ signo = (int) u + offset;
+ if (signo > 0 && signo < _NSIG)
+ return signo;
+ }
+ return -EINVAL;
+}
+
+int signal_from_string_try_harder(const char *s) {
+ int signo;
+ assert(s);
+
+ signo = signal_from_string(s);
+ if (signo <= 0)
+ if (startswith(s, "SIG"))
+ return signal_from_string(s+3);
+
+ return signo;
+}
diff --git a/src/shared/signal-util.h b/src/shared/signal-util.h
new file mode 100644
index 0000000000..ddf64cda76
--- /dev/null
+++ b/src/shared/signal-util.h
@@ -0,0 +1,41 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010-2015 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 <signal.h>
+
+#include "macro.h"
+
+int reset_all_signal_handlers(void);
+int reset_signal_mask(void);
+
+int ignore_signals(int sig, ...);
+int default_signals(int sig, ...);
+int sigaction_many(const struct sigaction *sa, ...);
+
+void sigset_add_many(sigset_t *ss, ...);
+int sigprocmask_many(int how, ...);
+
+const char *signal_to_string(int i) _const_;
+int signal_from_string(const char *s) _pure_;
+
+int signal_from_string_try_harder(const char *s);
diff --git a/src/shared/switch-root.c b/src/shared/switch-root.c
index ae3839de16..b12189cd10 100644
--- a/src/shared/switch-root.c
+++ b/src/shared/switch-root.c
@@ -105,7 +105,7 @@ int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot,
* 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);
+ (void) base_filesystem_create(new_root, UID_INVALID, GID_INVALID);
if (chdir(new_root) < 0)
return log_error_errno(errno, "Failed to change directory to %s: %m", new_root);
diff --git a/src/shared/unit-name.c b/src/shared/unit-name.c
index b23e47a2e6..bf52463d81 100644
--- a/src/shared/unit-name.c
+++ b/src/shared/unit-name.c
@@ -812,6 +812,8 @@ static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
[UNIT_PART_OF] = "PartOf",
[UNIT_REQUIRED_BY] = "RequiredBy",
[UNIT_REQUIRED_BY_OVERRIDABLE] = "RequiredByOverridable",
+ [UNIT_REQUISITE_OF] = "RequisiteOf",
+ [UNIT_REQUISITE_OF_OVERRIDABLE] = "RequisiteOfOverridable",
[UNIT_WANTED_BY] = "WantedBy",
[UNIT_BOUND_BY] = "BoundBy",
[UNIT_CONSISTS_OF] = "ConsistsOf",
diff --git a/src/shared/unit-name.h b/src/shared/unit-name.h
index c2f31e3f90..b2043d0870 100644
--- a/src/shared/unit-name.h
+++ b/src/shared/unit-name.h
@@ -71,8 +71,10 @@ enum UnitDependency {
UNIT_PART_OF,
/* Inverse of the above */
- UNIT_REQUIRED_BY, /* inverse of 'requires' and 'requisite' is 'required_by' */
- UNIT_REQUIRED_BY_OVERRIDABLE, /* inverse of 'requires_overridable' and 'requisite_overridable' is 'soft_required_by' */
+ UNIT_REQUIRED_BY, /* inverse of 'requires' is 'required_by' */
+ UNIT_REQUIRED_BY_OVERRIDABLE, /* inverse of 'requires_overridable' is 'required_by_overridable' */
+ UNIT_REQUISITE_OF, /* inverse of 'requisite' is 'requisite_of' */
+ UNIT_REQUISITE_OF_OVERRIDABLE,/* inverse of 'requisite_overridable' is 'requisite_of_overridable' */
UNIT_WANTED_BY, /* inverse of 'wants' */
UNIT_BOUND_BY, /* inverse of 'binds_to' */
UNIT_CONSISTS_OF, /* inverse of 'part_of' */
diff --git a/src/shared/util.c b/src/shared/util.c
index da6343f4c4..8a6107969a 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -93,6 +93,7 @@
#include "random-util.h"
#include "terminal-util.h"
#include "hostname-util.h"
+#include "signal-util.h"
/* Put this test here for a lack of better place */
assert_cc(EAGAIN == EWOULDBLOCK);
@@ -148,6 +149,27 @@ char* endswith(const char *s, const char *postfix) {
return (char*) s + sl - pl;
}
+char* endswith_no_case(const char *s, const char *postfix) {
+ size_t sl, pl;
+
+ assert(s);
+ assert(postfix);
+
+ sl = strlen(s);
+ pl = strlen(postfix);
+
+ if (pl == 0)
+ return (char*) s + sl;
+
+ if (sl < pl)
+ return NULL;
+
+ if (strcasecmp(s + sl - pl, postfix) != 0)
+ return NULL;
+
+ return (char*) s + sl - pl;
+}
+
char* first_word(const char *s, const char *word) {
size_t sl, wl;
const char *p;
@@ -750,41 +772,6 @@ int readlink_and_canonicalize(const char *p, char **r) {
return 0;
}
-int reset_all_signal_handlers(void) {
- int sig, r = 0;
-
- for (sig = 1; sig < _NSIG; sig++) {
- struct sigaction sa = {
- .sa_handler = SIG_DFL,
- .sa_flags = SA_RESTART,
- };
-
- /* These two cannot be caught... */
- if (sig == SIGKILL || sig == SIGSTOP)
- continue;
-
- /* On Linux the first two RT signals are reserved by
- * glibc, and sigaction() will return EINVAL for them. */
- if ((sigaction(sig, &sa, NULL) < 0))
- if (errno != EINVAL && r == 0)
- r = -errno;
- }
-
- return r;
-}
-
-int reset_signal_mask(void) {
- sigset_t ss;
-
- if (sigemptyset(&ss) < 0)
- return -errno;
-
- if (sigprocmask(SIG_SETMASK, &ss, NULL) < 0)
- return -errno;
-
- return 0;
-}
-
char *strstrip(char *s) {
char *e;
@@ -1540,59 +1527,6 @@ int flush_fd(int fd) {
}
}
-int sigaction_many(const struct sigaction *sa, ...) {
- va_list ap;
- int r = 0, sig;
-
- va_start(ap, sa);
- while ((sig = va_arg(ap, int)) > 0)
- if (sigaction(sig, sa, NULL) < 0)
- r = -errno;
- va_end(ap);
-
- return r;
-}
-
-int ignore_signals(int sig, ...) {
- struct sigaction sa = {
- .sa_handler = SIG_IGN,
- .sa_flags = SA_RESTART,
- };
- va_list ap;
- int r = 0;
-
- if (sigaction(sig, &sa, NULL) < 0)
- r = -errno;
-
- va_start(ap, sig);
- while ((sig = va_arg(ap, int)) > 0)
- if (sigaction(sig, &sa, NULL) < 0)
- r = -errno;
- va_end(ap);
-
- return r;
-}
-
-int default_signals(int sig, ...) {
- struct sigaction sa = {
- .sa_handler = SIG_DFL,
- .sa_flags = SA_RESTART,
- };
- va_list ap;
- int r = 0;
-
- if (sigaction(sig, &sa, NULL) < 0)
- r = -errno;
-
- va_start(ap, sig);
- while ((sig = va_arg(ap, int)) > 0)
- if (sigaction(sig, &sa, NULL) < 0)
- r = -errno;
- va_end(ap);
-
- return r;
-}
-
void safe_close_pair(int p[]) {
assert(p);
@@ -1697,7 +1631,7 @@ int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
int parse_size(const char *t, off_t base, off_t *size) {
- /* Soo, sometimes we want to parse IEC binary suffxies, and
+ /* Soo, sometimes we want to parse IEC binary suffixes, and
* sometimes SI decimal suffixes. This function can parse
* both. Which one is the right way depends on the
* context. Wikipedia suggests that SI is customary for
@@ -1906,35 +1840,6 @@ void rename_process(const char name[8]) {
}
}
-void sigset_add_many(sigset_t *ss, ...) {
- va_list ap;
- int sig;
-
- assert(ss);
-
- va_start(ap, ss);
- while ((sig = va_arg(ap, int)) > 0)
- assert_se(sigaddset(ss, sig) == 0);
- va_end(ap);
-}
-
-int sigprocmask_many(int how, ...) {
- va_list ap;
- sigset_t ss;
- int sig;
-
- assert_se(sigemptyset(&ss) == 0);
-
- va_start(ap, how);
- while ((sig = va_arg(ap, int)) > 0)
- assert_se(sigaddset(&ss, sig) == 0);
- va_end(ap);
-
- if (sigprocmask(how, &ss, NULL) < 0)
- return -errno;
-
- return 0;
-}
char *lookup_uid(uid_t uid) {
long bufsize;
char *name;
@@ -2323,18 +2228,6 @@ DIR *xopendirat(int fd, const char *name, int flags) {
return d;
}
-int signal_from_string_try_harder(const char *s) {
- int signo;
- assert(s);
-
- signo = signal_from_string(s);
- if (signo <= 0)
- if (startswith(s, "SIG"))
- return signal_from_string(s+3);
-
- return signo;
-}
-
static char *tag_to_udev_node(const char *tagvalue, const char *by) {
_cleanup_free_ char *t = NULL, *u = NULL;
size_t enc_len;
@@ -3291,81 +3184,6 @@ static const char* const ip_tos_table[] = {
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff);
-static const char *const __signal_table[] = {
- [SIGHUP] = "HUP",
- [SIGINT] = "INT",
- [SIGQUIT] = "QUIT",
- [SIGILL] = "ILL",
- [SIGTRAP] = "TRAP",
- [SIGABRT] = "ABRT",
- [SIGBUS] = "BUS",
- [SIGFPE] = "FPE",
- [SIGKILL] = "KILL",
- [SIGUSR1] = "USR1",
- [SIGSEGV] = "SEGV",
- [SIGUSR2] = "USR2",
- [SIGPIPE] = "PIPE",
- [SIGALRM] = "ALRM",
- [SIGTERM] = "TERM",
-#ifdef SIGSTKFLT
- [SIGSTKFLT] = "STKFLT", /* Linux on SPARC doesn't know SIGSTKFLT */
-#endif
- [SIGCHLD] = "CHLD",
- [SIGCONT] = "CONT",
- [SIGSTOP] = "STOP",
- [SIGTSTP] = "TSTP",
- [SIGTTIN] = "TTIN",
- [SIGTTOU] = "TTOU",
- [SIGURG] = "URG",
- [SIGXCPU] = "XCPU",
- [SIGXFSZ] = "XFSZ",
- [SIGVTALRM] = "VTALRM",
- [SIGPROF] = "PROF",
- [SIGWINCH] = "WINCH",
- [SIGIO] = "IO",
- [SIGPWR] = "PWR",
- [SIGSYS] = "SYS"
-};
-
-DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int);
-
-const char *signal_to_string(int signo) {
- static thread_local char buf[sizeof("RTMIN+")-1 + DECIMAL_STR_MAX(int) + 1];
- const char *name;
-
- name = __signal_to_string(signo);
- if (name)
- return name;
-
- if (signo >= SIGRTMIN && signo <= SIGRTMAX)
- snprintf(buf, sizeof(buf), "RTMIN+%d", signo - SIGRTMIN);
- else
- snprintf(buf, sizeof(buf), "%d", signo);
-
- return buf;
-}
-
-int signal_from_string(const char *s) {
- int signo;
- int offset = 0;
- unsigned u;
-
- signo = __signal_from_string(s);
- if (signo > 0)
- return signo;
-
- if (startswith(s, "RTMIN+")) {
- s += 6;
- offset = SIGRTMIN;
- }
- if (safe_atou(s, &u) >= 0) {
- signo = (int) u + offset;
- if (signo > 0 && signo < _NSIG)
- return signo;
- }
- return -EINVAL;
-}
-
bool kexec_loaded(void) {
bool loaded = false;
char *s;
@@ -4665,16 +4483,7 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int root_fd) {
return -errno;
}
- if (setresgid(0, 0, 0) < 0)
- return -errno;
-
- if (setgroups(0, NULL) < 0)
- return -errno;
-
- if (setresuid(0, 0, 0) < 0)
- return -errno;
-
- return 0;
+ return reset_uid_gid();
}
int getpeercred(int fd, struct ucred *ucred) {
@@ -4825,10 +4634,7 @@ unsigned long personality_from_string(const char *p) {
return PER_LINUX;
#endif
- /* personality(7) documents that 0xffffffffUL is used for
- * querying the current personality, hence let's use that here
- * as error indicator. */
- return 0xffffffffUL;
+ return PERSONALITY_INVALID;
}
const char* personality_to_string(unsigned long p) {
@@ -5878,7 +5684,7 @@ int same_fd(int a, int b) {
/* The fds refer to the same inode on disk, let's also check
* if they have the same fd flags. This is useful to
- * distuingish the read and write side of a pipe created with
+ * distinguish the read and write side of a pipe created with
* pipe(). */
fa = fcntl(a, F_GETFL);
if (fa < 0)
@@ -6208,3 +6014,35 @@ int parse_mode(const char *s, mode_t *ret) {
*ret = (mode_t) l;
return 0;
}
+
+int mount_move_root(const char *path) {
+ assert(path);
+
+ if (chdir(path) < 0)
+ return -errno;
+
+ if (mount(path, "/", NULL, MS_MOVE, NULL) < 0)
+ return -errno;
+
+ if (chroot(".") < 0)
+ return -errno;
+
+ if (chdir("/") < 0)
+ return -errno;
+
+ return 0;
+}
+
+int reset_uid_gid(void) {
+
+ if (setgroups(0, NULL) < 0)
+ return -errno;
+
+ if (setresgid(0, 0, 0) < 0)
+ return -errno;
+
+ if (setresuid(0, 0, 0) < 0)
+ return -errno;
+
+ return 0;
+}
diff --git a/src/shared/util.h b/src/shared/util.h
index a2b1ec5030..467ae234a0 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -29,7 +29,6 @@
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
-#include <signal.h>
#include <sched.h>
#include <limits.h>
#include <sys/types.h>
@@ -134,6 +133,7 @@ static inline char *startswith_no_case(const char *s, const char *prefix) {
}
char *endswith(const char *s, const char *postfix) _pure_;
+char *endswith_no_case(const char *s, const char *postfix) _pure_;
char *first_word(const char *s, const char *word) _pure_;
@@ -226,9 +226,6 @@ 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);
-int reset_all_signal_handlers(void);
-int reset_signal_mask(void);
-
char *strstrip(char *s);
char *delete_chars(char *s, const char *bad);
char *truncate_nl(char *s);
@@ -333,10 +330,6 @@ bool fstype_is_network(const char *fstype);
int flush_fd(int fd);
-int ignore_signals(int sig, ...);
-int default_signals(int sig, ...);
-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);
@@ -348,9 +341,6 @@ bool is_device_path(const char *path);
int dir_is_empty(const char *path);
char* dirname_malloc(const char *path);
-void sigset_add_many(sigset_t *ss, ...);
-int sigprocmask_many(int how, ...);
-
char* lookup_uid(uid_t uid);
char* getlogname_malloc(void);
char* getusername_malloc(void);
@@ -464,11 +454,6 @@ int rlimit_from_string(const char *s) _pure_;
int ip_tos_to_string_alloc(int i, char **s);
int ip_tos_from_string(const char *s);
-const char *signal_to_string(int i) _const_;
-int signal_from_string(const char *s) _pure_;
-
-int signal_from_string_try_harder(const char *s);
-
extern int saved_argc;
extern char **saved_argv;
@@ -774,7 +759,7 @@ int shall_restore_state(void);
* that only if nmemb > 0.
*/
static inline void qsort_safe(void *base, size_t nmemb, size_t size, comparison_fn_t compar) {
- if (nmemb <= 0)
+ if (nmemb <= 1)
return;
assert(base);
@@ -815,6 +800,13 @@ int open_tmpfile(const char *path, int flags);
int fd_warn_permissions(const char *path, int fd);
+#ifndef PERSONALITY_INVALID
+/* personality(7) documents that 0xffffffffUL is used for querying the
+ * current personality, hence let's use that here as error
+ * indicator. */
+#define PERSONALITY_INVALID 0xffffffffLU
+#endif
+
unsigned long personality_from_string(const char *p);
const char *personality_to_string(unsigned long);
@@ -905,3 +897,7 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char
char *shell_maybe_quote(const char *s);
int parse_mode(const char *s, mode_t *ret);
+
+int mount_move_root(const char *path);
+
+int reset_uid_gid(void);
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index f8e10a4710..a7b8e54a9c 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -72,6 +72,7 @@
#include "process-util.h"
#include "terminal-util.h"
#include "hostname-util.h"
+#include "signal-util.h"
static char **arg_types = NULL;
static char **arg_states = NULL;
@@ -5098,7 +5099,7 @@ static int import_environment(sd_bus *bus, char **args) {
static int enable_sysv_units(const char *verb, char **args) {
int r = 0;
-#if defined(HAVE_SYSV_COMPAT) && defined(HAVE_CHKCONFIG)
+#if defined(HAVE_SYSV_COMPAT)
unsigned f = 0;
_cleanup_lookup_paths_free_ LookupPaths paths = {};
@@ -5123,7 +5124,7 @@ static int enable_sysv_units(const char *verb, char **args) {
_cleanup_free_ char *p = NULL, *q = NULL, *l = NULL;
bool found_native = false, found_sysv;
unsigned c = 1;
- const char *argv[6] = { "/sbin/chkconfig", NULL, NULL, NULL, NULL };
+ const char *argv[6] = { ROOTLIBEXECDIR "/systemd-sysv-install", NULL, NULL, NULL, NULL };
char **k;
int j;
pid_t pid;
@@ -5149,7 +5150,10 @@ static int enable_sysv_units(const char *verb, char **args) {
break;
}
- if (found_native)
+ /* If we have both a native unit and a SysV script,
+ * enable/disable them both (below); for is-enabled, prefer the
+ * native unit */
+ if (found_native && streq(verb, "is-enabled"))
continue;
p = path_join(arg_root, SYSTEM_SYSVINIT_PATH, name);
@@ -5161,15 +5165,16 @@ static int enable_sysv_units(const char *verb, char **args) {
if (!found_sysv)
continue;
- log_info("%s is not a native service, redirecting to /sbin/chkconfig.", name);
+ if (found_native)
+ log_info("Synchronizing state of %s with SysV init with %s...", name, argv[0]);
+ else
+ log_info("%s is not a native service, redirecting to systemd-sysv-install", name);
if (!isempty(arg_root))
argv[c++] = q = strappend("--root=", arg_root);
+ argv[c++] = verb;
argv[c++] = basename(p);
- argv[c++] =
- streq(verb, "enable") ? "on" :
- streq(verb, "disable") ? "off" : "--level=5";
argv[c] = NULL;
l = strv_join((char**)argv, " ");
@@ -5185,6 +5190,7 @@ static int enable_sysv_units(const char *verb, char **args) {
/* Child */
execv(argv[0], (char**) argv);
+ log_error("Failed to execute %s: %m", argv[0]);
_exit(EXIT_FAILURE);
}
@@ -5210,6 +5216,9 @@ static int enable_sysv_units(const char *verb, char **args) {
} else
return -EPROTO;
+ if (found_native)
+ continue;
+
/* Remove this entry, so that we don't try enabling it as native unit */
assert(f > 0);
f--;
diff --git a/src/systemctl/systemd-sysv-install.SKELETON b/src/systemctl/systemd-sysv-install.SKELETON
new file mode 100755
index 0000000000..a53a3e6221
--- /dev/null
+++ b/src/systemctl/systemd-sysv-install.SKELETON
@@ -0,0 +1,47 @@
+#!/bin/sh
+# This script is called by "systemctl enable/disable" when the given unit is a
+# SysV init.d script. It needs to call the distribution's mechanism for
+# enabling/disabling those, such as chkconfig, update-rc.d, or similar. This
+# can optionally take a --root argument for enabling a SysV init script
+# in a chroot or similar.
+set -e
+
+usage() {
+ echo "Usage: $0 [--root=path] enable|disable|is-enabled <sysv script name>" >&2
+ exit 1
+}
+
+# parse options
+eval set -- "$(getopt -o r: --long root: -- "$@")"
+while true; do
+ case "$1" in
+ -r|--root)
+ ROOT="$2"
+ shift 2 ;;
+ --) shift ; break ;;
+ *) usage ;;
+ esac
+done
+
+NAME="$2"
+[ -n "$NAME" ] || usage
+
+case "$1" in
+ enable)
+ # call the command to enable SysV init script $NAME here
+ # (consider optional $ROOT)
+ echo "IMPLEMENT ME: enabling SysV init.d script $NAME"
+ ;;
+ disable)
+ # call the command to disable SysV init script $NAME here
+ # (consider optional $ROOT)
+ echo "IMPLEMENT ME: disabling SysV init.d script $NAME"
+ ;;
+ is-enabled)
+ # exit with 0 if $NAME is enabled, non-zero if it is disabled
+ # (consider optional $ROOT)
+ echo "IMPLEMENT ME: checking SysV init.d script $NAME"
+ ;;
+ *)
+ usage ;;
+esac
diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h
index 9dadae9e59..e6e2ecd0b7 100644
--- a/src/systemd/sd-bus.h
+++ b/src/systemd/sd-bus.h
@@ -141,10 +141,10 @@ 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_creds(sd_bus *bus, int b, uint64_t creds_mask);
+int sd_bus_negotiate_timestamp(sd_bus *bus, int b);
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, int b, uint64_t creds_mask);
int sd_bus_get_creds_mask(sd_bus *bus, uint64_t *creds_mask);
int sd_bus_set_allow_interactive_authorization(sd_bus *bus, int b);
int sd_bus_get_allow_interactive_authorization(sd_bus *bus);
@@ -303,7 +303,7 @@ int sd_bus_get_property(sd_bus *bus, const char *destination, const char *path,
int sd_bus_get_property_trivial(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, char type, void *ret_ptr);
int sd_bus_get_property_string(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, char **ret); /* free the result! */
int sd_bus_get_property_strv(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, char ***ret); /* free the result! */
-int sd_bus_set_property(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, const char *ret_type, ...);
+int sd_bus_set_property(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *ret_error, const char *type, ...);
int sd_bus_reply_method_return(sd_bus_message *call, const char *types, ...);
int sd_bus_reply_method_error(sd_bus_message *call, const sd_bus_error *e);
diff --git a/src/test/test-conf-parser.c b/src/test/test-conf-parser.c
new file mode 100644
index 0000000000..463906d304
--- /dev/null
+++ b/src/test/test-conf-parser.c
@@ -0,0 +1,234 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2015 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 "conf-parser.h"
+#include "macro.h"
+#include "util.h"
+#include "strv.h"
+#include "log.h"
+
+static void test_config_parse_path_one(const char *rvalue, const char *expected) {
+ char *path = NULL;
+
+ assert_se(config_parse_path("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &path, NULL) >= 0);
+ assert_se(streq_ptr(expected, path));
+
+ free(path);
+}
+
+static void test_config_parse_log_level_one(const char *rvalue, int expected) {
+ int log_level = 0;
+
+ assert_se(config_parse_log_level("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &log_level, NULL) >= 0);
+ assert_se(expected == log_level);
+}
+
+static void test_config_parse_log_facility_one(const char *rvalue, int expected) {
+ int log_facility = 0;
+
+ assert_se(config_parse_log_facility("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &log_facility, NULL) >= 0);
+ assert_se(expected == log_facility);
+}
+
+static void test_config_parse_iec_size_one(const char *rvalue, size_t expected) {
+ size_t iec_size = 0;
+
+ assert_se(config_parse_iec_size("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &iec_size, NULL) >= 0);
+ assert_se(expected == iec_size);
+}
+
+static void test_config_parse_si_size_one(const char *rvalue, size_t expected) {
+ size_t si_size = 0;
+
+ assert_se(config_parse_si_size("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &si_size, NULL) >= 0);
+ assert_se(expected == si_size);
+}
+
+static void test_config_parse_int_one(const char *rvalue, int expected) {
+ int v = -1;
+
+ assert_se(config_parse_int("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &v, NULL) >= 0);
+ assert_se(expected == v);
+}
+
+static void test_config_parse_unsigned_one(const char *rvalue, unsigned expected) {
+ unsigned v = 0;
+
+ assert_se(config_parse_unsigned("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &v, NULL) >= 0);
+ assert_se(expected == v);
+}
+
+static void test_config_parse_strv_one(const char *rvalue, char **expected) {
+ char **strv = 0;
+
+ assert_se(config_parse_strv("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &strv, NULL) >= 0);
+ assert_se(strv_equal(expected, strv));
+
+ strv_free(strv);
+}
+
+static void test_config_parse_mode_one(const char *rvalue, mode_t expected) {
+ mode_t v = 0;
+
+ assert_se(config_parse_mode("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &v, NULL) >= 0);
+ assert_se(expected == v);
+}
+
+static void test_config_parse_sec_one(const char *rvalue, usec_t expected) {
+ usec_t v = 0;
+
+ assert_se(config_parse_sec("unit", "filename", 1, "section", 1, "lvalue", 0, rvalue, &v, NULL) >= 0);
+ assert_se(expected == v);
+}
+
+static void test_config_parse_nsec_one(const char *rvalue, nsec_t expected) {
+ nsec_t v = 0;
+
+ assert_se(config_parse_nsec("unit", "filename", 1, "nsection", 1, "lvalue", 0, rvalue, &v, NULL) >= 0);
+ assert_se(expected == v);
+}
+
+static void test_config_parse_path(void) {
+ test_config_parse_path_one("/path", "/path");
+ test_config_parse_path_one("/path//////////", "/path");
+ test_config_parse_path_one("///path/foo///bar////bar//", "/path/foo/bar/bar");
+
+ test_config_parse_path_one("not_absolute/path", NULL);
+}
+
+static void test_config_parse_log_level(void) {
+ test_config_parse_log_level_one("debug", LOG_DEBUG);
+ test_config_parse_log_level_one("info", LOG_INFO);
+
+ test_config_parse_log_level_one("garbage", 0);
+}
+
+static void test_config_parse_log_facility(void) {
+ test_config_parse_log_facility_one("mail", LOG_MAIL);
+ test_config_parse_log_facility_one("user", LOG_USER);
+
+ test_config_parse_log_facility_one("garbage", 0);
+}
+
+static void test_config_parse_iec_size(void) {
+ test_config_parse_iec_size_one("1024", 1024);
+ test_config_parse_iec_size_one("2K", 2048);
+ test_config_parse_iec_size_one("10M", 10 * 1024 * 1024);
+ test_config_parse_iec_size_one("1G", 1 * 1024 * 1024 * 1024);
+ test_config_parse_iec_size_one("0G", 0);
+ test_config_parse_iec_size_one("0", 0);
+
+ test_config_parse_iec_size_one("-982", 0);
+ test_config_parse_iec_size_one("49874444198739873000000G", 0);
+ test_config_parse_iec_size_one("garbage", 0);
+}
+
+static void test_config_parse_si_size(void) {
+ test_config_parse_si_size_one("1024", 1024);
+ test_config_parse_si_size_one("2K", 2000);
+ test_config_parse_si_size_one("10M", 10 * 1000 * 1000);
+ test_config_parse_si_size_one("1G", 1 * 1000 * 1000 * 1000);
+ test_config_parse_si_size_one("0G", 0);
+ test_config_parse_si_size_one("0", 0);
+
+ test_config_parse_si_size_one("-982", 0);
+ test_config_parse_si_size_one("49874444198739873000000G", 0);
+ test_config_parse_si_size_one("garbage", 0);
+}
+
+static void test_config_parse_int(void) {
+ test_config_parse_int_one("1024", 1024);
+ test_config_parse_int_one("-1024", -1024);
+ test_config_parse_int_one("0", 0);
+
+ test_config_parse_int_one("99999999999999999999999999999999999999999999999999999999", -1);
+ test_config_parse_int_one("-99999999999999999999999999999999999999999999999999999999", -1);
+ test_config_parse_int_one("1G", -1);
+ test_config_parse_int_one("garbage", -1);
+}
+
+static void test_config_parse_unsigned(void) {
+ test_config_parse_unsigned_one("10241024", 10241024);
+ test_config_parse_unsigned_one("1024", 1024);
+ test_config_parse_unsigned_one("0", 0);
+
+ test_config_parse_unsigned_one("99999999999999999999999999999999999999999999999999999999", 0);
+ test_config_parse_unsigned_one("1G", 0);
+ test_config_parse_unsigned_one("garbage", 0);
+ test_config_parse_unsigned_one("1000garbage", 0);
+}
+
+static void test_config_parse_strv(void) {
+ test_config_parse_strv_one("", STRV_MAKE_EMPTY);
+ test_config_parse_strv_one("foo", STRV_MAKE("foo"));
+ test_config_parse_strv_one("foo bar foo", STRV_MAKE("foo", "bar", "foo"));
+ test_config_parse_strv_one("\"foo bar\" foo", STRV_MAKE("foo bar", "foo"));
+}
+
+static void test_config_parse_mode(void) {
+ test_config_parse_mode_one("777", 0777);
+ test_config_parse_mode_one("644", 0644);
+
+ test_config_parse_mode_one("-777", 0);
+ test_config_parse_mode_one("999", 0);
+ test_config_parse_mode_one("garbage", 0);
+ test_config_parse_mode_one("777garbage", 0);
+ test_config_parse_mode_one("777 garbage", 0);
+}
+
+static void test_config_parse_sec(void) {
+ test_config_parse_sec_one("1", 1 * USEC_PER_SEC);
+ test_config_parse_sec_one("1s", 1 * USEC_PER_SEC);
+ test_config_parse_sec_one("100ms", 100 * USEC_PER_MSEC);
+ test_config_parse_sec_one("5min 20s", 5 * 60 * USEC_PER_SEC + 20 * USEC_PER_SEC);
+
+ test_config_parse_sec_one("-1", 0);
+ test_config_parse_sec_one("10foo", 0);
+ test_config_parse_sec_one("garbage", 0);
+}
+
+static void test_config_parse_nsec(void) {
+ test_config_parse_nsec_one("1", 1);
+ test_config_parse_nsec_one("1s", 1 * NSEC_PER_SEC);
+ test_config_parse_nsec_one("100ms", 100 * NSEC_PER_MSEC);
+ test_config_parse_nsec_one("5min 20s", 5 * 60 * NSEC_PER_SEC + 20 * NSEC_PER_SEC);
+
+ test_config_parse_nsec_one("-1", 0);
+ test_config_parse_nsec_one("10foo", 0);
+ test_config_parse_nsec_one("garbage", 0);
+}
+
+int main(int argc, char **argv) {
+ log_parse_environment();
+ log_open();
+
+ test_config_parse_path();
+ test_config_parse_log_level();
+ test_config_parse_log_facility();
+ test_config_parse_iec_size();
+ test_config_parse_si_size();
+ test_config_parse_int();
+ test_config_parse_unsigned();
+ test_config_parse_strv();
+ test_config_parse_mode();
+ test_config_parse_sec();
+ test_config_parse_nsec();
+
+ return 0;
+}
diff --git a/src/test/test-fdset.c b/src/test/test-fdset.c
index 91df7eb663..242c5d9dc2 100644
--- a/src/test/test-fdset.c
+++ b/src/test/test-fdset.c
@@ -154,6 +154,56 @@ static void test_fdset_iterate(void) {
unlink(name);
}
+static void test_fdset_isempty(void) {
+ int fd;
+ _cleanup_fdset_free_ FDSet *fdset = NULL;
+ char name[] = "/tmp/test-fdset_isempty.XXXXXX";
+
+ fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ assert_se(fd >= 0);
+
+ fdset = fdset_new();
+ assert_se(fdset);
+
+ assert_se(fdset_isempty(fdset));
+ assert_se(fdset_put(fdset, fd) >= 0);
+ assert_se(!fdset_isempty(fdset));
+
+ unlink(name);
+}
+
+static void test_fdset_steal_first(void) {
+ int fd;
+ _cleanup_fdset_free_ FDSet *fdset = NULL;
+ char name[] = "/tmp/test-fdset_steal_first.XXXXXX";
+
+ fd = mkostemp_safe(name, O_RDWR|O_CLOEXEC);
+ assert_se(fd >= 0);
+
+ fdset = fdset_new();
+ assert_se(fdset);
+
+ assert_se(fdset_steal_first(fdset) < 0);
+ assert_se(fdset_put(fdset, fd) >= 0);
+ assert_se(fdset_steal_first(fdset) == fd);
+ assert_se(fdset_steal_first(fdset) < 0);
+ assert_se(fdset_put(fdset, fd) >= 0);
+
+ unlink(name);
+}
+
+static void test_fdset_new_array(void) {
+ int fds[] = {10, 11, 12, 13};
+ _cleanup_fdset_free_ FDSet *fdset = NULL;
+
+ assert_se(fdset_new_array(&fdset, fds, 4) >= 0);
+ assert_se(fdset_size(fdset) == 4);
+ assert_se(fdset_contains(fdset, 10));
+ assert_se(fdset_contains(fdset, 11));
+ assert_se(fdset_contains(fdset, 12));
+ assert_se(fdset_contains(fdset, 13));
+}
+
int main(int argc, char *argv[]) {
test_fdset_new_fill();
test_fdset_put_dup();
@@ -161,6 +211,9 @@ int main(int argc, char *argv[]) {
test_fdset_close_others();
test_fdset_remove();
test_fdset_iterate();
+ test_fdset_isempty();
+ test_fdset_steal_first();
+ test_fdset_new_array();
return 0;
}
diff --git a/src/test/test-hashmap-plain.c b/src/test/test-hashmap-plain.c
index 84b508f874..c1a5ccf1f5 100644
--- a/src/test/test-hashmap-plain.c
+++ b/src/test/test-hashmap-plain.c
@@ -682,7 +682,7 @@ static void test_hashmap_get2(void) {
r = hashmap_get2(m, key_orig, &key_copy);
assert_se(streq(r, val));
assert_se(key_orig != key_copy);
- assert_se(streq(key_orig, key_orig));
+ assert_se(streq(key_orig, key_copy));
r = hashmap_get2(m, "no such key", NULL);
assert_se(r == NULL);
diff --git a/src/test/test-json.c b/src/test/test-json.c
index 24dc7003bc..1058c583c3 100644
--- a/src/test/test-json.c
+++ b/src/test/test-json.c
@@ -72,6 +72,97 @@ static void test_one(const char *data, ...) {
va_end(ap);
}
+typedef void (*Test)(JsonVariant *);
+
+static void test_file(const char *data, Test test) {
+ _cleanup_json_variant_unref_ JsonVariant *v = NULL;
+ int r;
+
+ r = json_parse(data, &v);
+ assert_se(r == 0);
+ assert_se(v != NULL);
+ assert_se(v->type == JSON_VARIANT_OBJECT);
+
+ if (test)
+ test(v);
+}
+
+static void test_1(JsonVariant *v) {
+ JsonVariant *p, *q;
+ unsigned i;
+
+ /* 3 keys + 3 values */
+ assert_se(v->size == 6);
+
+ /* has k */
+ p = json_variant_value(v, "k");
+ assert_se(p && p->type == JSON_VARIANT_STRING);
+
+ /* k equals v */
+ assert_se(streq(json_variant_string(p), "v"));
+
+ /* has foo */
+ p = json_variant_value(v, "foo");
+ assert_se(p && p->type == JSON_VARIANT_ARRAY && p->size == 3);
+
+ /* check foo[0] = 1, foo[1] = 2, foo[2] = 3 */
+ for (i = 0; i < 3; ++i) {
+ q = json_variant_element(p, i);
+ assert_se(q && q->type == JSON_VARIANT_INTEGER && json_variant_integer(q) == (i+1));
+ }
+
+ /* has bar */
+ p = json_variant_value(v, "bar");
+ assert_se(p && p->type == JSON_VARIANT_OBJECT && p->size == 2);
+
+ /* zap is null */
+ q = json_variant_value(p, "zap");
+ assert_se(q && q->type == JSON_VARIANT_NULL);
+}
+
+static void test_2(JsonVariant *v) {
+ JsonVariant *p, *q;
+
+ /* 2 keys + 2 values */
+ assert_se(v->size == 4);
+
+ /* has mutant */
+ p = json_variant_value(v, "mutant");
+ assert_se(p && p->type == JSON_VARIANT_ARRAY && p->size == 4);
+
+ /* mutant[0] == 1 */
+ q = json_variant_element(p, 0);
+ assert_se(q && q->type == JSON_VARIANT_INTEGER && json_variant_integer(q) == 1);
+
+ /* mutant[1] == null */
+ q = json_variant_element(p, 1);
+ assert_se(q && q->type == JSON_VARIANT_NULL);
+
+ /* mutant[2] == "1" */
+ q = json_variant_element(p, 2);
+ assert_se(q && q->type == JSON_VARIANT_STRING && streq(json_variant_string(q), "1"));
+
+ /* mutant[3] == JSON_VARIANT_OBJECT */
+ q = json_variant_element(p, 3);
+ assert_se(q && q->type == JSON_VARIANT_OBJECT && q->size == 2);
+
+ /* has 1 */
+ p = json_variant_value(q, "1");
+ assert_se(p && p->type == JSON_VARIANT_ARRAY && p->size == 2);
+
+ /* "1"[0] == 1 */
+ q = json_variant_element(p, 0);
+ assert_se(q && q->type == JSON_VARIANT_INTEGER && json_variant_integer(q) == 1);
+
+ /* "1"[1] == "1" */
+ q = json_variant_element(p, 1);
+ assert_se(q && q->type == JSON_VARIANT_STRING && streq(json_variant_string(q), "1"));
+
+ /* has blah */
+ p = json_variant_value(v, "blah");
+ assert_se(p && p->type == JSON_VARIANT_REAL && fabs(json_variant_real(p) - 1.27) < 0.001);
+}
+
int main(int argc, char *argv[]) {
test_one("x", -EINVAL);
@@ -102,5 +193,10 @@ int main(int argc, char *argv[]) {
test_one("\"\\udc00\\udc00\"", -EINVAL);
test_one("\"\\ud801\\udc37\"", JSON_STRING, "\xf0\x90\x90\xb7", JSON_END);
+ test_one("[1, 2]", JSON_ARRAY_OPEN, JSON_INTEGER, (intmax_t) 1, JSON_COMMA, JSON_INTEGER, (intmax_t) 2, JSON_ARRAY_CLOSE, JSON_END);
+
+ test_file("{\"k\": \"v\", \"foo\": [1, 2, 3], \"bar\": {\"zap\": null}}", test_1);
+ test_file("{\"mutant\": [1, null, \"1\", {\"1\": [1, \"1\"]}], \"blah\": 1.27}", test_2);
+
return 0;
}
diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c
index 09f0f2f89e..0045ae6824 100644
--- a/src/test/test-path-util.c
+++ b/src/test/test-path-util.c
@@ -21,6 +21,7 @@
#include <stdio.h>
#include <unistd.h>
+#include <sys/mount.h>
#include "path-util.h"
#include "util.h"
@@ -88,21 +89,9 @@ static void test_path(void) {
test_parent("/aa///file...", "/aa///");
test_parent("file.../", NULL);
- assert_se(path_is_mount_point("/", true) > 0);
- assert_se(path_is_mount_point("/", false) > 0);
-
fd = open("/", O_RDONLY|O_CLOEXEC|O_DIRECTORY|O_NOCTTY);
assert_se(fd >= 0);
- assert_se(fd_is_mount_point(fd) > 0);
-
- assert_se(path_is_mount_point("/proc", true) > 0);
- assert_se(path_is_mount_point("/proc", false) > 0);
-
- assert_se(path_is_mount_point("/proc/1", true) == 0);
- assert_se(path_is_mount_point("/proc/1", false) == 0);
-
- assert_se(path_is_mount_point("/sys", true) > 0);
- assert_se(path_is_mount_point("/sys", false) > 0);
+ assert_se(fd_is_mount_point(fd, "/", 0) > 0);
{
char p1[] = "aaa/bbb////ccc";
@@ -322,6 +311,66 @@ static void test_prefix_root(void) {
test_prefix_root_one("/foo///", "//bar", "/foo/bar");
}
+static void test_path_is_mount_point(void) {
+ int fd, rt, rf, rlt, rlf;
+ char tmp_dir[] = "/tmp/test-path-is-mount-point-XXXXXX";
+ _cleanup_free_ char *file1 = NULL, *file2 = NULL, *link1 = NULL, *link2 = NULL;
+
+ assert_se(path_is_mount_point("/", AT_SYMLINK_FOLLOW) > 0);
+ assert_se(path_is_mount_point("/", 0) > 0);
+
+ assert_se(path_is_mount_point("/proc", AT_SYMLINK_FOLLOW) > 0);
+ assert_se(path_is_mount_point("/proc", 0) > 0);
+
+ assert_se(path_is_mount_point("/proc/1", AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point("/proc/1", 0) == 0);
+
+ assert_se(path_is_mount_point("/sys", AT_SYMLINK_FOLLOW) > 0);
+ assert_se(path_is_mount_point("/sys", 0) > 0);
+
+ /* file mountpoints */
+ assert_se(mkdtemp(tmp_dir) != NULL);
+ file1 = path_join(NULL, tmp_dir, "file1");
+ assert_se(file1);
+ file2 = path_join(NULL, tmp_dir, "file2");
+ assert_se(file2);
+ fd = open(file1, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
+ assert_se(fd > 0);
+ close(fd);
+ fd = open(file2, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0664);
+ assert_se(fd > 0);
+ close(fd);
+ link1 = path_join(NULL, tmp_dir, "link1");
+ assert_se(link1);
+ assert_se(symlink("file1", link1) == 0);
+ link2 = path_join(NULL, tmp_dir, "link2");
+ assert_se(link1);
+ assert_se(symlink("file2", link2) == 0);
+
+ assert_se(path_is_mount_point(file1, AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point(file1, 0) == 0);
+ assert_se(path_is_mount_point(link1, AT_SYMLINK_FOLLOW) == 0);
+ assert_se(path_is_mount_point(link1, 0) == 0);
+
+ /* this test will only work as root */
+ if (mount(file1, file2, NULL, MS_BIND, NULL) >= 0) {
+ rf = path_is_mount_point(file2, 0);
+ rt = path_is_mount_point(file2, AT_SYMLINK_FOLLOW);
+ rlf = path_is_mount_point(link2, 0);
+ rlt = path_is_mount_point(link2, AT_SYMLINK_FOLLOW);
+
+ assert_se(umount(file2) == 0);
+
+ assert_se(rf == 1);
+ assert_se(rt == 1);
+ assert_se(rlf == 0);
+ assert_se(rlt == 1);
+ } else
+ printf("Skipping bind mount file test: %m\n");
+
+ assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
+}
+
int main(int argc, char **argv) {
test_path();
test_find_binary(argv[0], true);
@@ -333,6 +382,7 @@ int main(int argc, char **argv) {
test_strv_resolve();
test_path_startswith();
test_prefix_root();
+ test_path_is_mount_point();
return 0;
}
diff --git a/src/test/test-pty.c b/src/test/test-pty.c
index b5f4d4f094..f8807c9150 100644
--- a/src/test/test-pty.c
+++ b/src/test/test-pty.c
@@ -27,6 +27,7 @@
#include "pty.h"
#include "util.h"
+#include "signal-util.h"
static const char sndmsg[] = "message\n";
static const char rcvmsg[] = "message\r\n";
diff --git a/src/test/test-udev.c b/src/test/test-udev.c
index 23b7faa939..f3953fe26a 100644
--- a/src/test/test-udev.c
+++ b/src/test/test-udev.c
@@ -120,11 +120,6 @@ int main(int argc, char *argv[]) {
sigfillset(&mask);
sigprocmask(SIG_SETMASK, &mask, &sigmask_orig);
- event->fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
- if (event->fd_signal < 0) {
- fprintf(stderr, "error creating signalfd\n");
- goto out;
- }
/* do what devtmpfs usually provides us */
if (udev_device_get_devnode(dev) != NULL) {
@@ -153,8 +148,6 @@ int main(int argc, char *argv[]) {
3 * USEC_PER_SEC, USEC_PER_SEC,
NULL);
out:
- if (event != NULL && event->fd_signal >= 0)
- close(event->fd_signal);
mac_selinux_finish();
return err ? EXIT_FAILURE : EXIT_SUCCESS;
diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c
index a9711ac9f5..31b12d50d7 100644
--- a/src/test/test-unit-file.c
+++ b/src/test/test-unit-file.c
@@ -225,6 +225,15 @@ static void test_config_parse_exec(void) {
check_execcommand(c1,
"/sbin/find", NULL, ";", "x", false);
+ log_info("/* encoded semicolon */");
+ r = config_parse_exec(NULL, "fake", 5, "section", 1,
+ "LValue", 0,
+ "/bin/find \\073",
+ &c, NULL);
+ assert_se(r >= 0);
+ c1 = c1->command_next;
+ check_execcommand(c1, "/bin/find", NULL, "\\073", NULL, false);
+
log_info("/* spaces in the filename */");
r = config_parse_exec(NULL, "fake", 5, "section", 1,
"LValue", 0,
@@ -296,6 +305,16 @@ static void test_config_parse_exec(void) {
c1 = c1->command_next;
check_execcommand(c1, "/path ", NULL, NULL, NULL, false);
+ log_info("/* quoted backslashes */");
+ r = config_parse_exec(NULL, "fake", 5, "section", 1,
+ "LValue", 0,
+ "/bin/grep '\\w+\\K'",
+ &c, NULL);
+ assert_se(r >= 0);
+ c1 = c1->command_next;
+ check_execcommand(c1, "/bin/grep", NULL, "\\w+\\K", NULL, false);
+
+
log_info("/* trailing backslash: \\ */");
/* backslash is invalid */
r = config_parse_exec(NULL, "fake", 4, "section", 1,
diff --git a/src/test/test-util.c b/src/test/test-util.c
index 9af3e757e3..e0269821d7 100644
--- a/src/test/test-util.c
+++ b/src/test/test-util.c
@@ -39,6 +39,7 @@
#include "virt.h"
#include "process-util.h"
#include "hostname-util.h"
+#include "signal-util.h"
static void test_streq_ptr(void) {
assert_se(streq_ptr(NULL, NULL));
@@ -549,6 +550,59 @@ static void test_hostname_is_valid(void) {
assert_se(!hostname_is_valid("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"));
}
+static void test_read_hostname_config(void) {
+ char path[] = "/tmp/hostname.XXXXXX";
+ char *hostname;
+ int fd;
+
+ fd = mkostemp_safe(path, O_RDWR|O_CLOEXEC);
+ assert(fd > 0);
+ close(fd);
+
+ /* simple hostname */
+ write_string_file(path, "foo");
+ assert_se(read_hostname_config(path, &hostname) == 0);
+ assert_se(streq(hostname, "foo"));
+ free(hostname);
+ hostname = NULL;
+
+ /* with comment */
+ write_string_file(path, "# comment\nfoo");
+ assert_se(read_hostname_config(path, &hostname) == 0);
+ assert_se(hostname);
+ assert_se(streq(hostname, "foo"));
+ free(hostname);
+ hostname = NULL;
+
+ /* with comment and extra whitespace */
+ write_string_file(path, "# comment\n\n foo ");
+ assert_se(read_hostname_config(path, &hostname) == 0);
+ assert_se(hostname);
+ assert_se(streq(hostname, "foo"));
+ free(hostname);
+ hostname = NULL;
+
+ /* cleans up name */
+ write_string_file(path, "!foo/bar.com");
+ assert_se(read_hostname_config(path, &hostname) == 0);
+ assert_se(hostname);
+ assert_se(streq(hostname, "foobar.com"));
+ free(hostname);
+ hostname = NULL;
+
+ /* no value set */
+ hostname = (char*) 0x1234;
+ write_string_file(path, "# nothing here\n");
+ assert_se(read_hostname_config(path, &hostname) == -ENOENT);
+ assert_se(hostname == (char*) 0x1234); /* does not touch argument on error */
+
+ /* nonexisting file */
+ assert_se(read_hostname_config("/non/existing", &hostname) == -ENOENT);
+ assert_se(hostname == (char*) 0x1234); /* does not touch argument on error */
+
+ unlink(path);
+}
+
static void test_u64log2(void) {
assert_se(u64log2(0) == 0);
assert_se(u64log2(8) == 3);
@@ -1481,6 +1535,7 @@ int main(int argc, char *argv[]) {
test_foreach_word_quoted();
test_memdup_multiply();
test_hostname_is_valid();
+ test_read_hostname_config();
test_u64log2();
test_protect_errno();
test_parse_size();
diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c
index 735668bede..d69129ee03 100644
--- a/src/timesync/timesyncd.c
+++ b/src/timesync/timesyncd.c
@@ -24,6 +24,7 @@
#include "capability.h"
#include "clock-util.h"
#include "network-util.h"
+#include "signal-util.h"
#include "timesyncd-manager.h"
#include "timesyncd-conf.h"
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 c440170f95..97251ef0aa 100644
--- a/src/tty-ask-password-agent/tty-ask-password-agent.c
+++ b/src/tty-ask-password-agent/tty-ask-password-agent.c
@@ -44,6 +44,7 @@
#include "def.h"
#include "process-util.h"
#include "terminal-util.h"
+#include "signal-util.h"
static enum {
ACTION_LIST,
diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c
index b3e7d02543..ce038abee5 100644
--- a/src/udev/net/link-config.c
+++ b/src/udev/net/link-config.c
@@ -174,6 +174,9 @@ static int load_link(link_config_ctx *ctx, const char *filename) {
else
log_debug("Parsed configuration file %s", filename);
+ if (link->mtu > UINT_MAX || link->speed > UINT_MAX)
+ return -ERANGE;
+
link->filename = strdup(filename);
LIST_PREPEND(links, ctx->links, link);
@@ -376,10 +379,9 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
if (!old_name)
return -EINVAL;
- r = ethtool_set_speed(&ctx->ethtool_fd, old_name, config->speed / 1024,
- config->duplex);
+ r = ethtool_set_speed(&ctx->ethtool_fd, old_name, config->speed / 1024, config->duplex);
if (r < 0)
- log_warning_errno(r, "Could not set speed or duplex of %s to %u Mbps (%s): %m",
+ log_warning_errno(r, "Could not set speed or duplex of %s to %zu Mbps (%s): %m",
old_name, config->speed / 1024,
duplex_to_string(config->duplex));
@@ -458,8 +460,7 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
mac = config->mac;
}
- r = rtnl_set_link_properties(&ctx->rtnl, ifindex, config->alias, mac,
- config->mtu);
+ r = rtnl_set_link_properties(&ctx->rtnl, ifindex, config->alias, mac, config->mtu);
if (r < 0)
return log_warning_errno(r, "Could not set Alias, MACAddress or MTU on %s: %m", old_name);
diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h
index 34facdeb5d..9875057e84 100644
--- a/src/udev/net/link-config.h
+++ b/src/udev/net/link-config.h
@@ -66,8 +66,8 @@ struct link_config {
NamePolicy *name_policy;
char *name;
char *alias;
- unsigned int mtu;
- unsigned int speed;
+ size_t mtu;
+ size_t speed;
Duplex duplex;
WakeOnLan wol;
diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c
index 78aef206b2..448920507a 100644
--- a/src/udev/udev-builtin-net_id.c
+++ b/src/udev/udev-builtin-net_id.c
@@ -91,6 +91,7 @@
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
+#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <net/if.h>
@@ -166,15 +167,15 @@ static int dev_pci_onboard(struct udev_device *dev, struct netnames *names) {
/* read the 256 bytes PCI configuration space to check the multi-function bit */
static bool is_pci_multifunction(struct udev_device *dev) {
- _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_close_ int fd = -1;
const char *filename;
uint8_t config[64];
filename = strjoina(udev_device_get_syspath(dev), "/config");
- f = fopen(filename, "re");
- if (!f)
+ fd = open(filename, O_RDONLY | O_CLOEXEC);
+ if (fd < 0)
return false;
- if (fread(&config, sizeof(config), 1, f) != 1)
+ if (read(fd, &config, sizeof(config)) != sizeof(config))
return false;
/* bit 0-6 header type, bit 7 multi/single function device */
diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c
index 2fa26a40be..a5c3edbff8 100644
--- a/src/udev/udev-event.c
+++ b/src/udev/udev-event.c
@@ -30,9 +30,19 @@
#include <sys/wait.h>
#include <sys/signalfd.h>
-#include "udev.h"
#include "rtnl-util.h"
+#include "event-util.h"
#include "formats-util.h"
+#include "process-util.h"
+#include "signal-util.h"
+#include "udev.h"
+
+typedef struct Spawn {
+ const char *cmd;
+ pid_t pid;
+ usec_t timeout_warn;
+ usec_t timeout;
+} Spawn;
struct udev_event *udev_event_new(struct udev_device *dev) {
struct udev *udev = udev_device_get_udev(dev);
@@ -45,8 +55,7 @@ struct udev_event *udev_event_new(struct udev_device *dev) {
event->udev = udev;
udev_list_init(udev, &event->run_list, false);
udev_list_init(udev, &event->seclabel_list, false);
- event->fd_signal = -1;
- event->birth_usec = now(CLOCK_MONOTONIC);
+ event->birth_usec = clock_boottime_or_monotonic();
return event;
}
@@ -467,7 +476,7 @@ static void spawn_read(struct udev_event *event,
if (timeout_usec > 0) {
usec_t age_usec;
- age_usec = now(CLOCK_MONOTONIC) - event->birth_usec;
+ age_usec = clock_boottime_or_monotonic() - event->birth_usec;
if (age_usec >= timeout_usec) {
log_error("timeout '%s'", cmd);
return;
@@ -540,102 +549,116 @@ static void spawn_read(struct udev_event *event,
result[respos] = '\0';
}
+static int on_spawn_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
+ Spawn *spawn = userdata;
+ char timeout[FORMAT_TIMESTAMP_RELATIVE_MAX];
+
+ assert(spawn);
+
+ kill_and_sigcont(spawn->pid, SIGKILL);
+
+ log_error("spawned process '%s' ["PID_FMT"] timed out after %s, killing", spawn->cmd, spawn->pid,
+ format_timestamp_relative(timeout, sizeof(timeout), spawn->timeout));
+
+ return 1;
+}
+
+static int on_spawn_timeout_warning(sd_event_source *s, uint64_t usec, void *userdata) {
+ Spawn *spawn = userdata;
+ char timeout[FORMAT_TIMESTAMP_RELATIVE_MAX];
+
+ assert(spawn);
+
+ log_warning("spawned process '%s' ["PID_FMT"] is taking longer than %s to complete", spawn->cmd, spawn->pid,
+ format_timestamp_relative(timeout, sizeof(timeout), spawn->timeout));
+
+ return 1;
+}
+
+static int on_spawn_sigchld(sd_event_source *s, const siginfo_t *si, void *userdata) {
+ Spawn *spawn = userdata;
+
+ assert(spawn);
+
+ switch (si->si_code) {
+ case CLD_EXITED:
+ if (si->si_status != 0)
+ log_warning("process '%s' failed with exit code %i.", spawn->cmd, si->si_status);
+ else {
+ log_debug("process '%s' succeeded.", spawn->cmd);
+ sd_event_exit(sd_event_source_get_event(s), 0);
+
+ return 1;
+ }
+
+ break;
+ case CLD_KILLED:
+ case CLD_DUMPED:
+ log_warning("process '%s' terminated by signal %s.", spawn->cmd, signal_to_string(si->si_status));
+
+ break;
+ default:
+ log_error("process '%s' failed due to unknown reason.", spawn->cmd);
+ }
+
+ sd_event_exit(sd_event_source_get_event(s), -EIO);
+
+ return 1;
+}
+
static int spawn_wait(struct udev_event *event,
usec_t timeout_usec,
usec_t timeout_warn_usec,
const char *cmd, pid_t pid) {
- struct pollfd pfd[1];
- int err = 0;
-
- pfd[0].events = POLLIN;
- pfd[0].fd = event->fd_signal;
+ Spawn spawn = {
+ .cmd = cmd,
+ .pid = pid,
+ };
+ _cleanup_event_unref_ sd_event *e = NULL;
+ int r, ret;
- while (pid > 0) {
- int timeout;
- int timeout_warn = 0;
- int fdcount;
+ r = sd_event_new(&e);
+ if (r < 0)
+ return r;
- if (timeout_usec > 0) {
- usec_t age_usec;
+ if (timeout_usec > 0) {
+ usec_t usec, age_usec;
- age_usec = now(CLOCK_MONOTONIC) - event->birth_usec;
- if (age_usec >= timeout_usec)
- timeout = 1000;
- else {
- if (timeout_warn_usec > 0)
- timeout_warn = ((timeout_warn_usec - age_usec) / USEC_PER_MSEC) + MSEC_PER_SEC;
+ usec = now(clock_boottime_or_monotonic());
+ age_usec = usec - event->birth_usec;
+ if (age_usec < timeout_usec) {
+ if (timeout_warn_usec > 0 && timeout_warn_usec < timeout_usec && age_usec < timeout_warn_usec) {
+ spawn.timeout_warn = timeout_warn_usec - age_usec;
- timeout = ((timeout_usec - timeout_warn_usec - age_usec) / USEC_PER_MSEC) + MSEC_PER_SEC;
+ r = sd_event_add_time(e, NULL, clock_boottime_or_monotonic(),
+ usec + spawn.timeout_warn, USEC_PER_SEC,
+ on_spawn_timeout_warning, &spawn);
+ if (r < 0)
+ return r;
}
- } else {
- timeout = -1;
- }
- fdcount = poll(pfd, 1, timeout_warn);
- if (fdcount < 0) {
- if (errno == EINTR)
- continue;
- err = -errno;
- log_error_errno(errno, "failed to poll: %m");
- goto out;
- }
- if (fdcount == 0) {
- log_warning("slow: '%s' ["PID_FMT"]", cmd, pid);
+ spawn.timeout = timeout_usec - age_usec;
- fdcount = poll(pfd, 1, timeout);
- if (fdcount < 0) {
- if (errno == EINTR)
- continue;
- err = -errno;
- log_error_errno(errno, "failed to poll: %m");
- goto out;
- }
- if (fdcount == 0) {
- log_error("timeout: killing '%s' ["PID_FMT"]", cmd, pid);
- kill(pid, SIGKILL);
- }
+ r = sd_event_add_time(e, NULL, clock_boottime_or_monotonic(),
+ usec + spawn.timeout, USEC_PER_SEC, on_spawn_timeout, &spawn);
+ if (r < 0)
+ return r;
}
+ }
- if (pfd[0].revents & POLLIN) {
- struct signalfd_siginfo fdsi;
- int status;
- ssize_t size;
+ r = sd_event_add_child(e, NULL, pid, WEXITED, on_spawn_sigchld, &spawn);
+ if (r < 0)
+ return r;
- size = read(event->fd_signal, &fdsi, sizeof(struct signalfd_siginfo));
- if (size != sizeof(struct signalfd_siginfo))
- continue;
+ r = sd_event_loop(e);
+ if (r < 0)
+ return r;
- switch (fdsi.ssi_signo) {
- case SIGTERM:
- event->sigterm = true;
- break;
- case SIGCHLD:
- if (waitpid(pid, &status, WNOHANG) < 0)
- break;
- if (WIFEXITED(status)) {
- log_debug("'%s' ["PID_FMT"] exit with return code %i", cmd, pid, WEXITSTATUS(status));
- if (WEXITSTATUS(status) != 0)
- err = -1;
- } else if (WIFSIGNALED(status)) {
- log_error("'%s' ["PID_FMT"] terminated by signal %i (%s)", cmd, pid, WTERMSIG(status), strsignal(WTERMSIG(status)));
- err = -1;
- } else if (WIFSTOPPED(status)) {
- log_error("'%s' ["PID_FMT"] stopped", cmd, pid);
- err = -1;
- } else if (WIFCONTINUED(status)) {
- log_error("'%s' ["PID_FMT"] continued", cmd, pid);
- err = -1;
- } else {
- log_error("'%s' ["PID_FMT"] exit with status 0x%04x", cmd, pid, status);
- err = -1;
- }
- pid = 0;
- break;
- }
- }
- }
-out:
- return err;
+ r = sd_event_get_exit_code(e, &ret);
+ if (r < 0)
+ return r;
+
+ return ret;
}
int udev_build_argv(struct udev *udev, char *cmd, int *argc, char *argv[]) {
diff --git a/src/udev/udev.h b/src/udev/udev.h
index dece6eccab..1b17c615b8 100644
--- a/src/udev/udev.h
+++ b/src/udev/udev.h
@@ -44,11 +44,9 @@ struct udev_event {
struct udev_list run_list;
int exec_delay;
usec_t birth_usec;
- int fd_signal;
sd_rtnl *rtnl;
unsigned int builtin_run;
unsigned int builtin_ret;
- bool sigterm;
bool inotify_watch;
bool inotify_watch_final;
bool group_set;
diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c
index fe092cfbd9..46ec0e3225 100644
--- a/src/udev/udevadm-test.c
+++ b/src/udev/udevadm-test.c
@@ -131,12 +131,6 @@ static int adm_test(struct udev *udev, int argc, char *argv[]) {
sigfillset(&mask);
sigprocmask(SIG_SETMASK, &mask, &sigmask_orig);
- event->fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
- if (event->fd_signal < 0) {
- fprintf(stderr, "error creating signalfd\n");
- rc = 5;
- goto out;
- }
udev_event_execute_rules(event,
60 * USEC_PER_SEC, 20 * USEC_PER_SEC,
@@ -154,8 +148,6 @@ static int adm_test(struct udev *udev, int argc, char *argv[]) {
printf("run: '%s'\n", program);
}
out:
- if (event != NULL && event->fd_signal >= 0)
- close(event->fd_signal);
udev_builtin_exit(udev);
return rc;
}
diff --git a/src/udev/udevd.c b/src/udev/udevd.c
index 19640cb6ea..34e88af539 100644
--- a/src/udev/udevd.c
+++ b/src/udev/udevd.c
@@ -41,6 +41,8 @@
#include <sys/inotify.h>
#include "sd-daemon.h"
+#include "sd-event.h"
+#include "event-util.h"
#include "rtnl-util.h"
#include "cgroup-util.h"
#include "process-util.h"
@@ -62,6 +64,7 @@ static usec_t arg_event_timeout_warn_usec = 180 * USEC_PER_SEC / 3;
typedef struct Manager {
struct udev *udev;
+ sd_event *event;
Hashmap *workers;
struct udev_list_node events;
char *cgroup;
@@ -74,17 +77,16 @@ typedef struct Manager {
struct udev_monitor *monitor;
struct udev_ctrl *ctrl;
struct udev_ctrl_connection *ctrl_conn_blocking;
-
- int fd_ep;
- int fd_ctrl;
- int fd_uevent;
- int fd_signal;
int fd_inotify;
- int fd_worker;
int worker_watch[2];
+ sd_event_source *ctrl_event;
+ sd_event_source *uevent_event;
+ sd_event_source *inotify_event;
+
+ usec_t last_usec;
+
bool stop_exec_queue:1;
- bool reload:1;
bool exit:1;
} Manager;
@@ -110,8 +112,8 @@ struct event {
dev_t devnum;
int ifindex;
bool is_block;
- usec_t start_usec;
- bool warned;
+ sd_event_source *timeout_warning;
+ sd_event_source *timeout;
};
static inline struct event *node_to_event(struct udev_list_node *node) {
@@ -151,6 +153,9 @@ static void event_free(struct event *event) {
udev_device_unref(event->dev);
udev_device_unref(event->dev_kernel);
+ sd_event_source_unref(event->timeout_warning);
+ sd_event_source_unref(event->timeout);
+
if (event->worker)
event->worker->event = NULL;
@@ -252,7 +257,12 @@ static int on_event_timeout_warning(sd_event_source *s, uint64_t usec, void *use
}
static void worker_attach_event(struct worker *worker, struct event *event) {
+ sd_event *e;
+ uint64_t usec;
+ int r;
+
assert(worker);
+ assert(worker->manager);
assert(event);
assert(!event->worker);
assert(!worker->event);
@@ -260,9 +270,19 @@ static void worker_attach_event(struct worker *worker, struct event *event) {
worker->state = WORKER_RUNNING;
worker->event = event;
event->state = EVENT_RUNNING;
- event->start_usec = now(CLOCK_MONOTONIC);
- event->warned = false;
event->worker = worker;
+
+ e = worker->manager->event;
+
+ r = sd_event_now(e, clock_boottime_or_monotonic(), &usec);
+ if (r < 0)
+ return;
+
+ (void) sd_event_add_time(e, &event->timeout_warning, clock_boottime_or_monotonic(),
+ usec + arg_event_timeout_warn_usec, USEC_PER_SEC, on_event_timeout_warning, event);
+
+ (void) sd_event_add_time(e, &event->timeout, clock_boottime_or_monotonic(),
+ usec + arg_event_timeout_usec, USEC_PER_SEC, on_event_timeout, event);
}
static void manager_free(Manager *manager) {
@@ -271,7 +291,12 @@ static void manager_free(Manager *manager) {
udev_builtin_exit(manager->udev);
+ sd_event_source_unref(manager->ctrl_event);
+ sd_event_source_unref(manager->uevent_event);
+ sd_event_source_unref(manager->inotify_event);
+
udev_unref(manager->udev);
+ sd_event_unref(manager->event);
manager_workers_free(manager);
event_queue_cleanup(manager, EVENT_UNDEF);
@@ -283,8 +308,6 @@ static void manager_free(Manager *manager) {
udev_rules_unref(manager->rules);
free(manager->cgroup);
- safe_close(manager->fd_ep);
- safe_close(manager->fd_signal);
safe_close(manager->fd_inotify);
safe_close_pair(manager->worker_watch);
@@ -316,10 +339,11 @@ static void worker_spawn(Manager *manager, struct event *event) {
switch (pid) {
case 0: {
struct udev_device *dev = NULL;
+ _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
int fd_monitor;
_cleanup_close_ int fd_signal = -1, fd_ep = -1;
- _cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
- struct epoll_event ep_signal, ep_monitor;
+ struct epoll_event ep_signal = { .events = EPOLLIN };
+ struct epoll_event ep_monitor = { .events = EPOLLIN };
sigset_t mask;
int r = 0;
@@ -327,13 +351,22 @@ static void worker_spawn(Manager *manager, struct event *event) {
dev = event->dev;
event->dev = NULL;
+ unsetenv("NOTIFY_SOCKET");
+
manager_workers_free(manager);
event_queue_cleanup(manager, EVENT_UNDEF);
+
manager->monitor = udev_monitor_unref(manager->monitor);
+ manager->ctrl_conn_blocking = udev_ctrl_connection_unref(manager->ctrl_conn_blocking);
manager->ctrl = udev_ctrl_unref(manager->ctrl);
- manager->fd_signal = safe_close(manager->fd_signal);
+ manager->ctrl_conn_blocking = udev_ctrl_connection_unref(manager->ctrl_conn_blocking);
manager->worker_watch[READ_END] = safe_close(manager->worker_watch[READ_END]);
- manager->fd_ep = safe_close(manager->fd_ep);
+
+ manager->ctrl_event = sd_event_source_unref(manager->ctrl_event);
+ manager->uevent_event = sd_event_source_unref(manager->uevent_event);
+ manager->inotify_event = sd_event_source_unref(manager->inotify_event);
+
+ manager->event = sd_event_unref(manager->event);
sigfillset(&mask);
fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
@@ -341,6 +374,10 @@ static void worker_spawn(Manager *manager, struct event *event) {
r = log_error_errno(errno, "error creating signalfd %m");
goto out;
}
+ ep_signal.data.fd = fd_signal;
+
+ fd_monitor = udev_monitor_get_fd(worker_monitor);
+ ep_monitor.data.fd = fd_monitor;
fd_ep = epoll_create1(EPOLL_CLOEXEC);
if (fd_ep < 0) {
@@ -348,15 +385,6 @@ static void worker_spawn(Manager *manager, struct event *event) {
goto out;
}
- memzero(&ep_signal, sizeof(struct epoll_event));
- ep_signal.events = EPOLLIN;
- ep_signal.data.fd = fd_signal;
-
- fd_monitor = udev_monitor_get_fd(worker_monitor);
- memzero(&ep_monitor, sizeof(struct epoll_event));
- ep_monitor.events = EPOLLIN;
- ep_monitor.data.fd = fd_monitor;
-
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) {
r = log_error_errno(errno, "fail to add fds to epoll: %m");
@@ -380,9 +408,6 @@ static void worker_spawn(Manager *manager, struct event *event) {
goto out;
}
- /* needed for SIGCHLD/SIGTERM in spawn() */
- udev_event->fd_signal = fd_signal;
-
if (arg_exec_delay > 0)
udev_event->exec_delay = arg_exec_delay;
@@ -455,11 +480,6 @@ skip:
udev_device_unref(dev);
dev = NULL;
- if (udev_event->sigterm) {
- udev_event_unref(udev_event);
- goto out;
- }
-
udev_event_unref(udev_event);
/* wait for more device messages from main udevd, or term signal */
@@ -564,7 +584,10 @@ static int event_queue_insert(Manager *manager, struct udev_device *dev) {
assert(manager);
assert(dev);
- /* only the main process can add events to the queue */
+ /* only one process can add events to the queue */
+ if (manager->pid == 0)
+ manager->pid = getpid();
+
assert(manager->pid == getpid());
event = new0(struct event, 1);
@@ -687,11 +710,104 @@ static bool is_devpath_busy(Manager *manager, struct event *event) {
return false;
}
+static int on_exit_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
+ Manager *manager = userdata;
+
+ assert(manager);
+
+ log_error_errno(ETIMEDOUT, "giving up waiting for workers to finish");
+
+ sd_event_exit(manager->event, -ETIMEDOUT);
+
+ return 1;
+}
+
+static void manager_exit(Manager *manager) {
+ uint64_t usec;
+ int r;
+
+ assert(manager);
+
+ manager->exit = true;
+
+ sd_notify(false,
+ "STOPPING=1\n"
+ "STATUS=Starting shutdown...");
+
+ /* close sources of new events and discard buffered events */
+ manager->ctrl = udev_ctrl_unref(manager->ctrl);
+ manager->ctrl_event = sd_event_source_unref(manager->ctrl_event);
+
+ manager->fd_inotify = safe_close(manager->fd_inotify);
+ manager->inotify_event = sd_event_source_unref(manager->inotify_event);
+
+ manager->monitor = udev_monitor_unref(manager->monitor);
+ manager->uevent_event = sd_event_source_unref(manager->uevent_event);
+
+ /* discard queued events and kill workers */
+ event_queue_cleanup(manager, EVENT_QUEUED);
+ manager_kill_workers(manager);
+
+ r = sd_event_now(manager->event, clock_boottime_or_monotonic(), &usec);
+ if (r < 0)
+ return;
+
+ r = sd_event_add_time(manager->event, NULL, clock_boottime_or_monotonic(),
+ usec + 30 * USEC_PER_SEC, USEC_PER_SEC, on_exit_timeout, manager);
+ if (r < 0)
+ return;
+}
+
+/* reload requested, HUP signal received, rules changed, builtin changed */
+static void manager_reload(Manager *manager) {
+
+ assert(manager);
+
+ sd_notify(false,
+ "RELOADING=1\n"
+ "STATUS=Flushing configuration...");
+
+ manager_kill_workers(manager);
+ manager->rules = udev_rules_unref(manager->rules);
+ udev_builtin_exit(manager->udev);
+
+ sd_notify(false,
+ "READY=1\n"
+ "STATUS=Processing...");
+}
+
static void event_queue_start(Manager *manager) {
struct udev_list_node *loop;
+ usec_t usec;
+ int r;
assert(manager);
+ if (udev_list_node_is_empty(&manager->events) ||
+ manager->exit || manager->stop_exec_queue)
+ return;
+
+ r = sd_event_now(manager->event, clock_boottime_or_monotonic(), &usec);
+ if (r >= 0) {
+ /* check for changed config, every 3 seconds at most */
+ if (manager->last_usec == 0 ||
+ (usec - manager->last_usec) > 3 * USEC_PER_SEC) {
+ if (udev_rules_check_timestamp(manager->rules) ||
+ udev_builtin_validate(manager->udev))
+ manager_reload(manager);
+
+ manager->last_usec = usec;
+ }
+ }
+
+ udev_builtin_init(manager->udev);
+
+ if (!manager->rules) {
+ manager->rules = udev_rules_new(manager->udev, arg_resolve_names);
+ if (!manager->rules)
+ return;
+ }
+
udev_list_node_foreach(loop, &manager->events) {
struct event *event = node_to_event(loop);
@@ -785,6 +901,9 @@ static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdat
event_free(worker->event);
}
+ /* we have free workers, try to schedule events */
+ event_queue_start(manager);
+
return 1;
}
@@ -801,6 +920,9 @@ static int on_uevent(sd_event_source *s, int fd, uint32_t revents, void *userdat
r = event_queue_insert(manager, dev);
if (r < 0)
udev_device_unref(dev);
+ else
+ /* we have fresh events, try to schedule them */
+ event_queue_start(manager);
}
return 1;
@@ -839,11 +961,12 @@ static int on_ctrl_msg(sd_event_source *s, int fd, uint32_t revents, void *userd
if (udev_ctrl_get_start_exec_queue(ctrl_msg) > 0) {
log_debug("udevd message (START_EXEC_QUEUE) received");
manager->stop_exec_queue = false;
+ event_queue_start(manager);
}
if (udev_ctrl_get_reload(ctrl_msg) > 0) {
log_debug("udevd message (RELOAD) received");
- manager->reload = true;
+ manager_reload(manager);
}
str = udev_ctrl_get_set_env(ctrl_msg);
@@ -882,7 +1005,7 @@ static int on_ctrl_msg(sd_event_source *s, int fd, uint32_t revents, void *userd
if (udev_ctrl_get_exit(ctrl_msg) > 0) {
log_debug("udevd message (EXIT) received");
- manager->exit = true;
+ manager_exit(manager);
/* keep reference to block the client until we exit
TODO: deal with several blocking exit requests */
manager->ctrl_conn_blocking = udev_ctrl_connection_ref(ctrl_conn);
@@ -1040,7 +1163,7 @@ static int on_sigterm(sd_event_source *s, const struct signalfd_siginfo *si, voi
assert(manager);
- manager->exit = true;
+ manager_exit(manager);
return 1;
}
@@ -1050,7 +1173,7 @@ static int on_sighup(sd_event_source *s, const struct signalfd_siginfo *si, void
assert(manager);
- manager->reload = true;
+ manager_reload(manager);
return 1;
}
@@ -1105,6 +1228,36 @@ static int on_sigchld(sd_event_source *s, const struct signalfd_siginfo *si, voi
worker_free(worker);
}
+ /* we can start new workers, try to schedule events */
+ event_queue_start(manager);
+
+ return 1;
+}
+
+static int on_post(sd_event_source *s, void *userdata) {
+ Manager *manager = userdata;
+ int r;
+
+ assert(manager);
+
+ if (udev_list_node_is_empty(&manager->events)) {
+ /* no pending events */
+ if (!hashmap_isempty(manager->workers)) {
+ /* there are idle workers */
+ log_debug("cleanup idle workers");
+ manager_kill_workers(manager);
+ } else {
+ /* we are idle */
+ if (manager->exit) {
+ r = sd_event_exit(manager->event, 0);
+ if (r < 0)
+ return r;
+ } else if (manager->cgroup)
+ /* cleanup possible left-over processes in our cgroup */
+ cg_kill(SYSTEMD_CGROUP_CONTROLLER, manager->cgroup, SIGKILL, false, true, NULL);
+ }
+ }
+
return 1;
}
@@ -1286,13 +1439,7 @@ static int parse_argv(int argc, char *argv[]) {
static int manager_new(Manager **ret) {
_cleanup_(manager_freep) Manager *manager = NULL;
- struct epoll_event ep_ctrl = { .events = EPOLLIN };
- struct epoll_event ep_inotify = { .events = EPOLLIN };
- struct epoll_event ep_signal = { .events = EPOLLIN };
- struct epoll_event ep_netlink = { .events = EPOLLIN };
- struct epoll_event ep_worker = { .events = EPOLLIN };
- sigset_t mask;
- int r, one = 1;
+ int r, fd_ctrl, fd_uevent;
assert(ret);
@@ -1300,12 +1447,6 @@ static int manager_new(Manager **ret) {
if (!manager)
return log_oom();
- manager->pid = getpid();
-
- manager->fd_ep = -1;
- manager->fd_ctrl = -1;
- manager->fd_uevent = -1;
- manager->fd_signal = -1;
manager->fd_inotify = -1;
manager->worker_watch[WRITE_END] = -1;
manager->worker_watch[READ_END] = -1;
@@ -1323,14 +1464,14 @@ static int manager_new(Manager **ret) {
udev_list_node_init(&manager->events);
udev_list_init(manager->udev, &manager->properties, true);
- r = systemd_fds(&manager->fd_ctrl, &manager->fd_uevent);
+ r = systemd_fds(&fd_ctrl, &fd_uevent);
if (r >= 0) {
/* get control and netlink socket from systemd */
- manager->ctrl = udev_ctrl_new_from_fd(manager->udev, manager->fd_ctrl);
+ manager->ctrl = udev_ctrl_new_from_fd(manager->udev, fd_ctrl);
if (!manager->ctrl)
return log_error_errno(EINVAL, "error taking over udev control socket");
- manager->monitor = udev_monitor_new_from_netlink_fd(manager->udev, "kernel", manager->fd_uevent);
+ manager->monitor = udev_monitor_new_from_netlink_fd(manager->udev, "kernel", fd_uevent);
if (!manager->monitor)
return log_error_errno(EINVAL, "error taking over netlink socket");
@@ -1344,13 +1485,13 @@ static int manager_new(Manager **ret) {
if (!manager->ctrl)
return log_error_errno(EINVAL, "error initializing udev control socket");
- manager->fd_ctrl = udev_ctrl_get_fd(manager->ctrl);
+ fd_ctrl = udev_ctrl_get_fd(manager->ctrl);
manager->monitor = udev_monitor_new_from_netlink(manager->udev, "kernel");
if (!manager->monitor)
return log_error_errno(EINVAL, "error initializing netlink socket");
- manager->fd_uevent = udev_monitor_get_fd(manager->monitor);
+ fd_uevent = udev_monitor_get_fd(manager->monitor);
(void) udev_monitor_set_receive_buffer_size(manager->monitor, 128 * 1024 * 1024);
}
@@ -1363,14 +1504,26 @@ static int manager_new(Manager **ret) {
if (r < 0)
return log_error_errno(EINVAL, "error binding udev control socket");
+ *ret = manager;
+ manager = NULL;
+
+ return 0;
+}
+
+static int manager_listen(Manager *manager) {
+ sigset_t mask;
+ int r, fd_worker, one = 1;
+
+ assert(manager);
+
/* unnamed socket from workers to the main daemon */
r = socketpair(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0, manager->worker_watch);
if (r < 0)
return log_error_errno(errno, "error creating socketpair: %m");
- manager->fd_worker = manager->worker_watch[READ_END];
+ fd_worker = manager->worker_watch[READ_END];
- r = setsockopt(manager->fd_worker, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
+ r = setsockopt(fd_worker, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
if (r < 0)
return log_error_errno(errno, "could not enable SO_PASSCRED: %m");
@@ -1383,31 +1536,60 @@ static int manager_new(Manager **ret) {
/* block and listen to all signals on signalfd */
sigfillset(&mask);
sigprocmask(SIG_SETMASK, &mask, &manager->sigmask_orig);
- manager->fd_signal = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
- if (manager->fd_signal < 0)
- return log_error_errno(errno, "error creating signalfd");
-
- ep_ctrl.data.fd = manager->fd_ctrl;
- ep_inotify.data.fd = manager->fd_inotify;
- ep_signal.data.fd = manager->fd_signal;
- ep_netlink.data.fd = manager->fd_uevent;
- ep_worker.data.fd = manager->fd_worker;
-
- manager->fd_ep = epoll_create1(EPOLL_CLOEXEC);
- if (manager->fd_ep < 0)
- return log_error_errno(errno, "error creating epoll fd: %m");
-
- if (epoll_ctl(manager->fd_ep, EPOLL_CTL_ADD, manager->fd_ctrl, &ep_ctrl) < 0 ||
- epoll_ctl(manager->fd_ep, EPOLL_CTL_ADD, manager->fd_inotify, &ep_inotify) < 0 ||
- epoll_ctl(manager->fd_ep, EPOLL_CTL_ADD, manager->fd_signal, &ep_signal) < 0 ||
- epoll_ctl(manager->fd_ep, EPOLL_CTL_ADD, manager->fd_uevent, &ep_netlink) < 0 ||
- epoll_ctl(manager->fd_ep, EPOLL_CTL_ADD, manager->fd_worker, &ep_worker) < 0)
- return log_error_errno(errno, "fail to add fds to epoll: %m");
- *ret = manager;
- manager = NULL;
+ r = sd_event_default(&manager->event);
+ if (r < 0)
+ return log_error_errno(errno, "could not allocate event loop: %m");
- return 1;
+ r = sd_event_add_signal(manager->event, NULL, SIGINT, on_sigterm, manager);
+ if (r < 0)
+ return log_error_errno(r, "error creating sigint event source: %m");
+
+ r = sd_event_add_signal(manager->event, NULL, SIGTERM, on_sigterm, manager);
+ if (r < 0)
+ return log_error_errno(r, "error creating sigterm event source: %m");
+
+ r = sd_event_add_signal(manager->event, NULL, SIGHUP, on_sighup, manager);
+ if (r < 0)
+ return log_error_errno(r, "error creating sighup event source: %m");
+
+ r = sd_event_add_signal(manager->event, NULL, SIGCHLD, on_sigchld, manager);
+ if (r < 0)
+ return log_error_errno(r, "error creating sigchld event source: %m");
+
+ r = sd_event_set_watchdog(manager->event, true);
+ if (r < 0)
+ return log_error_errno(r, "error creating watchdog event source: %m");
+
+ r = sd_event_add_io(manager->event, &manager->ctrl_event, udev_ctrl_get_fd(manager->ctrl), EPOLLIN, on_ctrl_msg, manager);
+ if (r < 0)
+ return log_error_errno(r, "error creating ctrl event source: %m");
+
+ /* This needs to be after the inotify and uevent handling, to make sure
+ * that the ping is send back after fully processing the pending uevents
+ * (including the synthetic ones we may create due to inotify events).
+ */
+ r = sd_event_source_set_priority(manager->ctrl_event, SD_EVENT_PRIORITY_IDLE);
+ if (r < 0)
+ return log_error_errno(r, "cold not set IDLE event priority for ctrl event source: %m");
+
+ r = sd_event_add_io(manager->event, &manager->inotify_event, manager->fd_inotify, EPOLLIN, on_inotify, manager);
+ if (r < 0)
+ return log_error_errno(r, "error creating inotify event source: %m");
+
+ r = sd_event_add_io(manager->event, &manager->uevent_event, udev_monitor_get_fd(manager->monitor), EPOLLIN, on_uevent, manager);
+ if (r < 0)
+ return log_error_errno(r, "error creating uevent event source: %m");
+
+ r = sd_event_add_io(manager->event, NULL, fd_worker, EPOLLIN, on_worker, manager);
+ if (r < 0)
+ return log_error_errno(r, "error creating worker event source: %m");
+
+ r = sd_event_add_post(manager->event, NULL, on_post, manager);
+ if (r < 0)
+ return log_error_errno(r, "error creating post event source: %m");
+
+ return 0;
}
int main(int argc, char *argv[]) {
@@ -1446,23 +1628,6 @@ int main(int argc, char *argv[]) {
log_debug("set children_max to %u", arg_children_max);
}
- /* before opening new files, make sure std{in,out,err} fds are in a sane state */
- if (arg_daemonize) {
- int fd;
-
- fd = open("/dev/null", O_RDWR);
- if (fd < 0)
- log_error("cannot open /dev/null");
- else {
- if (write(STDOUT_FILENO, 0, 0) < 0)
- dup2(fd, STDOUT_FILENO);
- if (write(STDERR_FILENO, 0, 0) < 0)
- dup2(fd, STDERR_FILENO);
- if (fd > STDERR_FILENO)
- close(fd);
- }
- }
-
/* set umask before creating any file/directory */
r = chdir("/");
if (r < 0) {
@@ -1484,14 +1649,12 @@ int main(int argc, char *argv[]) {
goto exit;
}
- dev_setup(NULL);
+ dev_setup(NULL, UID_INVALID, GID_INVALID);
r = manager_new(&manager);
if (r < 0)
goto exit;
- log_info("starting version " VERSION);
-
r = udev_rules_apply_static_dev_perms(manager->rules);
if (r < 0)
log_error_errno(r, "failed to apply permissions on static device nodes: %m");
@@ -1499,6 +1662,8 @@ int main(int argc, char *argv[]) {
if (arg_daemonize) {
pid_t pid;
+ log_info("starting version " VERSION);
+
pid = fork();
switch (pid) {
case 0:
@@ -1516,187 +1681,27 @@ int main(int argc, char *argv[]) {
write_string_file("/proc/self/oom_score_adj", "-1000");
} else
- sd_notify(1, "READY=1");
+ sd_notify(false,
+ "READY=1\n"
+ "STATUS=Processing...");
- for (;;) {
- static usec_t last_usec;
- struct epoll_event ev[8];
- int fdcount;
- int timeout;
- bool is_worker, is_signal, is_inotify, is_uevent, is_ctrl;
- int i;
-
- if (manager->exit) {
- /* close sources of new events and discard buffered events */
- if (manager->fd_ctrl >= 0) {
- epoll_ctl(manager->fd_ep, EPOLL_CTL_DEL, manager->fd_ctrl, NULL);
- manager->fd_ctrl = safe_close(manager->fd_ctrl);
- }
-
- if (manager->monitor) {
- epoll_ctl(manager->fd_ep, EPOLL_CTL_DEL, manager->fd_uevent, NULL);
- manager->monitor = udev_monitor_unref(manager->monitor);
- }
-
- if (manager->fd_inotify >= 0) {
- epoll_ctl(manager->fd_ep, EPOLL_CTL_DEL, manager->fd_inotify, NULL);
- manager->fd_inotify = safe_close(manager->fd_inotify);
- }
-
- /* discard queued events and kill workers */
- event_queue_cleanup(manager, EVENT_QUEUED);
- manager_kill_workers(manager);
-
- /* exit after all has cleaned up */
- if (udev_list_node_is_empty(&manager->events) && hashmap_isempty(manager->workers))
- break;
-
- /* timeout at exit for workers to finish */
- timeout = 30 * MSEC_PER_SEC;
- } else if (udev_list_node_is_empty(&manager->events) && hashmap_isempty(manager->workers)) {
- /* we are idle */
- timeout = -1;
-
- /* cleanup possible left-over processes in our cgroup */
- if (manager->cgroup)
- cg_kill(SYSTEMD_CGROUP_CONTROLLER, manager->cgroup, SIGKILL, false, true, NULL);
- } else {
- /* kill idle or hanging workers */
- timeout = 3 * MSEC_PER_SEC;
- }
-
- fdcount = epoll_wait(manager->fd_ep, ev, ELEMENTSOF(ev), timeout);
- if (fdcount < 0)
- continue;
-
- if (fdcount == 0) {
- struct worker *worker;
- Iterator j;
-
- /* timeout */
- if (manager->exit) {
- log_error("timeout, giving up waiting for workers to finish");
- break;
- }
-
- /* kill idle workers */
- if (udev_list_node_is_empty(&manager->events)) {
- log_debug("cleanup idle workers");
- manager_kill_workers(manager);
- }
-
- /* check for hanging events */
- HASHMAP_FOREACH(worker, manager->workers, j) {
- struct event *event = worker->event;
- usec_t ts;
-
- if (worker->state != WORKER_RUNNING)
- continue;
-
- assert(event);
-
- ts = now(CLOCK_MONOTONIC);
-
- if ((ts - event->start_usec) > arg_event_timeout_warn_usec) {
- if ((ts - event->start_usec) > arg_event_timeout_usec)
- on_event_timeout(NULL, 0, event);
- else if (!event->warned) {
- on_event_timeout_warning(NULL, 0, event);
- event->warned = true;
- }
- }
- }
-
- }
-
- is_worker = is_signal = is_inotify = is_uevent = is_ctrl = false;
- for (i = 0; i < fdcount; i++) {
- if (ev[i].data.fd == manager->fd_worker && ev[i].events & EPOLLIN)
- is_worker = true;
- else if (ev[i].data.fd == manager->fd_uevent && ev[i].events & EPOLLIN)
- is_uevent = true;
- else if (ev[i].data.fd == manager->fd_signal && ev[i].events & EPOLLIN)
- is_signal = true;
- else if (ev[i].data.fd == manager->fd_inotify && ev[i].events & EPOLLIN)
- is_inotify = true;
- else if (ev[i].data.fd == manager->fd_ctrl && ev[i].events & EPOLLIN)
- is_ctrl = true;
- }
-
- /* check for changed config, every 3 seconds at most */
- if ((now(CLOCK_MONOTONIC) - last_usec) > 3 * USEC_PER_SEC) {
- if (udev_rules_check_timestamp(manager->rules))
- manager->reload = true;
- if (udev_builtin_validate(manager->udev))
- manager->reload = true;
-
- last_usec = now(CLOCK_MONOTONIC);
- }
-
- /* reload requested, HUP signal received, rules changed, builtin changed */
- if (manager->reload) {
- manager_kill_workers(manager);
- manager->rules = udev_rules_unref(manager->rules);
- udev_builtin_exit(manager->udev);
- manager->reload = false;
- }
-
- /* event has finished */
- if (is_worker)
- on_worker(NULL, manager->fd_worker, 0, manager);
-
- /* uevent from kernel */
- if (is_uevent)
- on_uevent(NULL, manager->fd_uevent, 0, manager);
-
- /* start new events */
- if (!udev_list_node_is_empty(&manager->events) && !manager->exit && !manager->stop_exec_queue) {
- udev_builtin_init(manager->udev);
- if (!manager->rules)
- manager->rules = udev_rules_new(manager->udev, arg_resolve_names);
- if (manager->rules)
- event_queue_start(manager);
- }
-
- if (is_signal) {
- struct signalfd_siginfo fdsi;
- ssize_t size;
-
- size = read(manager->fd_signal, &fdsi, sizeof(struct signalfd_siginfo));
- if (size == sizeof(struct signalfd_siginfo)) {
- switch (fdsi.ssi_signo) {
- case SIGINT:
- case SIGTERM:
- on_sigterm(NULL, &fdsi, manager);
- break;
- case SIGHUP:
- on_sighup(NULL, &fdsi, manager);
- break;
- case SIGCHLD:
- on_sigchld(NULL, &fdsi, manager);
- break;
- }
- }
- }
-
- /* we are shutting down, the events below are not handled anymore */
- if (manager->exit)
- continue;
-
- /* device node watch */
- if (is_inotify)
- on_inotify(NULL, manager->fd_inotify, 0, manager);
+ r = manager_listen(manager);
+ if (r < 0)
+ return log_error_errno(r, "failed to set up fds and listen for events: %m");
- /*
- * This needs to be after the inotify handling, to make sure,
- * that the ping is send back after the possibly generated
- * "change" events by the inotify device node watch.
- */
- if (is_ctrl)
- on_ctrl_msg(NULL, manager->fd_ctrl, 0, manager);
+ r = sd_event_loop(manager->event);
+ if (r < 0) {
+ log_error_errno(r, "event loop failed: %m");
+ goto exit;
}
+ sd_event_get_exit_code(manager->event, &r);
+
exit:
+ sd_notify(false,
+ "STOPPING=1\n"
+ "STATUS=Shutting down...");
+
if (manager)
udev_ctrl_cleanup(manager->ctrl);
mac_selinux_finish();