summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/basic/architecture.c3
-rw-r--r--src/basic/architecture.h4
-rw-r--r--src/basic/copy.c54
-rw-r--r--src/basic/copy.h1
-rw-r--r--src/basic/dirent-util.c12
-rw-r--r--src/basic/dirent-util.h2
-rw-r--r--src/basic/fd-util.c10
-rw-r--r--src/basic/fdset.c2
-rw-r--r--src/basic/hashmap.c10
-rw-r--r--src/basic/missing.h4
-rw-r--r--src/basic/parse-util.h2
-rw-r--r--src/basic/path-util.c68
-rw-r--r--src/basic/path-util.h3
-rw-r--r--src/basic/process-util.c12
-rw-r--r--src/basic/process-util.h4
-rw-r--r--src/basic/rlimit-util.c52
-rw-r--r--src/basic/util.c2
-rw-r--r--src/core/automount.c3
-rw-r--r--src/core/busname.c3
-rw-r--r--src/core/dbus-manager.c55
-rw-r--r--src/core/dbus-socket.c2
-rw-r--r--src/core/dbus-unit.c3
-rw-r--r--src/core/device.c2
-rw-r--r--src/core/load-fragment-gperf.gperf.m45
-rw-r--r--src/core/load-fragment.c29
-rw-r--r--src/core/main.c4
-rw-r--r--src/core/mount.c16
-rw-r--r--src/core/org.freedesktop.systemd1.conf8
-rw-r--r--src/core/scope.c2
-rw-r--r--src/core/service.c34
-rw-r--r--src/core/service.h1
-rw-r--r--src/core/slice.c2
-rw-r--r--src/core/socket.c81
-rw-r--r--src/core/socket.h3
-rw-r--r--src/core/swap.c3
-rw-r--r--src/core/system.conf2
-rw-r--r--src/core/unit.c23
-rw-r--r--src/core/unit.h6
-rw-r--r--src/core/user.conf2
-rw-r--r--src/import/pull-common.c2
-rw-r--r--src/journal/journal-file.c41
-rw-r--r--src/journal/journald-gperf.gperf2
-rw-r--r--src/journal/journald.conf2
-rw-r--r--src/libsystemd/sd-netlink/netlink-message.c6
-rw-r--r--src/libsystemd/sd-netlink/rtnl-message.c28
-rw-r--r--src/locale/language-fallback-map4
-rw-r--r--src/machine/image-dbus.c75
-rw-r--r--src/machine/machine-dbus.c66
-rw-r--r--src/machine/machine.c24
-rw-r--r--src/machine/machine.h20
-rw-r--r--src/machine/machinectl.c78
-rw-r--r--src/machine/machined.c8
-rw-r--r--src/machine/machined.h4
-rw-r--r--src/machine/operation.c131
-rw-r--r--src/machine/operation.h47
-rw-r--r--src/network/networkd-address.c48
-rw-r--r--src/network/networkd-address.h1
-rw-r--r--src/network/networkd-conf.c55
-rw-r--r--src/network/networkd-gperf.gperf4
-rw-r--r--src/network/networkd-link.c32
-rw-r--r--src/network/networkd-network-gperf.gperf6
-rw-r--r--src/network/networkd-network.c1
-rw-r--r--src/network/networkd-route.c64
-rw-r--r--src/network/networkd-route.h3
-rw-r--r--src/nspawn/nspawn.c2
-rw-r--r--src/resolve/resolved-dns-transaction.c65
-rw-r--r--src/shared/dropin.c2
-rw-r--r--src/shared/install.c80
-rw-r--r--src/shared/install.h5
-rw-r--r--src/shared/machine-image.c22
-rw-r--r--src/systemctl/systemctl.c88
-rw-r--r--src/systemd/sd-netlink.h2
-rw-r--r--src/test/test-copy.c31
-rw-r--r--src/test/test-install-root.c2
-rw-r--r--src/test/test-install.c2
-rw-r--r--src/test/test-path-util.c22
-rw-r--r--src/test/test-rlimit-util.c12
-rw-r--r--src/test/test-unit-file.c2
-rw-r--r--src/tty-ask-password-agent/tty-ask-password-agent.c2
79 files changed, 1192 insertions, 433 deletions
diff --git a/src/basic/architecture.c b/src/basic/architecture.c
index a9ecfc1cd6..8e2c2b02d2 100644
--- a/src/basic/architecture.c
+++ b/src/basic/architecture.c
@@ -121,6 +121,8 @@ int uname_architecture(void) {
{ "tilegx", ARCHITECTURE_TILEGX },
#elif defined(__cris__)
{ "crisv32", ARCHITECTURE_CRIS },
+#elif defined(__nios2__)
+ { "nios2", ARCHITECTURE_NIOS2 },
#else
#error "Please register your architecture here!"
#endif
@@ -171,6 +173,7 @@ static const char *const architecture_table[_ARCHITECTURE_MAX] = {
[ARCHITECTURE_M68K] = "m68k",
[ARCHITECTURE_TILEGX] = "tilegx",
[ARCHITECTURE_CRIS] = "cris",
+ [ARCHITECTURE_NIOS2] = "nios2",
};
DEFINE_STRING_TABLE_LOOKUP(architecture, int);
diff --git a/src/basic/architecture.h b/src/basic/architecture.h
index c22cbc8279..91ec108e04 100644
--- a/src/basic/architecture.h
+++ b/src/basic/architecture.h
@@ -57,6 +57,7 @@ enum {
ARCHITECTURE_M68K,
ARCHITECTURE_TILEGX,
ARCHITECTURE_CRIS,
+ ARCHITECTURE_NIOS2,
_ARCHITECTURE_MAX,
_ARCHITECTURE_INVALID = -1
};
@@ -187,6 +188,9 @@ int uname_architecture(void);
#elif defined(__cris__)
# define native_architecture() ARCHITECTURE_CRIS
# error "Missing LIB_ARCH_TUPLE for CRIS"
+#elif defined(__nios2__)
+# define native_architecture() ARCHITECTURE_NIOS2
+# define LIB_ARCH_TUPLE "nios2-linux-gnu"
#else
# error "Please register your architecture here!"
#endif
diff --git a/src/basic/copy.c b/src/basic/copy.c
index 03487a6878..c3586728d0 100644
--- a/src/basic/copy.c
+++ b/src/basic/copy.c
@@ -305,6 +305,8 @@ static int fd_copy_directory(
fdf = openat(df, from, O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
else
fdf = fcntl(df, F_DUPFD_CLOEXEC, 3);
+ if (fdf < 0)
+ return -errno;
d = fdopendir(fdf);
if (!d)
@@ -325,22 +327,6 @@ static int fd_copy_directory(
r = 0;
- if (created) {
- struct timespec ut[2] = {
- st->st_atim,
- st->st_mtim
- };
-
- if (fchown(fdt, st->st_uid, st->st_gid) < 0)
- r = -errno;
-
- if (fchmod(fdt, st->st_mode & 07777) < 0)
- r = -errno;
-
- (void) futimens(fdt, ut);
- (void) copy_xattr(dirfd(d), fdt);
- }
-
FOREACH_DIRENT_ALL(de, d, return -errno) {
struct stat buf;
int q;
@@ -364,7 +350,7 @@ static int fd_copy_directory(
q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name);
else if (S_ISFIFO(buf.st_mode))
q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name);
- else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode))
+ else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode) || S_ISSOCK(buf.st_mode))
q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name);
else
q = -EOPNOTSUPP;
@@ -376,6 +362,22 @@ static int fd_copy_directory(
r = q;
}
+ if (created) {
+ struct timespec ut[2] = {
+ st->st_atim,
+ st->st_mtim
+ };
+
+ if (fchown(fdt, st->st_uid, st->st_gid) < 0)
+ r = -errno;
+
+ if (fchmod(fdt, st->st_mode & 07777) < 0)
+ r = -errno;
+
+ (void) copy_xattr(dirfd(d), fdt);
+ (void) futimens(fdt, ut);
+ }
+
return r;
}
@@ -396,7 +398,7 @@ int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge)
return fd_copy_symlink(fdf, from, &st, fdt, to);
else if (S_ISFIFO(st.st_mode))
return fd_copy_fifo(fdf, from, &st, fdt, to);
- else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
+ else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) || S_ISSOCK(st.st_mode))
return fd_copy_node(fdf, from, &st, fdt, to);
else
return -EOPNOTSUPP;
@@ -407,7 +409,6 @@ int copy_tree(const char *from, const char *to, bool merge) {
}
int copy_directory_fd(int dirfd, const char *to, bool merge) {
-
struct stat st;
assert(dirfd >= 0);
@@ -422,6 +423,21 @@ int copy_directory_fd(int dirfd, const char *to, bool merge) {
return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge);
}
+int copy_directory(const char *from, const char *to, bool merge) {
+ struct stat st;
+
+ assert(from);
+ assert(to);
+
+ if (lstat(from, &st) < 0)
+ return -errno;
+
+ if (!S_ISDIR(st.st_mode))
+ return -ENOTDIR;
+
+ return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, merge);
+}
+
int copy_file_fd(const char *from, int fdt, bool try_reflink) {
_cleanup_close_ int fdf = -1;
int r;
diff --git a/src/basic/copy.h b/src/basic/copy.h
index 3e5eb52506..b5d08ebafe 100644
--- a/src/basic/copy.h
+++ b/src/basic/copy.h
@@ -30,6 +30,7 @@ int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace
int copy_tree(const char *from, const char *to, bool merge);
int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge);
int copy_directory_fd(int dirfd, const char *to, bool merge);
+int copy_directory(const char *from, const char *to, bool merge);
int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink);
int copy_times(int fdf, int fdt);
int copy_xattr(int fdf, int fdt);
diff --git a/src/basic/dirent-util.c b/src/basic/dirent-util.c
index 5fb535cb13..59067121b7 100644
--- a/src/basic/dirent-util.c
+++ b/src/basic/dirent-util.c
@@ -52,12 +52,10 @@ int dirent_ensure_type(DIR *d, struct dirent *de) {
bool dirent_is_file(const struct dirent *de) {
assert(de);
- if (hidden_file(de->d_name))
+ if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN))
return false;
- if (de->d_type != DT_REG &&
- de->d_type != DT_LNK &&
- de->d_type != DT_UNKNOWN)
+ if (hidden_or_backup_file(de->d_name))
return false;
return true;
@@ -66,12 +64,10 @@ bool dirent_is_file(const struct dirent *de) {
bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) {
assert(de);
- if (de->d_type != DT_REG &&
- de->d_type != DT_LNK &&
- de->d_type != DT_UNKNOWN)
+ if (!IN_SET(de->d_type, DT_REG, DT_LNK, DT_UNKNOWN))
return false;
- if (hidden_file_allow_backup(de->d_name))
+ if (de->d_name[0] == '.')
return false;
return endswith(de->d_name, suffix);
diff --git a/src/basic/dirent-util.h b/src/basic/dirent-util.h
index 6bf099b46c..b91d04908f 100644
--- a/src/basic/dirent-util.h
+++ b/src/basic/dirent-util.h
@@ -38,7 +38,7 @@ bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) _pu
on_error; \
} \
break; \
- } else if (hidden_file((de)->d_name)) \
+ } else if (hidden_or_backup_file((de)->d_name)) \
continue; \
else
diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c
index 3d46d708c7..8b466cff15 100644
--- a/src/basic/fd-util.c
+++ b/src/basic/fd-util.c
@@ -231,7 +231,7 @@ int close_all_fds(const int except[], unsigned n_except) {
while ((de = readdir(d))) {
int fd = -1;
- if (hidden_file(de->d_name))
+ if (hidden_or_backup_file(de->d_name))
continue;
if (safe_atoi(de->d_name, &fd) < 0)
@@ -361,8 +361,14 @@ bool fdname_is_valid(const char *s) {
int fd_get_path(int fd, char **ret) {
char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
+ int r;
xsprintf(procfs_path, "/proc/self/fd/%i", fd);
- return readlink_malloc(procfs_path, ret);
+ r = readlink_malloc(procfs_path, ret);
+
+ if (r == -ENOENT) /* If the file doesn't exist the fd is invalid */
+ return -EBADF;
+
+ return r;
}
diff --git a/src/basic/fdset.c b/src/basic/fdset.c
index 06f8ecbdbc..527f27bc67 100644
--- a/src/basic/fdset.c
+++ b/src/basic/fdset.c
@@ -151,7 +151,7 @@ int fdset_new_fill(FDSet **_s) {
while ((de = readdir(d))) {
int fd = -1;
- if (hidden_file(de->d_name))
+ if (hidden_or_backup_file(de->d_name))
continue;
r = safe_atoi(de->d_name, &fd);
diff --git a/src/basic/hashmap.c b/src/basic/hashmap.c
index 85b8d812b3..49a0479592 100644
--- a/src/basic/hashmap.c
+++ b/src/basic/hashmap.c
@@ -1773,20 +1773,18 @@ int set_consume(Set *s, void *value) {
int set_put_strdup(Set *s, const char *p) {
char *c;
- int r;
assert(s);
assert(p);
+ if (set_contains(s, (char*) p))
+ return 0;
+
c = strdup(p);
if (!c)
return -ENOMEM;
- r = set_consume(s, c);
- if (r == -EEXIST)
- return 0;
-
- return r;
+ return set_consume(s, c);
}
int set_put_strdupv(Set *s, char **l) {
diff --git a/src/basic/missing.h b/src/basic/missing.h
index b389e94cf7..22ea8f67cc 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -557,7 +557,7 @@ struct btrfs_ioctl_quota_ctl_args {
#define IFLA_INET6_ADDR_GEN_MODE 8
#define __IFLA_INET6_MAX 9
-#define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1)
+#define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1)
#define IN6_ADDR_GEN_MODE_EUI64 0
#define IN6_ADDR_GEN_MODE_NONE 1
@@ -742,7 +742,7 @@ struct btrfs_ioctl_quota_ctl_args {
#define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
#endif
-#if !HAVE_DECL_IFLA_BR_PRIORITY
+#if !HAVE_DECL_IFLA_BR_VLAN_DEFAULT_PVID
#define IFLA_BR_UNSPEC 0
#define IFLA_BR_FORWARD_DELAY 1
#define IFLA_BR_HELLO_TIME 2
diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h
index c407263e16..7dc579a159 100644
--- a/src/basic/parse-util.h
+++ b/src/basic/parse-util.h
@@ -93,7 +93,7 @@ static inline int safe_atoli(const char *s, long int *ret_u) {
#if SIZE_MAX == UINT_MAX
static inline int safe_atozu(const char *s, size_t *ret_u) {
assert_cc(sizeof(size_t) == sizeof(unsigned));
- return safe_atou(s, ret_u);
+ return safe_atou(s, (unsigned *) ret_u);
}
#else
static inline int safe_atozu(const char *s, size_t *ret_u) {
diff --git a/src/basic/path-util.c b/src/basic/path-util.c
index 25aa355397..b2fa81a294 100644
--- a/src/basic/path-util.c
+++ b/src/basic/path-util.c
@@ -756,37 +756,53 @@ char *file_in_same_dir(const char *path, const char *filename) {
return ret;
}
-bool hidden_file_allow_backup(const char *filename) {
- assert(filename);
-
- return
- filename[0] == '.' ||
- streq(filename, "lost+found") ||
- streq(filename, "aquota.user") ||
- streq(filename, "aquota.group") ||
- endswith(filename, ".rpmnew") ||
- endswith(filename, ".rpmsave") ||
- endswith(filename, ".rpmorig") ||
- endswith(filename, ".dpkg-old") ||
- endswith(filename, ".dpkg-new") ||
- endswith(filename, ".dpkg-tmp") ||
- endswith(filename, ".dpkg-dist") ||
- endswith(filename, ".dpkg-bak") ||
- endswith(filename, ".dpkg-backup") ||
- endswith(filename, ".dpkg-remove") ||
- endswith(filename, ".ucf-new") ||
- endswith(filename, ".ucf-old") ||
- endswith(filename, ".ucf-dist") ||
- endswith(filename, ".swp");
-}
+bool hidden_or_backup_file(const char *filename) {
+ const char *p;
-bool hidden_file(const char *filename) {
assert(filename);
- if (endswith(filename, "~"))
+ if (filename[0] == '.' ||
+ streq(filename, "lost+found") ||
+ streq(filename, "aquota.user") ||
+ streq(filename, "aquota.group") ||
+ endswith(filename, "~"))
return true;
- return hidden_file_allow_backup(filename);
+ p = strrchr(filename, '.');
+ if (!p)
+ return false;
+
+ /* Please, let's not add more entries to the list below. If external projects think it's a good idea to come up
+ * with always new suffixes and that everybody else should just adjust to that, then it really should be on
+ * them. Hence, in future, let's not add any more entries. Instead, let's ask those packages to instead adopt
+ * one of the generic suffixes/prefixes for hidden files or backups, possibly augmented with an additional
+ * string. Specifically: there's now:
+ *
+ * The generic suffixes "~" and ".bak" for backup files
+ * The generic prefix "." for hidden files
+ *
+ * Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old", ".foopkg-dist"
+ * or so registered, let's refuse that and ask them to use ".foopkg.new", ".foopkg.old" or ".foopkg~" instead.
+ */
+
+ return STR_IN_SET(p + 1,
+ "rpmnew",
+ "rpmsave",
+ "rpmorig",
+ "dpkg-old",
+ "dpkg-new",
+ "dpkg-tmp",
+ "dpkg-dist",
+ "dpkg-bak",
+ "dpkg-backup",
+ "dpkg-remove",
+ "ucf-new",
+ "ucf-old",
+ "ucf-dist",
+ "swp",
+ "bak",
+ "old",
+ "new");
}
bool is_device_path(const char *path) {
diff --git a/src/basic/path-util.h b/src/basic/path-util.h
index 34d5cd1570..a27c13fcc3 100644
--- a/src/basic/path-util.h
+++ b/src/basic/path-util.h
@@ -122,7 +122,6 @@ bool path_is_safe(const char *p) _pure_;
char *file_in_same_dir(const char *path, const char *filename);
-bool hidden_file_allow_backup(const char *filename);
-bool hidden_file(const char *filename) _pure_;
+bool hidden_or_backup_file(const char *filename) _pure_;
bool is_device_path(const char *path);
diff --git a/src/basic/process-util.c b/src/basic/process-util.c
index f2cea01979..4a7367cc92 100644
--- a/src/basic/process-util.c
+++ b/src/basic/process-util.c
@@ -528,14 +528,20 @@ int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_cod
return -EPROTO;
}
-void sigkill_wait(pid_t *pid) {
+void sigkill_wait(pid_t pid) {
+ assert(pid > 1);
+
+ if (kill(pid, SIGKILL) > 0)
+ (void) wait_for_terminate(pid, NULL);
+}
+
+void sigkill_waitp(pid_t *pid) {
if (!pid)
return;
if (*pid <= 1)
return;
- if (kill(*pid, SIGKILL) > 0)
- (void) wait_for_terminate(*pid, NULL);
+ sigkill_wait(*pid);
}
int kill_and_sigcont(pid_t pid, int sig) {
diff --git a/src/basic/process-util.h b/src/basic/process-util.h
index ffd4bcb0ff..9f75088796 100644
--- a/src/basic/process-util.h
+++ b/src/basic/process-util.h
@@ -58,8 +58,8 @@ int get_process_ppid(pid_t pid, pid_t *ppid);
int wait_for_terminate(pid_t pid, siginfo_t *status);
int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code);
-void sigkill_wait(pid_t *pid);
-#define _cleanup_sigkill_wait_ _cleanup_(sigkill_wait)
+void sigkill_wait(pid_t pid);
+void sigkill_waitp(pid_t *pid);
int kill_and_sigcont(pid_t pid, int sig);
diff --git a/src/basic/rlimit-util.c b/src/basic/rlimit-util.c
index 7540b43215..ee063720ed 100644
--- a/src/basic/rlimit-util.c
+++ b/src/basic/rlimit-util.c
@@ -153,6 +153,56 @@ static int rlimit_parse_usec(const char *val, rlim_t *ret) {
return 0;
}
+static int rlimit_parse_nice(const char *val, rlim_t *ret) {
+ uint64_t rl;
+ int r;
+
+ /* So, Linux is weird. The range for RLIMIT_NICE is 40..1, mapping to the nice levels -20..19. However, the
+ * RLIMIT_NICE limit defaults to 0 by the kernel, i.e. a value that maps to nice level 20, which of course is
+ * bogus and does not exist. In order to permit parsing the RLIMIT_NICE of 0 here we hence implement a slight
+ * asymmetry: when parsing as positive nice level we permit 0..19. When parsing as negative nice level, we
+ * permit -20..0. But when parsing as raw resource limit value then we also allow the special value 0.
+ *
+ * Yeah, Linux is quality engineering sometimes... */
+
+ if (val[0] == '+') {
+
+ /* Prefixed with "+": Parse as positive user-friendly nice value */
+ r = safe_atou64(val + 1, &rl);
+ if (r < 0)
+ return r;
+
+ if (rl >= PRIO_MAX)
+ return -ERANGE;
+
+ rl = 20 - rl;
+
+ } else if (val[0] == '-') {
+
+ /* Prefixed with "-": Parse as negative user-friendly nice value */
+ r = safe_atou64(val + 1, &rl);
+ if (r < 0)
+ return r;
+
+ if (rl > (uint64_t) (-PRIO_MIN))
+ return -ERANGE;
+
+ rl = 20 + rl;
+ } else {
+
+ /* Not prefixed: parse as raw resource limit value */
+ r = safe_atou64(val, &rl);
+ if (r < 0)
+ return r;
+
+ if (rl > (uint64_t) (20 - PRIO_MIN))
+ return -ERANGE;
+ }
+
+ *ret = (rlim_t) rl;
+ return 0;
+}
+
static int (*const rlimit_parse_table[_RLIMIT_MAX])(const char *val, rlim_t *ret) = {
[RLIMIT_CPU] = rlimit_parse_sec,
[RLIMIT_FSIZE] = rlimit_parse_size,
@@ -167,7 +217,7 @@ static int (*const rlimit_parse_table[_RLIMIT_MAX])(const char *val, rlim_t *ret
[RLIMIT_LOCKS] = rlimit_parse_u64,
[RLIMIT_SIGPENDING] = rlimit_parse_u64,
[RLIMIT_MSGQUEUE] = rlimit_parse_size,
- [RLIMIT_NICE] = rlimit_parse_u64,
+ [RLIMIT_NICE] = rlimit_parse_nice,
[RLIMIT_RTPRIO] = rlimit_parse_u64,
[RLIMIT_RTTIME] = rlimit_parse_usec,
};
diff --git a/src/basic/util.c b/src/basic/util.c
index b70c50047f..756c663be4 100644
--- a/src/basic/util.c
+++ b/src/basic/util.c
@@ -522,7 +522,7 @@ int on_ac_power(void) {
if (!de)
break;
- if (hidden_file(de->d_name))
+ if (hidden_or_backup_file(de->d_name))
continue;
device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
diff --git a/src/core/automount.c b/src/core/automount.c
index 7c55d7bc49..1239a0efc6 100644
--- a/src/core/automount.c
+++ b/src/core/automount.c
@@ -1050,9 +1050,6 @@ const UnitVTable automount_vtable = {
"Automount\0"
"Install\0",
- .no_alias = true,
- .no_instances = true,
-
.init = automount_init,
.load = automount_load,
.done = automount_done,
diff --git a/src/core/busname.c b/src/core/busname.c
index f4f433340c..e7b7b5c012 100644
--- a/src/core/busname.c
+++ b/src/core/busname.c
@@ -1028,9 +1028,6 @@ const UnitVTable busname_vtable = {
"Install\0",
.private_section = "BusName",
- .no_alias = true,
- .no_instances = true,
-
.init = busname_init,
.done = busname_done,
.load = busname_load,
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index d2eb388f7c..d45f511489 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -889,7 +889,7 @@ static int method_reset_failed(sd_bus_message *message, void *userdata, sd_bus_e
return sd_bus_reply_method_return(message, NULL);
}
-static int list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_error *error, char **states) {
+static int list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_error *error, char **states, char **patterns) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
Manager *m = userdata;
const char *k;
@@ -929,6 +929,10 @@ static int list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_e
!strv_contains(states, unit_sub_state_to_string(u)))
continue;
+ if (!strv_isempty(patterns) &&
+ !strv_fnmatch_or_empty(patterns, u->id, FNM_NOESCAPE))
+ continue;
+
unit_path = unit_dbus_path(u);
if (!unit_path)
return -ENOMEM;
@@ -963,7 +967,7 @@ static int list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_e
}
static int method_list_units(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- return list_units_filtered(message, userdata, error, NULL);
+ return list_units_filtered(message, userdata, error, NULL, NULL);
}
static int method_list_units_filtered(sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -974,7 +978,23 @@ static int method_list_units_filtered(sd_bus_message *message, void *userdata, s
if (r < 0)
return r;
- return list_units_filtered(message, userdata, error, states);
+ return list_units_filtered(message, userdata, error, states, NULL);
+}
+
+static int method_list_units_by_patterns(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_strv_free_ char **states = NULL;
+ _cleanup_strv_free_ char **patterns = NULL;
+ int r;
+
+ r = sd_bus_message_read_strv(message, &states);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_strv(message, &patterns);
+ if (r < 0)
+ return r;
+
+ return list_units_filtered(message, userdata, error, states, patterns);
}
static int method_list_jobs(sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -1465,7 +1485,7 @@ static int method_set_exit_code(sd_bus_message *message, void *userdata, sd_bus_
return sd_bus_reply_method_return(message, NULL);
}
-static int method_list_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int list_unit_files_by_patterns(sd_bus_message *message, void *userdata, sd_bus_error *error, char **states, char **patterns) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
Manager *m = userdata;
UnitFileList *item;
@@ -1490,7 +1510,7 @@ static int method_list_unit_files(sd_bus_message *message, void *userdata, sd_bu
if (!h)
return -ENOMEM;
- r = unit_file_get_list(m->unit_file_scope, NULL, h);
+ r = unit_file_get_list(m->unit_file_scope, NULL, h, states, patterns);
if (r < 0)
goto fail;
@@ -1518,6 +1538,26 @@ fail:
return r;
}
+static int method_list_unit_files(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ return list_unit_files_by_patterns(message, userdata, error, NULL, NULL);
+}
+
+static int method_list_unit_files_by_patterns(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_strv_free_ char **states = NULL;
+ _cleanup_strv_free_ char **patterns = NULL;
+ int r;
+
+ r = sd_bus_message_read_strv(message, &states);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_read_strv(message, &patterns);
+ if (r < 0)
+ return r;
+
+ return list_unit_files_by_patterns(message, userdata, error, states, patterns);
+}
+
static int method_get_unit_file_state(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
const char *name;
@@ -2010,7 +2050,8 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_PROPERTY("DefaultTimeoutStartUSec", "t", bus_property_get_usec, offsetof(Manager, default_timeout_start_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultTimeoutStopUSec", "t", bus_property_get_usec, offsetof(Manager, default_timeout_stop_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultRestartUSec", "t", bus_property_get_usec, offsetof(Manager, default_restart_usec), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("DefaultStartLimitInterval", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultStartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("DefaultStartLimitInterval", "t", bus_property_get_usec, offsetof(Manager, default_start_limit_interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), /* obsolete alias name */
SD_BUS_PROPERTY("DefaultStartLimitBurst", "u", bus_property_get_unsigned, offsetof(Manager, default_start_limit_burst), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultCPUAccounting", "b", bus_property_get_bool, offsetof(Manager, default_cpu_accounting), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("DefaultBlockIOAccounting", "b", bus_property_get_bool, offsetof(Manager, default_blockio_accounting), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -2073,6 +2114,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_METHOD("ResetFailed", NULL, NULL, method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ListUnits", NULL, "a(ssssssouso)", method_list_units, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ListUnitsFiltered", "as", "a(ssssssouso)", method_list_units_filtered, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ListUnitsByPatterns", "asas", "a(ssssssouso)", method_list_units_by_patterns, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ListJobs", NULL, "a(usssoo)", method_list_jobs, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Subscribe", NULL, NULL, method_subscribe, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Unsubscribe", NULL, NULL, method_unsubscribe, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -2091,6 +2133,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
SD_BUS_METHOD("UnsetEnvironment", "as", NULL, method_unset_environment, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("UnsetAndSetEnvironment", "asas", NULL, method_unset_and_set_environment, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ListUnitFiles", NULL, "a(ss)", method_list_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("ListUnitFilesByPatterns", "asas", "a(ss)", method_list_unit_files_by_patterns, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("GetUnitFileState", "s", "s", method_get_unit_file_state, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("EnableUnitFiles", "asbb", "ba(sss)", method_enable_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("DisableUnitFiles", "asb", "a(sss)", method_disable_unit_files, SD_BUS_VTABLE_UNPRIVILEGED),
diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c
index d33e494f6b..bb09a515f8 100644
--- a/src/core/dbus-socket.c
+++ b/src/core/dbus-socket.c
@@ -149,6 +149,8 @@ const sd_bus_vtable bus_socket_vtable[] = {
SD_BUS_PROPERTY("NAccepted", "u", bus_property_get_unsigned, offsetof(Socket, n_accepted), 0),
SD_BUS_PROPERTY("FileDescriptorName", "s", property_get_fdname, 0, 0),
SD_BUS_PROPERTY("SocketProtocol", "i", bus_property_get_int, offsetof(Socket, socket_protocol), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("TriggerLimitIntervalSec", "t", bus_property_get_usec, offsetof(Socket, trigger_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("TriggerLimitBurst", "u", bus_property_get_unsigned, offsetof(Socket, trigger_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Socket, exec_command[SOCKET_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPost", offsetof(Socket, exec_command[SOCKET_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPre", offsetof(Socket, exec_command[SOCKET_EXEC_STOP_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index abe30413c3..e912fe2192 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -704,7 +704,8 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("Asserts", "a(sbbsi)", property_get_conditions, offsetof(Unit, asserts), 0),
SD_BUS_PROPERTY("LoadError", "(ss)", property_get_load_error, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Transient", "b", bus_property_get_bool, offsetof(Unit, transient), SD_BUS_VTABLE_PROPERTY_CONST),
- SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("StartLimitIntervalSec", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("StartLimitInterval", "t", bus_property_get_usec, offsetof(Unit, start_limit.interval), SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), /* obsolete alias name */
SD_BUS_PROPERTY("StartLimitBurst", "u", bus_property_get_unsigned, offsetof(Unit, start_limit.burst), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("StartLimitAction", "s", property_get_failure_action, offsetof(Unit, start_limit_action), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RebootArgument", "s", NULL, offsetof(Unit, reboot_arg), SD_BUS_VTABLE_PROPERTY_CONST),
diff --git a/src/core/device.c b/src/core/device.c
index d01bec53d8..16e56efcc3 100644
--- a/src/core/device.c
+++ b/src/core/device.c
@@ -841,8 +841,6 @@ const UnitVTable device_vtable = {
"Device\0"
"Install\0",
- .no_instances = true,
-
.init = device_init,
.done = device_done,
.load = unit_load_fragment_and_dropin_optional,
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 5568b4696f..928b913c7b 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -164,6 +164,8 @@ Unit.IgnoreOnSnapshot, config_parse_warn_compat, DISABLED_LE
Unit.JobTimeoutSec, config_parse_sec_fix_0, 0, offsetof(Unit, job_timeout)
Unit.JobTimeoutAction, config_parse_failure_action, 0, offsetof(Unit, job_timeout_action)
Unit.JobTimeoutRebootArgument, config_parse_string, 0, offsetof(Unit, job_timeout_reboot_arg)
+Unit.StartLimitIntervalSec, config_parse_sec, 0, offsetof(Unit, start_limit.interval)
+m4_dnl The following is a legacy alias name for compatibility
Unit.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_limit.interval)
Unit.StartLimitBurst, config_parse_unsigned, 0, offsetof(Unit, start_limit.burst)
Unit.StartLimitAction, config_parse_failure_action, 0, offsetof(Unit, start_limit_action)
@@ -220,6 +222,7 @@ Service.TimeoutStartSec, config_parse_service_timeout, 0,
Service.TimeoutStopSec, config_parse_service_timeout, 0, 0
Service.RuntimeMaxSec, config_parse_sec, 0, offsetof(Service, runtime_max_usec)
Service.WatchdogSec, config_parse_sec, 0, offsetof(Service, watchdog_usec)
+m4_dnl The following three only exist for compatibility, they moved into Unit, see above
Service.StartLimitInterval, config_parse_sec, 0, offsetof(Unit, start_limit.interval)
Service.StartLimitBurst, config_parse_unsigned, 0, offsetof(Unit, start_limit.burst)
Service.StartLimitAction, config_parse_failure_action, 0, offsetof(Unit, start_limit_action)
@@ -297,6 +300,8 @@ Socket.RemoveOnStop, config_parse_bool, 0,
Socket.Symlinks, config_parse_unit_path_strv_printf, 0, offsetof(Socket, symlinks)
Socket.FileDescriptorName, config_parse_fdname, 0, 0
Socket.Service, config_parse_socket_service, 0, 0
+Socket.TriggerLimitIntervalSec, config_parse_sec, 0, offsetof(Socket, trigger_limit.interval)
+Socket.TriggerLimitBurst, config_parse_unsigned, 0, offsetof(Socket, trigger_limit.burst)
m4_ifdef(`HAVE_SMACK',
`Socket.SmackLabel, config_parse_string, 0, offsetof(Socket, smack)
Socket.SmackLabelIPIn, config_parse_string, 0, offsetof(Socket, smack_ip_in)
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index c4566f7709..1a8c03904c 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -3427,10 +3427,10 @@ int config_parse_protect_system(
#define FOLLOW_MAX 8
static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
+ char *id = NULL;
unsigned c = 0;
int fd, r;
FILE *f;
- char *id = NULL;
assert(filename);
assert(*filename);
@@ -3452,7 +3452,6 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
* the names of this unit, but only if it is a valid
* unit name. */
name = basename(*filename);
-
if (unit_name_is_valid(name, UNIT_NAME_ANY)) {
id = set_get(names, name);
@@ -3492,6 +3491,7 @@ static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
*_f = f;
*_final = id;
+
return 0;
}
@@ -3552,13 +3552,13 @@ static int merge_by_names(Unit **u, Set *names, const char *id) {
}
static int load_from_path(Unit *u, const char *path) {
- int r;
_cleanup_set_free_free_ Set *symlink_names = NULL;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *filename = NULL;
char *id = NULL;
Unit *merged;
struct stat st;
+ int r;
assert(u);
assert(path);
@@ -3597,18 +3597,14 @@ static int load_from_path(Unit *u, const char *path) {
r = -ENOENT;
else
r = open_follow(&filename, &f, symlink_names, &id);
+ if (r >= 0)
+ break;
+ filename = mfree(filename);
+ if (r != -ENOENT)
+ return r;
- if (r < 0) {
- filename = mfree(filename);
- if (r != -ENOENT)
- return r;
-
- /* Empty the symlink names for the next run */
- set_clear_free(symlink_names);
- continue;
- }
-
- break;
+ /* Empty the symlink names for the next run */
+ set_clear_free(symlink_names);
}
}
@@ -3616,6 +3612,11 @@ static int load_from_path(Unit *u, const char *path) {
/* Hmm, no suitable file found? */
return 0;
+ if (!unit_type_may_alias(u->type) && set_size(symlink_names) > 1) {
+ log_unit_warning(u, "Unit type of %s does not support alias names, refusing loading via symlink.", u->id);
+ return -ELOOP;
+ }
+
merged = u;
r = merge_by_names(&merged, symlink_names, id);
if (r < 0)
diff --git a/src/core/main.c b/src/core/main.c
index 75c5ff81f2..ed4d42c8cc 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -289,6 +289,7 @@ static int parse_crash_chvt(const char *value) {
}
static int set_machine_id(const char *m) {
+ assert(m);
if (sd_id128_from_string(m, &arg_machine_id) < 0)
return -EINVAL;
@@ -669,7 +670,8 @@ static int parse_config_file(void) {
{ "Manager", "DefaultTimeoutStartSec", config_parse_sec, 0, &arg_default_timeout_start_usec },
{ "Manager", "DefaultTimeoutStopSec", config_parse_sec, 0, &arg_default_timeout_stop_usec },
{ "Manager", "DefaultRestartSec", config_parse_sec, 0, &arg_default_restart_usec },
- { "Manager", "DefaultStartLimitInterval", config_parse_sec, 0, &arg_default_start_limit_interval },
+ { "Manager", "DefaultStartLimitInterval", config_parse_sec, 0, &arg_default_start_limit_interval }, /* obsolete alias */
+ { "Manager", "DefaultStartLimitIntervalSec",config_parse_sec, 0, &arg_default_start_limit_interval },
{ "Manager", "DefaultStartLimitBurst", config_parse_unsigned, 0, &arg_default_start_limit_burst },
{ "Manager", "DefaultEnvironment", config_parse_environ, 0, &arg_default_environment },
{ "Manager", "DefaultLimitCPU", config_parse_limit, RLIMIT_CPU, arg_default_rlimit },
diff --git a/src/core/mount.c b/src/core/mount.c
index 188fb0aa40..c8a898e4dc 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -86,6 +86,15 @@ static bool mount_is_network(const MountParameters *p) {
return mount_needs_network(p->options, p->fstype);
}
+static bool mount_is_loop(const MountParameters *p) {
+ assert(p);
+
+ if (fstab_test_option(p->options, "loop\0"))
+ return true;
+
+ return false;
+}
+
static bool mount_is_bind(const MountParameters *p) {
assert(p);
@@ -269,12 +278,12 @@ static int mount_add_mount_links(Mount *m) {
}
/* Adds in links to other mount points that might be needed
- * for the source path (if this is a bind mount) to be
+ * for the source path (if this is a bind mount or a loop mount) to be
* available. */
pm = get_mount_parameters_fragment(m);
if (pm && pm->what &&
path_is_absolute(pm->what) &&
- !mount_is_network(pm)) {
+ (mount_is_bind(pm) || mount_is_loop(pm) || !mount_is_network(pm))) {
r = unit_require_mounts_for(UNIT(m), pm->what);
if (r < 0)
@@ -1830,9 +1839,6 @@ const UnitVTable mount_vtable = {
"Install\0",
.private_section = "Mount",
- .no_alias = true,
- .no_instances = true,
-
.init = mount_init,
.load = mount_load,
.done = mount_done,
diff --git a/src/core/org.freedesktop.systemd1.conf b/src/core/org.freedesktop.systemd1.conf
index b732501364..6c504a5e69 100644
--- a/src/core/org.freedesktop.systemd1.conf
+++ b/src/core/org.freedesktop.systemd1.conf
@@ -70,10 +70,18 @@
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
+ send_member="ListUnitsByPatterns"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
send_member="ListUnitFiles"/>
<allow send_destination="org.freedesktop.systemd1"
send_interface="org.freedesktop.systemd1.Manager"
+ send_member="ListUnitFilesByPatterns"/>
+
+ <allow send_destination="org.freedesktop.systemd1"
+ send_interface="org.freedesktop.systemd1.Manager"
send_member="GetUnitFileState"/>
<allow send_destination="org.freedesktop.systemd1"
diff --git a/src/core/scope.c b/src/core/scope.c
index 7078d1f7e9..238f63a729 100644
--- a/src/core/scope.c
+++ b/src/core/scope.c
@@ -569,8 +569,6 @@ const UnitVTable scope_vtable = {
"Install\0",
.private_section = "Scope",
- .no_alias = true,
- .no_instances = true,
.can_transient = true,
.init = scope_init,
diff --git a/src/core/service.c b/src/core/service.c
index b46dd8bcdd..f7a3fcf2b9 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -180,20 +180,17 @@ static int service_set_main_pid(Service *s, pid_t pid) {
return 0;
}
-static void service_close_socket_fd(Service *s) {
+void service_close_socket_fd(Service *s) {
assert(s);
- s->socket_fd = asynchronous_close(s->socket_fd);
-}
-
-static void service_connection_unref(Service *s) {
- assert(s);
+ /* Undo the effect of service_set_socket_fd(). */
- if (!UNIT_ISSET(s->accept_socket))
- return;
+ s->socket_fd = asynchronous_close(s->socket_fd);
- socket_connection_unref(SOCKET(UNIT_DEREF(s->accept_socket)));
- unit_ref_unset(&s->accept_socket);
+ if (UNIT_ISSET(s->accept_socket)) {
+ socket_connection_unref(SOCKET(UNIT_DEREF(s->accept_socket)));
+ unit_ref_unset(&s->accept_socket);
+ }
}
static void service_stop_watchdog(Service *s) {
@@ -321,7 +318,6 @@ static void service_done(Unit *u) {
s->bus_name_owner = mfree(s->bus_name_owner);
service_close_socket_fd(s);
- service_connection_unref(s);
unit_ref_unset(&s->accept_socket);
@@ -910,10 +906,8 @@ static void service_set_state(Service *s, ServiceState state) {
SERVICE_RUNNING, SERVICE_RELOAD,
SERVICE_STOP, SERVICE_STOP_SIGABRT, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL) &&
- !(state == SERVICE_DEAD && UNIT(s)->job)) {
+ !(state == SERVICE_DEAD && UNIT(s)->job))
service_close_socket_fd(s);
- service_connection_unref(s);
- }
if (!IN_SET(state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD))
service_stop_watchdog(s);
@@ -3139,9 +3133,8 @@ int service_set_socket_fd(Service *s, int fd, Socket *sock, bool selinux_context
assert(s);
assert(fd >= 0);
- /* This is called by the socket code when instantiating a new
- * service for a stream socket and the socket needs to be
- * configured. */
+ /* This is called by the socket code when instantiating a new service for a stream socket and the socket needs
+ * to be configured. We take ownership of the passed fd on success. */
if (UNIT(s)->load_state != UNIT_LOADED)
return -EINVAL;
@@ -3169,12 +3162,15 @@ int service_set_socket_fd(Service *s, int fd, Socket *sock, bool selinux_context
return r;
}
+ r = unit_add_two_dependencies(UNIT(sock), UNIT_BEFORE, UNIT_TRIGGERS, UNIT(s), false);
+ if (r < 0)
+ return r;
+
s->socket_fd = fd;
s->socket_fd_selinux_context_net = selinux_context_net;
unit_ref_set(&s->accept_socket, UNIT(sock));
-
- return unit_add_two_dependencies(UNIT(sock), UNIT_BEFORE, UNIT_TRIGGERS, UNIT(s), false);
+ return 0;
}
static void service_reset_failed(Unit *u) {
diff --git a/src/core/service.h b/src/core/service.h
index cd9e41646e..c7f1e81bdb 100644
--- a/src/core/service.h
+++ b/src/core/service.h
@@ -198,6 +198,7 @@ struct Service {
extern const UnitVTable service_vtable;
int service_set_socket_fd(Service *s, int fd, struct Socket *socket, bool selinux_context_net);
+void service_close_socket_fd(Service *s);
const char* service_restart_to_string(ServiceRestart i) _const_;
ServiceRestart service_restart_from_string(const char *s) _pure_;
diff --git a/src/core/slice.c b/src/core/slice.c
index 63a77c9bca..c7700b8857 100644
--- a/src/core/slice.c
+++ b/src/core/slice.c
@@ -309,8 +309,6 @@ const UnitVTable slice_vtable = {
"Install\0",
.private_section = "Slice",
- .no_alias = true,
- .no_instances = true,
.can_transient = true,
.init = slice_init,
diff --git a/src/core/socket.c b/src/core/socket.c
index a9fff9c259..7eeed068bd 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -99,6 +99,8 @@ static void socket_init(Unit *u) {
s->exec_context.std_error = u->manager->default_std_error;
s->control_command_id = _SOCKET_EXEC_COMMAND_INVALID;
+
+ RATELIMIT_INIT(s->trigger_limit, 5*USEC_PER_SEC, 2500);
}
static void socket_unwatch_control_pid(Socket *s) {
@@ -227,7 +229,6 @@ int socket_instantiate_service(Socket *s) {
if (r < 0)
return r;
- u->no_gc = true;
unit_ref_set(&s->service, u);
return unit_add_two_dependencies(UNIT(s), UNIT_BEFORE, UNIT_TRIGGERS, u, false);
@@ -792,47 +793,45 @@ static void socket_close_fds(Socket *s) {
assert(s);
LIST_FOREACH(port, p, s->ports) {
+ bool was_open;
- p->event_source = sd_event_source_unref(p->event_source);
-
- if (p->fd < 0)
- continue;
+ was_open = p->fd >= 0;
+ p->event_source = sd_event_source_unref(p->event_source);
p->fd = safe_close(p->fd);
socket_cleanup_fd_list(p);
- /* One little note: we should normally not delete any
- * sockets in the file system here! After all some
- * other process we spawned might still have a
- * reference of this fd and wants to continue to use
- * it. Therefore we delete sockets in the file system
- * before we create a new one, not after we stopped
- * using one! */
+ /* One little note: we should normally not delete any sockets in the file system here! After all some
+ * other process we spawned might still have a reference of this fd and wants to continue to use
+ * it. Therefore we normally delete sockets in the file system before we create a new one, not after we
+ * stopped using one! That all said, if the user explicitly requested this, we'll delete them here
+ * anyway, but only then. */
- if (s->remove_on_stop) {
- switch (p->type) {
+ if (!was_open || !s->remove_on_stop)
+ continue;
- case SOCKET_FIFO:
- unlink(p->path);
- break;
+ switch (p->type) {
- case SOCKET_MQUEUE:
- mq_unlink(p->path);
- break;
+ case SOCKET_FIFO:
+ (void) unlink(p->path);
+ break;
- case SOCKET_SOCKET:
- socket_address_unlink(&p->address);
- break;
+ case SOCKET_MQUEUE:
+ (void) mq_unlink(p->path);
+ break;
- default:
- break;
- }
+ case SOCKET_SOCKET:
+ (void) socket_address_unlink(&p->address);
+ break;
+
+ default:
+ break;
}
}
if (s->remove_on_stop)
STRV_FOREACH(i, s->symlinks)
- unlink(*i);
+ (void) unlink(*i);
}
static void socket_apply_socket_options(Socket *s, int fd) {
@@ -1887,6 +1886,9 @@ static void socket_enter_running(Socket *s, int cfd) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
+ /* Note that this call takes possession of the connection fd passed. It either has to assign it somewhere or
+ * close it. */
+
assert(s);
/* We don't take connections anymore if we are supposed to
@@ -1896,7 +1898,7 @@ static void socket_enter_running(Socket *s, int cfd) {
log_unit_debug(UNIT(s), "Suppressing connection request since unit stop is scheduled.");
if (cfd >= 0)
- safe_close(cfd);
+ cfd = safe_close(cfd);
else {
/* Flush all sockets by closing and reopening them */
socket_close_fds(s);
@@ -1918,6 +1920,13 @@ static void socket_enter_running(Socket *s, int cfd) {
return;
}
+ if (!ratelimit_test(&s->trigger_limit)) {
+ safe_close(cfd);
+ log_unit_warning(UNIT(s), "Trigger limit hit, refusing further activation.");
+ socket_enter_stop_pre(s, SOCKET_FAILURE_TRIGGER_LIMIT_HIT);
+ return;
+ }
+
if (cfd < 0) {
Iterator i;
Unit *other;
@@ -1949,7 +1958,7 @@ static void socket_enter_running(Socket *s, int cfd) {
Service *service;
if (s->n_connections >= s->max_connections) {
- log_unit_warning(UNIT(s), "Too many incoming connections (%u)", s->n_connections);
+ log_unit_warning(UNIT(s), "Too many incoming connections (%u), refusing connection attempt.", s->n_connections);
safe_close(cfd);
return;
}
@@ -1965,6 +1974,7 @@ static void socket_enter_running(Socket *s, int cfd) {
/* ENOTCONN is legitimate if TCP RST was received.
* This connection is over, but the socket unit lives on. */
+ log_unit_debug(UNIT(s), "Got ENOTCONN on incoming socket, assuming aborted connection attempt, ignoring.");
safe_close(cfd);
return;
}
@@ -1983,22 +1993,24 @@ static void socket_enter_running(Socket *s, int cfd) {
service = SERVICE(UNIT_DEREF(s->service));
unit_ref_unset(&s->service);
- s->n_accepted++;
-
- UNIT(service)->no_gc = false;
+ s->n_accepted++;
unit_choose_id(UNIT(service), name);
r = service_set_socket_fd(service, cfd, s, s->selinux_context_from_net);
if (r < 0)
goto fail;
- cfd = -1;
+ cfd = -1; /* We passed ownership of the fd to the service now. Forget it here. */
s->n_connections++;
r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, &error, NULL);
- if (r < 0)
+ if (r < 0) {
+ /* We failed to activate the new service, but it still exists. Let's make sure the service
+ * closes and forgets the connection fd again, immediately. */
+ service_close_socket_fd(service);
goto fail;
+ }
/* Notify clients about changed counters */
unit_add_to_dbus_queue(UNIT(s));
@@ -2806,6 +2818,7 @@ static const char* const socket_result_table[_SOCKET_RESULT_MAX] = {
[SOCKET_FAILURE_EXIT_CODE] = "exit-code",
[SOCKET_FAILURE_SIGNAL] = "signal",
[SOCKET_FAILURE_CORE_DUMP] = "core-dump",
+ [SOCKET_FAILURE_TRIGGER_LIMIT_HIT] = "trigger-limit-hit",
[SOCKET_FAILURE_SERVICE_START_LIMIT_HIT] = "service-start-limit-hit"
};
diff --git a/src/core/socket.h b/src/core/socket.h
index b537b026a7..2a4b1bb674 100644
--- a/src/core/socket.h
+++ b/src/core/socket.h
@@ -52,6 +52,7 @@ typedef enum SocketResult {
SOCKET_FAILURE_EXIT_CODE,
SOCKET_FAILURE_SIGNAL,
SOCKET_FAILURE_CORE_DUMP,
+ SOCKET_FAILURE_TRIGGER_LIMIT_HIT,
SOCKET_FAILURE_SERVICE_START_LIMIT_HIT,
_SOCKET_RESULT_MAX,
_SOCKET_RESULT_INVALID = -1
@@ -156,6 +157,8 @@ struct Socket {
bool reset_cpu_usage:1;
char *fdname;
+
+ RateLimit trigger_limit;
};
/* Called from the service code when collecting fds */
diff --git a/src/core/swap.c b/src/core/swap.c
index d8802470d2..c018648d87 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -1465,9 +1465,6 @@ const UnitVTable swap_vtable = {
"Install\0",
.private_section = "Swap",
- .no_alias = true,
- .no_instances = true,
-
.init = swap_init,
.load = swap_load,
.done = swap_done,
diff --git a/src/core/system.conf b/src/core/system.conf
index e2ded27333..eacd7ee282 100644
--- a/src/core/system.conf
+++ b/src/core/system.conf
@@ -34,7 +34,7 @@
#DefaultTimeoutStartSec=90s
#DefaultTimeoutStopSec=90s
#DefaultRestartSec=100ms
-#DefaultStartLimitInterval=10s
+#DefaultStartLimitIntervalSec=10s
#DefaultStartLimitBurst=5
#DefaultEnvironment=
#DefaultCPUAccounting=no
diff --git a/src/core/unit.c b/src/core/unit.c
index cb79c7c6b1..4ace6b075b 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -193,7 +193,7 @@ int unit_add_name(Unit *u, const char *text) {
if (r < 0)
return r;
- if (i && unit_vtable[t]->no_instances)
+ if (i && !unit_type_may_template(t))
return -EINVAL;
/* Ensure that this unit is either instanced or not instanced,
@@ -202,7 +202,7 @@ int unit_add_name(Unit *u, const char *text) {
if (u->type != _UNIT_TYPE_INVALID && !u->instance != !i)
return -EINVAL;
- if (unit_vtable[t]->no_alias && !set_isempty(u->names))
+ if (!unit_type_may_alias(t) && !set_isempty(u->names))
return -EEXIST;
if (hashmap_size(u->manager->units) >= MANAGER_MAX_NAMES)
@@ -720,6 +720,9 @@ int unit_merge(Unit *u, Unit *other) {
if (!u->instance != !other->instance)
return -EINVAL;
+ if (!unit_type_may_alias(u->type)) /* Merging only applies to unit names that support aliases */
+ return -EEXIST;
+
if (other->load_state != UNIT_STUB &&
other->load_state != UNIT_NOT_FOUND)
return -EEXIST;
@@ -776,9 +779,9 @@ int unit_merge(Unit *u, Unit *other) {
}
int unit_merge_by_name(Unit *u, const char *name) {
+ _cleanup_free_ char *s = NULL;
Unit *other;
int r;
- _cleanup_free_ char *s = NULL;
assert(u);
assert(name);
@@ -1497,11 +1500,6 @@ int unit_start(Unit *u) {
if (UNIT_IS_ACTIVE_OR_RELOADING(state))
return -EALREADY;
- /* Make sure we don't enter a busy loop of some kind. */
- r = unit_start_limit_test(u);
- if (r < 0)
- return r;
-
/* Units that aren't loaded cannot be started */
if (u->load_state != UNIT_LOADED)
return -EINVAL;
@@ -1543,6 +1541,11 @@ int unit_start(Unit *u) {
if (!UNIT_VTABLE(u)->start)
return -EBADR;
+ /* Make sure we don't enter a busy loop of some kind. */
+ r = unit_start_limit_test(u);
+ if (r < 0)
+ return r;
+
/* We don't suppress calls to ->start() here when we are
* already starting, to allow this request to be used as a
* "hurry up" call, for example when the unit is in some "auto
@@ -3222,6 +3225,10 @@ void unit_ref_unset(UnitRef *ref) {
if (!ref->unit)
return;
+ /* We are about to drop a reference to the unit, make sure the garbage collection has a look at it as it might
+ * be unreferenced now. */
+ unit_add_to_gc_queue(ref->unit);
+
LIST_REMOVE(refs, ref->unit->refs, ref);
ref->unit = NULL;
}
diff --git a/src/core/unit.h b/src/core/unit.h
index 5909652976..be62e88421 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -416,12 +416,6 @@ struct UnitVTable {
/* The strings to print in status messages */
UnitStatusMessageFormats status_message_formats;
- /* Can units of this type have multiple names? */
- bool no_alias:1;
-
- /* Instances make no sense for this type */
- bool no_instances:1;
-
/* True if transient units of this type are OK */
bool can_transient:1;
};
diff --git a/src/core/user.conf b/src/core/user.conf
index 87c8164378..b427f1ef6d 100644
--- a/src/core/user.conf
+++ b/src/core/user.conf
@@ -23,7 +23,7 @@
#DefaultTimeoutStartSec=90s
#DefaultTimeoutStopSec=90s
#DefaultRestartSec=100ms
-#DefaultStartLimitInterval=10s
+#DefaultStartLimitIntervalSec=10s
#DefaultStartLimitBurst=5
#DefaultEnvironment=
#DefaultLimitCPU=
diff --git a/src/import/pull-common.c b/src/import/pull-common.c
index d301d4d79e..dc4e4667a9 100644
--- a/src/import/pull-common.c
+++ b/src/import/pull-common.c
@@ -330,7 +330,7 @@ int pull_verify(PullJob *main_job,
_cleanup_close_ int sig_file = -1;
const char *p, *line;
char sig_file_path[] = "/tmp/sigXXXXXX", gpg_home[] = "/tmp/gpghomeXXXXXX";
- _cleanup_sigkill_wait_ pid_t pid = 0;
+ _cleanup_(sigkill_waitp) pid_t pid = 0;
bool gpg_home_created = false;
int r;
diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c
index 35f4abab1d..ec50333c2c 100644
--- a/src/journal/journal-file.c
+++ b/src/journal/journal-file.c
@@ -439,6 +439,39 @@ static int journal_file_init_header(JournalFile *f, JournalFile *template) {
return 0;
}
+static int fsync_directory_of_file(int fd) {
+ _cleanup_free_ char *path = NULL, *dn = NULL;
+ _cleanup_close_ int dfd = -1;
+ struct stat st;
+ int r;
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (!S_ISREG(st.st_mode))
+ return -EBADFD;
+
+ r = fd_get_path(fd, &path);
+ if (r < 0)
+ return r;
+
+ if (!path_is_absolute(path))
+ return -EINVAL;
+
+ dn = dirname_malloc(path);
+ if (!dn)
+ return -ENOMEM;
+
+ dfd = open(dn, O_RDONLY|O_CLOEXEC|O_DIRECTORY);
+ if (dfd < 0)
+ return -errno;
+
+ if (fsync(dfd) < 0)
+ return -errno;
+
+ return 0;
+}
+
static int journal_file_refresh_header(JournalFile *f) {
sd_id128_t boot_id;
int r;
@@ -464,6 +497,9 @@ static int journal_file_refresh_header(JournalFile *f) {
/* Sync the online state to disk */
(void) fsync(f->fd);
+ /* We likely just created a new file, also sync the directory this file is located in. */
+ (void) fsync_directory_of_file(f->fd);
+
return r;
}
@@ -3177,6 +3213,9 @@ int journal_file_rotate(JournalFile **f, bool compress, bool seal, Set *deferred
if (r < 0 && errno != ENOENT)
return -errno;
+ /* Sync the rename to disk */
+ (void) fsync_directory_of_file(old_file->fd);
+
/* Set as archive so offlining commits w/state=STATE_ARCHIVED.
* Previously we would set old_file->header->state to STATE_ARCHIVED directly here,
* but journal_file_set_offline() short-circuits when state != STATE_ONLINE, which
@@ -3254,7 +3293,7 @@ int journal_file_open_reliably(
/* btrfs doesn't cope well with our write pattern and
* fragments heavily. Let's defrag all files we rotate */
- (void) chattr_path(p, false, FS_NOCOW_FL);
+ (void) chattr_path(p, 0, FS_NOCOW_FL);
(void) btrfs_defrag(p);
log_warning_errno(r, "File %s corrupted or uncleanly shut down, renaming and replacing.", fname);
diff --git a/src/journal/journald-gperf.gperf b/src/journal/journald-gperf.gperf
index c154610c54..7fecd7a964 100644
--- a/src/journal/journald-gperf.gperf
+++ b/src/journal/journald-gperf.gperf
@@ -19,7 +19,9 @@ Journal.Storage, config_parse_storage, 0, offsetof(Server, storage
Journal.Compress, config_parse_bool, 0, offsetof(Server, compress)
Journal.Seal, config_parse_bool, 0, offsetof(Server, seal)
Journal.SyncIntervalSec, config_parse_sec, 0, offsetof(Server, sync_interval_usec)
+# The following is a legacy name for compatibility
Journal.RateLimitInterval, config_parse_sec, 0, offsetof(Server, rate_limit_interval)
+Journal.RateLimitIntervalSec,config_parse_sec, 0, offsetof(Server, rate_limit_interval)
Journal.RateLimitBurst, config_parse_unsigned, 0, offsetof(Server, rate_limit_burst)
Journal.SystemMaxUse, config_parse_iec_uint64, 0, offsetof(Server, system_metrics.max_use)
Journal.SystemMaxFileSize, config_parse_iec_uint64, 0, offsetof(Server, system_metrics.max_size)
diff --git a/src/journal/journald.conf b/src/journal/journald.conf
index 7beb96c671..2541b949be 100644
--- a/src/journal/journald.conf
+++ b/src/journal/journald.conf
@@ -17,7 +17,7 @@
#Seal=yes
#SplitMode=uid
#SyncIntervalSec=5m
-#RateLimitInterval=30s
+#RateLimitIntervalSec=30s
#RateLimitBurst=1000
#SystemMaxUse=
#SystemKeepFree=
diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c
index f56798674c..86d8dee867 100644
--- a/src/libsystemd/sd-netlink/netlink-message.c
+++ b/src/libsystemd/sd-netlink/netlink-message.c
@@ -207,11 +207,11 @@ static int add_rtattr(sd_netlink_message *m, unsigned short type, const void *da
* and gives us too little data (so don't do that)
*/
padding = mempcpy(RTA_DATA(rta), data, data_length);
- else {
+
+ else
/* if no data was passed, make sure we still initialize the padding
note that we can have data_length > 0 (used by some containers) */
padding = RTA_DATA(rta);
- }
/* make sure also the padding at the end of the message is initialized */
padding_length = (uint8_t*)m->hdr + message_length - (uint8_t*)padding;
@@ -343,7 +343,7 @@ int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, c
assert_return(m, -EINVAL);
assert_return(!m->sealed, -EPERM);
- r = add_rtattr(m, type, &data, len);
+ r = add_rtattr(m, type, data, len);
if (r < 0)
return r;
diff --git a/src/libsystemd/sd-netlink/rtnl-message.c b/src/libsystemd/sd-netlink/rtnl-message.c
index f251536a89..09240c7b2a 100644
--- a/src/libsystemd/sd-netlink/rtnl-message.c
+++ b/src/libsystemd/sd-netlink/rtnl-message.c
@@ -111,6 +111,20 @@ int sd_rtnl_message_route_get_flags(sd_netlink_message *m, unsigned *flags) {
return 0;
}
+int sd_rtnl_message_route_set_table(sd_netlink_message *m, unsigned char table) {
+ struct rtmsg *rtm;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->hdr, -EINVAL);
+ assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL);
+
+ rtm = NLMSG_DATA(m->hdr);
+
+ rtm->rtm_table = table;
+
+ return 0;
+}
+
int sd_rtnl_message_route_get_family(sd_netlink_message *m, int *family) {
struct rtmsg *rtm;
@@ -126,6 +140,20 @@ int sd_rtnl_message_route_get_family(sd_netlink_message *m, int *family) {
return 0;
}
+int sd_rtnl_message_route_set_family(sd_netlink_message *m, int family) {
+ struct rtmsg *rtm;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->hdr, -EINVAL);
+ assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL);
+
+ rtm = NLMSG_DATA(m->hdr);
+
+ rtm->rtm_family = family;
+
+ return 0;
+}
+
int sd_rtnl_message_route_get_protocol(sd_netlink_message *m, unsigned char *protocol) {
struct rtmsg *rtm;
diff --git a/src/locale/language-fallback-map b/src/locale/language-fallback-map
index 6aadda091a..d0b02a6b98 100644
--- a/src/locale/language-fallback-map
+++ b/src/locale/language-fallback-map
@@ -3,6 +3,10 @@ en_AU en_AU:en_GB
en_IE en_IE:en_GB
en_NZ en_NZ:en_GB
en_ZA en_ZA:en_GB
+fr_BE fr_BE:fr_FR
+fr_CA fr_CA:fr_FR
+fr_CH fr_CH:fr_FR
+fr_LU fr_LU:fr_FR
it_CH it_CH:it_IT
mai_IN mai:hi
nds_DE nds:de
diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c
index b764bc43a0..0eed9b81bb 100644
--- a/src/machine/image-dbus.c
+++ b/src/machine/image-dbus.c
@@ -20,9 +20,11 @@
#include "alloc-util.h"
#include "bus-label.h"
#include "bus-util.h"
+#include "fd-util.h"
#include "image-dbus.h"
#include "io-util.h"
#include "machine-image.h"
+#include "process-util.h"
#include "strv.h"
#include "user-util.h"
@@ -33,13 +35,18 @@ int bus_image_method_remove(
void *userdata,
sd_bus_error *error) {
+ _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
Image *image = userdata;
Manager *m = image->userdata;
+ pid_t child;
int r;
assert(message);
assert(image);
+ if (m->n_operations >= OPERATIONS_MAX)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
+
r = bus_verify_polkit_async(
message,
CAP_SYS_ADMIN,
@@ -54,11 +61,35 @@ int bus_image_method_remove(
if (r == 0)
return 1; /* Will call us back */
- r = image_remove(image);
- if (r < 0)
+ if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
+
+ child = fork();
+ if (child < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
+ if (child == 0) {
+ errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
+
+ r = image_remove(image);
+ if (r < 0) {
+ (void) write(errno_pipe_fd[1], &r, sizeof(r));
+ _exit(EXIT_FAILURE);
+ }
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
+
+ r = operation_new(m, NULL, child, message, errno_pipe_fd[0]);
+ if (r < 0) {
+ (void) sigkill_wait(child);
return r;
+ }
- return sd_bus_reply_method_return(message, NULL);
+ errno_pipe_fd[0] = -1;
+
+ return 1;
}
int bus_image_method_rename(
@@ -107,13 +138,19 @@ int bus_image_method_clone(
void *userdata,
sd_bus_error *error) {
+ _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
Image *image = userdata;
Manager *m = image->userdata;
const char *new_name;
int r, read_only;
+ pid_t child;
assert(message);
assert(image);
+ assert(m);
+
+ if (m->n_operations >= OPERATIONS_MAX)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations.");
r = sd_bus_message_read(message, "sb", &new_name, &read_only);
if (r < 0)
@@ -136,13 +173,35 @@ int bus_image_method_clone(
if (r == 0)
return 1; /* Will call us back */
- r = image_clone(image, new_name, read_only);
- if (r == -EOPNOTSUPP)
- return sd_bus_reply_method_errnof(message, r, "Image cloning is currently only supported on btrfs file systems.");
- if (r < 0)
+ if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
+
+ child = fork();
+ if (child < 0)
+ return sd_bus_error_set_errnof(error, errno, "Failed to fork(): %m");
+ if (child == 0) {
+ errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
+
+ r = image_clone(image, new_name, read_only);
+ if (r < 0) {
+ (void) write(errno_pipe_fd[1], &r, sizeof(r));
+ _exit(EXIT_FAILURE);
+ }
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
+
+ r = operation_new(m, NULL, child, message, errno_pipe_fd[0]);
+ if (r < 0) {
+ (void) sigkill_wait(child);
return r;
+ }
- return sd_bus_reply_method_return(message, NULL);
+ errno_pipe_fd[0] = -1;
+
+ return 1;
}
int bus_image_method_mark_read_only(
diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c
index 5121bfdd18..7b9aa66d63 100644
--- a/src/machine/machine-dbus.c
+++ b/src/machine/machine-dbus.c
@@ -1085,52 +1085,11 @@ finish:
return r;
}
-static int machine_operation_done(sd_event_source *s, const siginfo_t *si, void *userdata) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- MachineOperation *o = userdata;
- int r;
-
- assert(o);
- assert(si);
-
- o->pid = 0;
-
- if (si->si_code != CLD_EXITED) {
- r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
- goto fail;
- }
-
- if (si->si_status != EXIT_SUCCESS) {
- if (read(o->errno_fd, &r, sizeof(r)) == sizeof(r))
- r = sd_bus_error_set_errnof(&error, r, "%m");
- else
- r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child failed.");
-
- goto fail;
- }
-
- r = sd_bus_reply_method_return(o->message, NULL);
- if (r < 0)
- log_error_errno(r, "Failed to reply to message: %m");
-
- machine_operation_unref(o);
- return 0;
-
-fail:
- r = sd_bus_reply_method_error(o->message, &error);
- if (r < 0)
- log_error_errno(r, "Failed to reply to message: %m");
-
- machine_operation_unref(o);
- return 0;
-}
-
int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error) {
const char *src, *dest, *host_path, *container_path, *host_basename, *host_dirname, *container_basename, *container_dirname;
_cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 };
_cleanup_close_ int hostfd = -1;
Machine *m = userdata;
- MachineOperation *o;
bool copy_from;
pid_t child;
char *t;
@@ -1139,7 +1098,7 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro
assert(message);
assert(m);
- if (m->n_operations >= MACHINE_OPERATIONS_MAX)
+ if (m->manager->n_operations >= OPERATIONS_MAX)
return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing copies.");
if (m->class != MACHINE_CONTAINER)
@@ -1249,27 +1208,14 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro
errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]);
- /* Copying might take a while, hence install a watch the
- * child, and return */
+ /* Copying might take a while, hence install a watch on the child, and return */
- o = new0(MachineOperation, 1);
- if (!o)
- return log_oom();
-
- o->pid = child;
- o->message = sd_bus_message_ref(message);
- o->errno_fd = errno_pipe_fd[0];
- errno_pipe_fd[0] = -1;
-
- r = sd_event_add_child(m->manager->event, &o->event_source, child, WEXITED, machine_operation_done, o);
+ r = operation_new(m->manager, m, child, message, errno_pipe_fd[0]);
if (r < 0) {
- machine_operation_unref(o);
- return log_oom();
+ (void) sigkill_wait(child);
+ return r;
}
-
- LIST_PREPEND(operations, m->operations, o);
- m->n_operations++;
- o->machine = m;
+ errno_pipe_fd[0] = -1;
return 1;
}
diff --git a/src/machine/machine.c b/src/machine/machine.c
index 7d4270a8ff..c1fae57084 100644
--- a/src/machine/machine.c
+++ b/src/machine/machine.c
@@ -89,7 +89,7 @@ void machine_free(Machine *m) {
assert(m);
while (m->operations)
- machine_operation_unref(m->operations);
+ operation_free(m->operations);
if (m->in_gc_queue)
LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m);
@@ -596,28 +596,6 @@ int machine_open_terminal(Machine *m, const char *path, int mode) {
}
}
-MachineOperation *machine_operation_unref(MachineOperation *o) {
- if (!o)
- return NULL;
-
- sd_event_source_unref(o->event_source);
-
- safe_close(o->errno_fd);
-
- if (o->pid > 1)
- (void) kill(o->pid, SIGKILL);
-
- sd_bus_message_unref(o->message);
-
- if (o->machine) {
- LIST_REMOVE(operations, o->machine->operations, o);
- o->machine->n_operations--;
- }
-
- free(o);
- return NULL;
-}
-
void machine_release_unit(Machine *m) {
assert(m);
diff --git a/src/machine/machine.h b/src/machine/machine.h
index 1d8cc5911a..e5d75361a9 100644
--- a/src/machine/machine.h
+++ b/src/machine/machine.h
@@ -20,11 +20,11 @@
***/
typedef struct Machine Machine;
-typedef struct MachineOperation MachineOperation;
typedef enum KillWho KillWho;
#include "list.h"
#include "machined.h"
+#include "operation.h"
typedef enum MachineState {
MACHINE_OPENING, /* Machine is being registered */
@@ -49,17 +49,6 @@ enum KillWho {
_KILL_WHO_INVALID = -1
};
-#define MACHINE_OPERATIONS_MAX 64
-
-struct MachineOperation {
- Machine *machine;
- pid_t pid;
- sd_bus_message *message;
- int errno_fd;
- sd_event_source *event_source;
- LIST_FIELDS(MachineOperation, operations);
-};
-
struct Machine {
Manager *manager;
@@ -88,10 +77,9 @@ struct Machine {
int *netif;
unsigned n_netif;
- LIST_FIELDS(Machine, gc_queue);
+ LIST_HEAD(Operation, operations);
- MachineOperation *operations;
- unsigned n_operations;
+ LIST_FIELDS(Machine, gc_queue);
};
Machine* machine_new(Manager *manager, MachineClass class, const char *name);
@@ -109,8 +97,6 @@ void machine_release_unit(Machine *m);
MachineState machine_get_state(Machine *u);
-MachineOperation *machine_operation_unref(MachineOperation *o);
-
const char* machine_class_to_string(MachineClass t) _const_;
MachineClass machine_class_from_string(const char *s) _pure_;
diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c
index b03198bbf1..1165ab5afa 100644
--- a/src/machine/machinectl.c
+++ b/src/machine/machinectl.c
@@ -1076,6 +1076,7 @@ static int terminate_machine(int argc, char *argv[], void *userdata) {
static int copy_files(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_free_ char *abs_host_path = NULL;
char *dest, *host_path, *container_path;
sd_bus *bus = userdata;
@@ -1099,19 +1100,28 @@ static int copy_files(int argc, char *argv[], void *userdata) {
host_path = abs_host_path;
}
- r = sd_bus_call_method(
+ r = sd_bus_message_new_method_call(
bus,
+ &m,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
- copy_from ? "CopyFromMachine" : "CopyToMachine",
- &error,
- NULL,
+ copy_from ? "CopyFromMachine" : "CopyToMachine");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(
+ m,
"sss",
argv[1],
copy_from ? container_path : host_path,
copy_from ? host_path : container_path);
if (r < 0)
+ return bus_log_create_error(r);
+
+ /* This is a slow operation, hence turn off any method call timeouts */
+ r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
+ if (r < 0)
return log_error_errno(r, "Failed to copy: %s", bus_error_message(&error, r));
return 0;
@@ -1393,7 +1403,6 @@ static int shell_machine(int argc, char *argv[], void *userdata) {
}
static int remove_image(int argc, char *argv[], void *userdata) {
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus = userdata;
int r, i;
@@ -1402,19 +1411,27 @@ static int remove_image(int argc, char *argv[], void *userdata) {
polkit_agent_open_if_enabled();
for (i = 1; i < argc; i++) {
- r = sd_bus_call_method(
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+
+ r = sd_bus_message_new_method_call(
bus,
+ &m,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
- "RemoveImage",
- &error,
- NULL,
- "s", argv[i]);
- if (r < 0) {
- log_error("Could not remove image: %s", bus_error_message(&error, -r));
- return r;
- }
+ "RemoveImage");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "s", argv[i]);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ /* This is a slow operation, hence turn off any method call timeouts */
+ r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Could not remove image: %s", bus_error_message(&error, r));
}
return 0;
@@ -1446,24 +1463,30 @@ static int rename_image(int argc, char *argv[], void *userdata) {
static int clone_image(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
sd_bus *bus = userdata;
int r;
polkit_agent_open_if_enabled();
- r = sd_bus_call_method(
+ r = sd_bus_message_new_method_call(
bus,
+ &m,
"org.freedesktop.machine1",
"/org/freedesktop/machine1",
"org.freedesktop.machine1.Manager",
- "CloneImage",
- &error,
- NULL,
- "ssb", argv[1], argv[2], arg_read_only);
- if (r < 0) {
- log_error("Could not clone image: %s", bus_error_message(&error, -r));
- return r;
- }
+ "CloneImage");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "ssb", argv[1], argv[2], arg_read_only);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ /* This is a slow operation, hence turn off any method call timeouts */
+ r = sd_bus_call(bus, m, USEC_INFINITY, &error, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Could not clone image: %s", bus_error_message(&error, r));
return 0;
}
@@ -2516,14 +2539,9 @@ static int parse_argv(int argc, char *argv[]) {
assert(argv);
for (;;) {
- const char *option_string;
-
- if (reorder)
- option_string = "hp:als:H:M:qn:o:";
- else
- option_string = "+hp:als:H:M:qn:o:";
+ const char * const option_string = "+hp:als:H:M:qn:o:";
- c = getopt_long(argc, argv, option_string, options, NULL);
+ c = getopt_long(argc, argv, option_string + reorder, options, NULL);
if (c < 0) {
/* We generally are fine with the fact that getopt_long() reorders the command line, and looks
* for switches after the main verb. However, for "shell" we really don't want that, since we
diff --git a/src/machine/machined.c b/src/machine/machined.c
index f2c1966a6b..f7ceb5e603 100644
--- a/src/machine/machined.c
+++ b/src/machine/machined.c
@@ -70,6 +70,11 @@ void manager_free(Manager *m) {
assert(m);
+ while (m->operations)
+ operation_free(m->operations);
+
+ assert(m->n_operations == 0);
+
while ((machine = hashmap_first(m->machines)))
machine_free(machine);
@@ -336,6 +341,9 @@ int manager_startup(Manager *m) {
static bool check_idle(void *userdata) {
Manager *m = userdata;
+ if (m->operations)
+ return false;
+
manager_gc(m, true);
return hashmap_isempty(m->machines);
diff --git a/src/machine/machined.h b/src/machine/machined.h
index e7d7dfdceb..7b9b148044 100644
--- a/src/machine/machined.h
+++ b/src/machine/machined.h
@@ -32,6 +32,7 @@ typedef struct Manager Manager;
#include "image-dbus.h"
#include "machine-dbus.h"
#include "machine.h"
+#include "operation.h"
struct Manager {
sd_event *event;
@@ -49,6 +50,9 @@ struct Manager {
LIST_HEAD(Machine, machine_gc_queue);
Machine *host_machine;
+
+ LIST_HEAD(Operation, operations);
+ unsigned n_operations;
};
Manager *manager_new(void);
diff --git a/src/machine/operation.c b/src/machine/operation.c
new file mode 100644
index 0000000000..e6ddc41a55
--- /dev/null
+++ b/src/machine/operation.c
@@ -0,0 +1,131 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "operation.h"
+#include "process-util.h"
+
+static int operation_done(sd_event_source *s, const siginfo_t *si, void *userdata) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ Operation *o = userdata;
+ int r;
+
+ assert(o);
+ assert(si);
+
+ log_debug("Operating " PID_FMT " is now complete with with code=%s status=%i",
+ o->pid,
+ sigchld_code_to_string(si->si_code), si->si_status);
+
+ o->pid = 0;
+
+ if (si->si_code != CLD_EXITED) {
+ r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child died abnormally.");
+ goto fail;
+ }
+
+ if (si->si_status != EXIT_SUCCESS) {
+ if (read(o->errno_fd, &r, sizeof(r)) == sizeof(r))
+ r = sd_bus_error_set_errnof(&error, r, "%m");
+ else
+ r = sd_bus_error_setf(&error, SD_BUS_ERROR_FAILED, "Child failed.");
+
+ goto fail;
+ }
+
+ r = sd_bus_reply_method_return(o->message, NULL);
+ if (r < 0)
+ log_error_errno(r, "Failed to reply to message: %m");
+
+ operation_free(o);
+ return 0;
+
+fail:
+ r = sd_bus_reply_method_error(o->message, &error);
+ if (r < 0)
+ log_error_errno(r, "Failed to reply to message: %m");
+
+ operation_free(o);
+ return 0;
+}
+
+int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_message *message, int errno_fd) {
+ Operation *o;
+ int r;
+
+ assert(manager);
+ assert(child > 1);
+ assert(message);
+ assert(errno_fd >= 0);
+
+ o = new0(Operation, 1);
+ if (!o)
+ return -ENOMEM;
+
+ r = sd_event_add_child(manager->event, &o->event_source, child, WEXITED, operation_done, o);
+ if (r < 0) {
+ free(o);
+ return r;
+ }
+
+ o->pid = child;
+ o->message = sd_bus_message_ref(message);
+ o->errno_fd = errno_fd;
+
+ LIST_PREPEND(operations, manager->operations, o);
+ manager->n_operations++;
+ o->manager = manager;
+
+ if (machine) {
+ LIST_PREPEND(operations_by_machine, machine->operations, o);
+ o->machine = machine;
+ }
+
+ log_debug("Started new operation " PID_FMT ".", child);
+
+ /* At this point we took ownership of both the child and the errno file descriptor! */
+
+ return 0;
+}
+
+Operation *operation_free(Operation *o) {
+ if (!o)
+ return NULL;
+
+ sd_event_source_unref(o->event_source);
+
+ safe_close(o->errno_fd);
+
+ if (o->pid > 1)
+ (void) sigkill_wait(o->pid);
+
+ sd_bus_message_unref(o->message);
+
+ if (o->manager) {
+ LIST_REMOVE(operations, o->manager->operations, o);
+ o->manager->n_operations--;
+ }
+
+ if (o->machine)
+ LIST_REMOVE(operations_by_machine, o->machine->operations, o);
+
+ free(o);
+ return NULL;
+}
diff --git a/src/machine/operation.h b/src/machine/operation.h
new file mode 100644
index 0000000000..7ca47bc3af
--- /dev/null
+++ b/src/machine/operation.h
@@ -0,0 +1,47 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+
+#include "sd-bus.h"
+#include "sd-event.h"
+
+#include "list.h"
+
+typedef struct Operation Operation;
+
+#include "machined.h"
+
+#define OPERATIONS_MAX 64
+
+struct Operation {
+ Manager *manager;
+ Machine *machine;
+ pid_t pid;
+ sd_bus_message *message;
+ int errno_fd;
+ sd_event_source *event_source;
+ LIST_FIELDS(Operation, operations);
+ LIST_FIELDS(Operation, operations_by_machine);
+};
+
+int operation_new(Manager *manager, Machine *machine, pid_t child, sd_bus_message *message, int errno_fd);
+Operation *operation_free(Operation *o);
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index 429319da6b..8b52a1f742 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -774,6 +774,54 @@ int config_parse_label(const char *unit,
return 0;
}
+int config_parse_lifetime(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ Network *network = userdata;
+ _cleanup_address_free_ Address *n = NULL;
+ unsigned k;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = address_new_static(network, section_line, &n);
+ if (r < 0)
+ return r;
+
+ if (STR_IN_SET(rvalue, "forever", "infinity")) {
+ n->cinfo.ifa_prefered = CACHE_INFO_INFINITY_LIFE_TIME;
+ n = NULL;
+
+ return 0;
+ }
+
+ r = safe_atou(rvalue, &k);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse PreferredLifetime, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ if (k != 0)
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid PreferredLifetime value, ignoring: %d", k);
+ else {
+ n->cinfo.ifa_prefered = k;
+ n = NULL;
+ }
+
+ return 0;
+}
+
bool address_is_ready(const Address *a) {
assert(a);
diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h
index 338f6eb9a2..3c81978fb1 100644
--- a/src/network/networkd-address.h
+++ b/src/network/networkd-address.h
@@ -74,3 +74,4 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Address*, address_free);
int config_parse_address(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_broadcast(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_label(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_lifetime(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/network/networkd-conf.c b/src/network/networkd-conf.c
index 73a8d16b58..70f0121d6d 100644
--- a/src/network/networkd-conf.c
+++ b/src/network/networkd-conf.c
@@ -31,7 +31,7 @@ int manager_parse_config_file(Manager *m) {
return config_parse_many(PKGSYSCONFDIR "/networkd.conf",
CONF_PATHS_NULSTR("systemd/networkd.conf.d"),
- "DUID\0",
+ "DHCP\0",
config_item_perf_lookup, networkd_gperf_lookup,
false, m);
}
@@ -57,7 +57,8 @@ int config_parse_duid_rawdata(
const char *rvalue,
void *data,
void *userdata) {
- int r, n1, n2, byte;
+
+ int r;
char *cbyte;
const char *pduid = rvalue;
Manager *m = userdata;
@@ -72,71 +73,78 @@ int config_parse_duid_rawdata(
assert(rvalue);
assert(userdata);
- duidtype = (ltype == DUID_CONFIG_SOURCE_GLOBAL) ? m->duid_type
- : n->duid_type;
+ duidtype = (ltype == DUID_CONFIG_SOURCE_GLOBAL) ? m->duid_type : n->duid_type;
if (duidtype == _DUID_TYPE_INVALID)
duidtype = DUID_TYPE_RAW;
switch (duidtype) {
+
case DUID_TYPE_LLT:
/* RawData contains DUID-LLT link-layer address (offset 6) */
duid_start_offset = 6;
break;
+
case DUID_TYPE_EN:
/* RawData contains DUID-EN identifier (offset 4) */
duid_start_offset = 4;
break;
+
case DUID_TYPE_LL:
/* RawData contains DUID-LL link-layer address (offset 2) */
duid_start_offset = 2;
break;
+
case DUID_TYPE_UUID:
/* RawData specifies UUID (offset 0) - fall thru */
+
case DUID_TYPE_RAW:
/* First two bytes of RawData is DUID Type - fall thru */
+
default:
break;
}
if (duidtype != DUID_TYPE_RAW)
- dhcp_duid_type = (uint16_t)duidtype;
+ dhcp_duid_type = (uint16_t) duidtype;
/* RawData contains DUID in format " NN:NN:NN... " */
for (;;) {
+ int n1, n2;
+ uint32_t byte;
+
r = extract_first_word(&pduid, &cbyte, ":", 0);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r,
- "Failed to read DUID, ignoring assignment: %s.", rvalue);
- goto exit;
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to read DUID, ignoring assignment: %s.", rvalue);
+ return 0;
}
if (r == 0)
break;
- if ((duid_start_offset + dhcp_duid_len) >= MAX_DUID_LEN) {
- log_syntax(unit, LOG_ERR, filename, line, 0,
- "Max DUID length exceeded, ignoring assignment: %s.", rvalue);
- goto exit;
+ if (duid_start_offset + dhcp_duid_len >= MAX_DUID_LEN) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Max DUID length exceeded, ignoring assignment: %s.", rvalue);
+ return 0;
}
len = strlen(cbyte);
- if ((len == 0) || (len > 2)) {
- log_syntax(unit, LOG_ERR, filename, line, 0,
- "Invalid length - DUID byte: %s, ignoring assignment: %s.", cbyte, rvalue);
- goto exit;
+ if (len != 1 && len != 2) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid length - DUID byte: %s, ignoring assignment: %s.", cbyte, rvalue);
+ return 0;
}
- n2 = 0;
n1 = unhexchar(cbyte[0]);
if (len == 2)
n2 = unhexchar(cbyte[1]);
- if ((n1 < 0) || (n2 < 0)) {
- log_syntax(unit, LOG_ERR, filename, line, 0,
- "Invalid DUID byte: %s. Ignoring assignment: %s.", cbyte, rvalue);
- goto exit;
+ else
+ n2 = 0;
+
+ if (n1 < 0 || n2 < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid DUID byte: %s. Ignoring assignment: %s.", cbyte, rvalue);
+ return 0;
}
- byte = (n1 << (4 * (len-1))) | n2;
+
+ byte = ((uint8_t) n1 << (4 * (len-1))) | (uint8_t) n2;
/* If DUID_TYPE_RAW, first two bytes hold DHCP DUID type code */
- if ((duidtype == DUID_TYPE_RAW) && (count < 2)) {
+ if (duidtype == DUID_TYPE_RAW && count < 2) {
dhcp_duid_type |= (byte << (8 * (1 - count)));
count++;
continue;
@@ -159,6 +167,5 @@ int config_parse_duid_rawdata(
memcpy(&n->dhcp_duid[duid_start_offset], dhcp_duid, dhcp_duid_len);
}
-exit:
return 0;
}
diff --git a/src/network/networkd-gperf.gperf b/src/network/networkd-gperf.gperf
index 0625fb335b..afc71b4cb8 100644
--- a/src/network/networkd-gperf.gperf
+++ b/src/network/networkd-gperf.gperf
@@ -14,5 +14,5 @@ struct ConfigPerfItem;
%struct-type
%includes
%%
-DUID.Type, config_parse_duid_type, 0, offsetof(Manager, duid_type)
-DUID.RawData, config_parse_duid_rawdata, DUID_CONFIG_SOURCE_GLOBAL, offsetof(Manager, dhcp_duid)
+DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Manager, duid_type)
+DHCP.DUIDRawData, config_parse_duid_rawdata, DUID_CONFIG_SOURCE_GLOBAL, offsetof(Manager, dhcp_duid)
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 5101d553d5..5fc513bfda 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -576,8 +576,6 @@ static void link_set_state(Link *link, LinkState state) {
link->state = state;
link_send_changed(link, "AdministrativeState", NULL);
-
- return;
}
static void link_enter_unmanaged(Link *link) {
@@ -1458,7 +1456,7 @@ static int link_acquire_ipv6_conf(Link *link) {
return 0;
}
-static int link_acquire_conf(Link *link) {
+static int link_acquire_ipv4_conf(Link *link) {
int r;
assert(link);
@@ -1486,6 +1484,24 @@ static int link_acquire_conf(Link *link) {
return log_link_warning_errno(link, r, "Could not acquire DHCPv4 lease: %m");
}
+ return 0;
+}
+
+static int link_acquire_conf(Link *link) {
+ int r;
+
+ assert(link);
+
+ r = link_acquire_ipv4_conf(link);
+ if (r < 0)
+ return r;
+
+ if (in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) == 0) {
+ r = link_acquire_ipv6_conf(link);
+ if (r < 0)
+ return r;
+ }
+
if (link_lldp_tx_enabled(link)) {
r = link_lldp_tx_start(link);
if (r < 0)
@@ -2351,12 +2367,6 @@ static int link_configure(Link *link) {
r = link_acquire_conf(link);
if (r < 0)
return r;
-
- if (in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) == 0) {
- r = link_acquire_ipv6_conf(link);
- if (r < 0)
- return r;
- }
}
return link_enter_join_netdev(link);
@@ -2739,6 +2749,10 @@ static int link_carrier_gained(Link *link) {
link_enter_failed(link);
return r;
}
+
+ r = link_enter_set_addresses(link);
+ if (r < 0)
+ return r;
}
r = link_handle_bound_by_list(link);
diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf
index 1da99cd5bc..550b5e5240 100644
--- a/src/network/networkd-network-gperf.gperf
+++ b/src/network/networkd-network-gperf.gperf
@@ -28,8 +28,6 @@ Match.Architecture, config_parse_net_condition,
Link.MACAddress, config_parse_hwaddr, 0, offsetof(Network, mac)
Link.MTUBytes, config_parse_iec_size, 0, offsetof(Network, mtu)
Link.IAID, config_parse_iaid, 0, offsetof(Network, iaid)
-DUID.Type, config_parse_duid_type, 0, offsetof(Network, duid_type)
-DUID.RawData, config_parse_duid_rawdata, DUID_CONFIG_SOURCE_NETWORK, offsetof(Network, dhcp_duid)
Network.Description, config_parse_string, 0, offsetof(Network, description)
Network.Bridge, config_parse_netdev, 0, offsetof(Network, bridge)
Network.Bond, config_parse_netdev, 0, offsetof(Network, bond)
@@ -67,12 +65,14 @@ Address.Address, config_parse_address,
Address.Peer, config_parse_address, 0, 0
Address.Broadcast, config_parse_broadcast, 0, 0
Address.Label, config_parse_label, 0, 0
+Address.PreferredLifetime, config_parse_lifetime, 0, 0
Route.Gateway, config_parse_gateway, 0, 0
Route.Destination, config_parse_destination, 0, 0
Route.Source, config_parse_destination, 0, 0
Route.Metric, config_parse_route_priority, 0, 0
Route.Scope, config_parse_route_scope, 0, 0
Route.PreferredSource, config_parse_preferred_src, 0, 0
+Route.Table, config_parse_route_table, 0, 0
DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
DHCP.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns)
DHCP.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_use_ntp)
@@ -85,6 +85,8 @@ DHCP.Hostname, config_parse_hostname,
DHCP.RequestBroadcast, config_parse_bool, 0, offsetof(Network, dhcp_broadcast)
DHCP.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical)
DHCP.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier)
+DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Network, duid_type)
+DHCP.DUIDRawData, config_parse_duid_rawdata, DUID_CONFIG_SOURCE_NETWORK, offsetof(Network, dhcp_duid)
DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric)
DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone)
DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec)
diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c
index 07f8fb028f..2ebcdfa744 100644
--- a/src/network/networkd-network.c
+++ b/src/network/networkd-network.c
@@ -137,7 +137,6 @@ static int network_load_one(Manager *manager, const char *filename) {
r = config_parse(NULL, filename, file,
"Match\0"
"Link\0"
- "DUID\0"
"Network\0"
"Address\0"
"Route\0"
diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c
index bda2707e6d..01094b20bd 100644
--- a/src/network/networkd-route.c
+++ b/src/network/networkd-route.c
@@ -451,6 +451,10 @@ int route_configure(Route *route, Link *link,
r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->gw.in6);
if (r < 0)
return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m");
+
+ r = sd_rtnl_message_route_set_family(req, route->family);
+ if (r < 0)
+ return log_error_errno(r, "Could not set route family: %m");
}
if (route->dst_prefixlen) {
@@ -494,7 +498,26 @@ int route_configure(Route *route, Link *link,
r = sd_rtnl_message_route_set_flags(req, route->flags);
if (r < 0)
- return log_error_errno(r, "Colud not set flags: %m");
+ return log_error_errno(r, "Could not set flags: %m");
+
+ if (route->table != RT_TABLE_DEFAULT) {
+
+ if (route->table < 256) {
+ r = sd_rtnl_message_route_set_table(req, route->table);
+ if (r < 0)
+ return log_error_errno(r, "Could not set route table: %m");
+ } else {
+
+ r = sd_rtnl_message_route_set_table(req, RT_TABLE_UNSPEC);
+ if (r < 0)
+ return log_error_errno(r, "Could not set route table: %m");
+
+ /* Table attribute to allow allow more than 256. */
+ r = sd_netlink_message_append_data(req, RTA_TABLE, &route->table, sizeof(route->table));
+ if (r < 0)
+ return log_error_errno(r, "Could not append RTA_TABLE attribute: %m");
+ }
+ }
r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority);
if (r < 0)
@@ -777,3 +800,42 @@ int config_parse_route_scope(const char *unit,
return 0;
}
+
+int config_parse_route_table(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ _cleanup_route_free_ Route *n = NULL;
+ Network *network = userdata;
+ uint32_t k;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = route_new_static(network, section_line, &n);
+ if (r < 0)
+ return r;
+
+ r = safe_atou32(rvalue, &k);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Could not parse route table number \"%s\", ignoring assignment: %m", rvalue);
+ return 0;
+ }
+
+ n->table = k;
+
+ n = NULL;
+
+ return 0;
+}
diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h
index a4a4bf2653..3ddeea96b7 100644
--- a/src/network/networkd-route.h
+++ b/src/network/networkd-route.h
@@ -37,7 +37,7 @@ struct Route {
unsigned char protocol; /* RTPROT_* */
unsigned char tos;
uint32_t priority; /* note that ip(8) calls this 'metric' */
- unsigned char table;
+ uint32_t table;
unsigned char pref;
unsigned flags;
@@ -74,3 +74,4 @@ int config_parse_preferred_src(const char *unit, const char *filename, unsigned
int config_parse_destination(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_route_priority(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_route_scope(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_route_table(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 8c90aa8015..3fc6cc955c 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -1464,7 +1464,7 @@ static int setup_journal(const char *directory) {
if (sd_id128_equal(arg_uuid, this_id)) {
log_full(try ? LOG_WARNING : LOG_ERR,
- "Host and machine ids are equal (%s): refusing to link journals", id);
+ "Host and machine ids are equal (%s): refusing to link journals", sd_id128_to_string(arg_uuid, id));
if (try)
return 0;
return -EEXIST;
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index 081131ede0..a4a67623e7 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -1804,7 +1804,8 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
* - For unsigned SOA/NS we get the matching DS
* - For unsigned CNAME/DNAME/DS we get the parent SOA RR
* - For other unsigned RRs we get the matching SOA RR
- * - For SOA/NS/DS queries with no matching response RRs, and no NSEC/NSEC3, the parent's SOA RR
+ * - For SOA/NS queries with no matching response RR, and no NSEC/NSEC3, the DS RR
+ * - For DS queries with no matching response RRs, and no NSEC/NSEC3, the parent's SOA RR
* - For other queries with no matching response RRs, and no NSEC/NSEC3, the SOA RR
*/
@@ -2038,32 +2039,42 @@ int dns_transaction_request_dnssec_keys(DnsTransaction *t) {
return r;
if (r > 0) {
const char *name;
+ uint16_t type = 0;
name = dns_resource_key_name(t->key);
- /* If this was a SOA or NS request, then this
- * indicates that we are not at a zone apex, hence ask
- * the parent name instead. If this was a DS request,
- * then it's signed when the parent zone is signed,
- * hence ask the parent in that case, too. */
+ /* If this was a SOA or NS request, then check if there's a DS RR for the same domain. Note that this
+ * could also be used as indication that we are not at a zone apex, but in real world setups there are
+ * too many broken DNS servers (Hello, incapdns.net!) where non-terminal zones return NXDOMAIN even
+ * though they have further children. If this was a DS request, then it's signed when the parent zone
+ * is signed, hence ask the parent SOA in that case. If this was any other RR then ask for the SOA RR,
+ * to see if that is signed. */
- if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS, DNS_TYPE_DS)) {
+ if (t->key->type == DNS_TYPE_DS) {
r = dns_name_parent(&name);
- if (r < 0)
- return r;
- if (r > 0)
- log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned empty SOA/NS/DS response).",
+ if (r > 0) {
+ type = DNS_TYPE_SOA;
+ log_debug("Requesting parent SOA to validate transaction %" PRIu16 " (%s, unsigned empty DS response).",
t->id, dns_resource_key_name(t->key));
- else
+ } else
name = NULL;
- } else
+
+ } else if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS)) {
+
+ type = DNS_TYPE_DS;
+ log_debug("Requesting DS to validate transaction %" PRIu16 " (%s, unsigned empty SOA/NS response).",
+ t->id, dns_resource_key_name(t->key));
+
+ } else {
+ type = DNS_TYPE_SOA;
log_debug("Requesting SOA to validate transaction %" PRIu16 " (%s, unsigned empty non-SOA/NS/DS response).",
t->id, dns_resource_key_name(t->key));
+ }
if (name) {
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *soa = NULL;
- soa = dns_resource_key_new(t->key->class, DNS_TYPE_SOA, name);
+ soa = dns_resource_key_new(t->key->class, type, name);
if (!soa)
return -ENOMEM;
@@ -2317,11 +2328,12 @@ static int dns_transaction_in_private_tld(DnsTransaction *t, const DnsResourceKe
}
static int dns_transaction_requires_nsec(DnsTransaction *t) {
+ char key_str[DNS_RESOURCE_KEY_STRING_MAX];
DnsTransaction *dt;
const char *name;
+ uint16_t type = 0;
Iterator i;
int r;
- char key_str[DNS_RESOURCE_KEY_STRING_MAX];
assert(t);
@@ -2355,22 +2367,25 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) {
name = dns_resource_key_name(t->key);
- if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS, DNS_TYPE_DS)) {
+ if (t->key->type == DNS_TYPE_DS) {
- /* We got a negative reply for this SOA/NS lookup? If
- * so, then we are not at a zone apex, and thus should
- * look at the result of the parent SOA lookup.
- *
- * We got a negative reply for this DS lookup? DS RRs
- * are signed when their parent zone is signed, hence
- * also check the parent SOA in this case. */
+ /* We got a negative reply for this DS lookup? DS RRs are signed when their parent zone is signed,
+ * hence check the parent SOA in this case. */
r = dns_name_parent(&name);
if (r < 0)
return r;
if (r == 0)
return true;
- }
+
+ type = DNS_TYPE_SOA;
+
+ } else if (IN_SET(t->key->type, DNS_TYPE_SOA, DNS_TYPE_NS))
+ /* We got a negative reply for this SOA/NS lookup? If so, check if there's a DS RR for this */
+ type = DNS_TYPE_DS;
+ else
+ /* For all other negative replies, check for the SOA lookup */
+ type = DNS_TYPE_SOA;
/* For all other RRs we check the SOA on the same level to see
* if it's signed. */
@@ -2379,7 +2394,7 @@ static int dns_transaction_requires_nsec(DnsTransaction *t) {
if (dt->key->class != t->key->class)
continue;
- if (dt->key->type != DNS_TYPE_SOA)
+ if (dt->key->type != type)
continue;
r = dns_name_equal(dns_resource_key_name(dt->key), name);
diff --git a/src/shared/dropin.c b/src/shared/dropin.c
index cc1acd6f23..b9cd952ac8 100644
--- a/src/shared/dropin.c
+++ b/src/shared/dropin.c
@@ -160,7 +160,7 @@ static int iterate_dir(
if (!de)
break;
- if (hidden_file(de->d_name))
+ if (hidden_or_backup_file(de->d_name))
continue;
f = strjoin(path, "/", de->d_name, NULL);
diff --git a/src/shared/install.c b/src/shared/install.c
index d2799bf0df..cc36da1853 100644
--- a/src/shared/install.c
+++ b/src/shared/install.c
@@ -97,6 +97,25 @@ static inline void presets_freep(Presets *p) {
static int unit_file_lookup_state(UnitFileScope scope, const LookupPaths *paths, const char *name, UnitFileState *ret);
+bool unit_type_may_alias(UnitType type) {
+ return IN_SET(type,
+ UNIT_SERVICE,
+ UNIT_SOCKET,
+ UNIT_TARGET,
+ UNIT_DEVICE,
+ UNIT_TIMER,
+ UNIT_PATH);
+}
+
+bool unit_type_may_template(UnitType type) {
+ return IN_SET(type,
+ UNIT_SERVICE,
+ UNIT_SOCKET,
+ UNIT_TARGET,
+ UNIT_TIMER,
+ UNIT_PATH);
+}
+
static int in_search_path(const LookupPaths *p, const char *path) {
_cleanup_free_ char *parent = NULL;
char **i;
@@ -927,6 +946,36 @@ fail:
return r;
}
+static int config_parse_alias(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ const char *name;
+ UnitType type;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ name = basename(filename);
+ type = unit_name_to_type(name);
+ if (!unit_type_may_alias(type))
+ return log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "Aliases are not allowed for %s units, ignoring.",
+ unit_type_to_string(type));
+
+ return config_parse_strv(unit, filename, line, section, section_line,
+ lvalue, ltype, rvalue, data, userdata);
+}
+
static int config_parse_also(
const char *unit,
const char *filename,
@@ -983,6 +1032,7 @@ static int config_parse_default_instance(
void *userdata) {
UnitFileInstallInfo *i = data;
+ const char *name;
char *printed;
int r;
@@ -990,6 +1040,15 @@ static int config_parse_default_instance(
assert(lvalue);
assert(rvalue);
+ name = basename(filename);
+ if (unit_name_is_valid(name, UNIT_NAME_INSTANCE))
+ /* When enabling an instance, we might be using a template unit file,
+ * but we should ignore DefaultInstance silently. */
+ return 0;
+ if (!unit_name_is_valid(name, UNIT_NAME_TEMPLATE))
+ return log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "DefaultInstance only makes sense for template units, ignoring.");
+
r = install_full_printf(i, rvalue, &printed);
if (r < 0)
return r;
@@ -1012,7 +1071,7 @@ static int unit_file_load(
SearchFlags flags) {
const ConfigTableItem items[] = {
- { "Install", "Alias", config_parse_strv, 0, &info->aliases },
+ { "Install", "Alias", config_parse_alias, 0, &info->aliases },
{ "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by },
{ "Install", "RequiredBy", config_parse_strv, 0, &info->required_by },
{ "Install", "DefaultInstance", config_parse_default_instance, 0, info },
@@ -1020,6 +1079,8 @@ static int unit_file_load(
{}
};
+ const char *name;
+ UnitType type;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_close_ int fd = -1;
struct stat st;
@@ -1029,6 +1090,12 @@ static int unit_file_load(
assert(info);
assert(path);
+ name = basename(path);
+ type = unit_name_to_type(name);
+ if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) &&
+ !unit_type_may_template(type))
+ return log_error_errno(EINVAL, "Unit type %s cannot be templated.", unit_type_to_string(type));
+
if (!(flags & SEARCH_LOAD)) {
r = lstat(path, &st);
if (r < 0)
@@ -2751,7 +2818,9 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList*, unit_file_list_free_one);
int unit_file_get_list(
UnitFileScope scope,
const char *root_dir,
- Hashmap *h) {
+ Hashmap *h,
+ char **states,
+ char **patterns) {
_cleanup_lookup_paths_free_ LookupPaths paths = {};
char **i;
@@ -2783,6 +2852,9 @@ int unit_file_get_list(
if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
continue;
+ if (!strv_fnmatch_or_empty(patterns, de->d_name, FNM_NOESCAPE))
+ continue;
+
if (hashmap_get(h, de->d_name))
continue;
@@ -2803,6 +2875,10 @@ int unit_file_get_list(
if (r < 0)
f->state = UNIT_FILE_BAD;
+ if (!strv_isempty(states) &&
+ !strv_contains(states, unit_file_state_to_string(f->state)))
+ continue;
+
r = hashmap_put(h, basename(f->path), f);
if (r < 0)
return r;
diff --git a/src/shared/install.h b/src/shared/install.h
index 4133faffa2..5812447c5b 100644
--- a/src/shared/install.h
+++ b/src/shared/install.h
@@ -138,6 +138,9 @@ static inline bool UNIT_FILE_INSTALL_INFO_HAS_ALSO(UnitFileInstallInfo *i) {
return !strv_isempty(i->also);
}
+bool unit_type_may_alias(UnitType type) _const_;
+bool unit_type_may_template(UnitType type) _const_;
+
int unit_file_enable(
UnitFileScope scope,
bool runtime,
@@ -232,7 +235,7 @@ int unit_file_add_dependency(
int unit_file_get_state(UnitFileScope scope, const char *root_dir, const char *filename, UnitFileState *ret);
int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *name);
-int unit_file_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h);
+int unit_file_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h, char **states, char **patterns);
Hashmap* unit_file_list_free(Hashmap *h);
int unit_file_changes_add(UnitFileChange **changes, unsigned *n_changes, UnitFileChangeType type, const char *path, const char *source);
diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c
index bebfc40efe..66f58ecd92 100644
--- a/src/shared/machine-image.c
+++ b/src/shared/machine-image.c
@@ -423,7 +423,7 @@ int image_remove(Image *i) {
case IMAGE_DIRECTORY:
/* Allow deletion of read-only directories */
- (void) chattr_path(i->path, false, FS_IMMUTABLE_FL);
+ (void) chattr_path(i->path, 0, FS_IMMUTABLE_FL);
r = rm_rf(i->path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
if (r < 0)
return r;
@@ -505,7 +505,7 @@ int image_rename(Image *i, const char *new_name) {
(void) read_attr_path(i->path, &file_attr);
if (file_attr & FS_IMMUTABLE_FL)
- (void) chattr_path(i->path, false, FS_IMMUTABLE_FL);
+ (void) chattr_path(i->path, 0, FS_IMMUTABLE_FL);
/* fall through */
@@ -538,7 +538,7 @@ int image_rename(Image *i, const char *new_name) {
/* Restore the immutable bit, if it was set before */
if (file_attr & FS_IMMUTABLE_FL)
- (void) chattr_path(new_path, true, FS_IMMUTABLE_FL);
+ (void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL);
free(i->path);
i->path = new_path;
@@ -603,13 +603,21 @@ int image_clone(Image *i, const char *new_name, bool read_only) {
case IMAGE_SUBVOLUME:
case IMAGE_DIRECTORY:
+ /* If we can we'll always try to create a new btrfs subvolume here, even if the source is a plain
+ * directory.*/
+
new_path = strjoina("/var/lib/machines/", new_name);
r = btrfs_subvol_snapshot(i->path, new_path, (read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | BTRFS_SNAPSHOT_FALLBACK_COPY | BTRFS_SNAPSHOT_RECURSIVE | BTRFS_SNAPSHOT_QUOTA);
+ if (r == -EOPNOTSUPP) {
+ /* No btrfs snapshots supported, create a normal directory then. */
- /* Enable "subtree" quotas for the copy, if we didn't
- * copy any quota from the source. */
- (void) btrfs_subvol_auto_qgroup(i->path, 0, true);
+ r = copy_directory(i->path, new_path, false);
+ if (r >= 0)
+ (void) chattr_path(new_path, read_only ? FS_IMMUTABLE_FL : 0, FS_IMMUTABLE_FL);
+ } else if (r >= 0)
+ /* Enable "subtree" quotas for the copy, if we didn't copy any quota from the source. */
+ (void) btrfs_subvol_auto_qgroup(new_path, 0, true);
break;
@@ -670,7 +678,7 @@ int image_read_only(Image *i, bool b) {
a read-only subvolume, but at least something, and
we can read the value back.*/
- r = chattr_path(i->path, b, FS_IMMUTABLE_FL);
+ r = chattr_path(i->path, b ? FS_IMMUTABLE_FL : 0, FS_IMMUTABLE_FL);
if (r < 0)
return r;
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 595d6853c6..9af25e22a4 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -541,6 +541,7 @@ static int get_unit_list(
size_t size = c;
int r;
UnitInfo u;
+ bool fallback = false;
assert(bus);
assert(unit_infos);
@@ -552,8 +553,7 @@ static int get_unit_list(
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- "ListUnitsFiltered");
-
+ "ListUnitsByPatterns");
if (r < 0)
return bus_log_create_error(r);
@@ -561,7 +561,34 @@ static int get_unit_list(
if (r < 0)
return bus_log_create_error(r);
+ r = sd_bus_message_append_strv(m, patterns);
+ if (r < 0)
+ return bus_log_create_error(r);
+
r = sd_bus_call(bus, m, 0, &error, &reply);
+ if (r < 0 && sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) {
+ /* Fallback to legacy ListUnitsFiltered method */
+ fallback = true;
+ log_debug_errno(r, "Failed to list units: %s Falling back to ListUnitsFiltered method.", bus_error_message(&error, r));
+ m = sd_bus_message_unref(m);
+ sd_bus_error_free(&error);
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "ListUnitsFiltered");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append_strv(m, arg_states);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, 0, &error, &reply);
+ }
if (r < 0)
return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r));
@@ -572,7 +599,7 @@ static int get_unit_list(
while ((r = bus_parse_unit_info(reply, &u)) > 0) {
u.machine = machine;
- if (!output_show_unit(&u, patterns))
+ if (!output_show_unit(&u, fallback ? patterns : NULL))
continue;
if (!GREEDY_REALLOC(*unit_infos, size, c+1))
@@ -1282,7 +1309,7 @@ static int compare_unit_file_list(const void *a, const void *b) {
return strcasecmp(basename(u->path), basename(v->path));
}
-static bool output_show_unit_file(const UnitFileList *u, char **patterns) {
+static bool output_show_unit_file(const UnitFileList *u, char **states, char **patterns) {
assert(u);
if (!strv_fnmatch_or_empty(patterns, basename(u->path), FNM_NOESCAPE))
@@ -1299,8 +1326,8 @@ static bool output_show_unit_file(const UnitFileList *u, char **patterns) {
return false;
}
- if (!strv_isempty(arg_states) &&
- !strv_find(arg_states, unit_file_state_to_string(u->state)))
+ if (!strv_isempty(states) &&
+ !strv_find(states, unit_file_state_to_string(u->state)))
return false;
return true;
@@ -1373,6 +1400,7 @@ static int list_unit_files(int argc, char *argv[], void *userdata) {
const char *state;
char *path;
int r;
+ bool fallback = false;
pager_open(arg_no_pager, false);
@@ -1386,7 +1414,7 @@ static int list_unit_files(int argc, char *argv[], void *userdata) {
if (!h)
return log_oom();
- r = unit_file_get_list(arg_scope, arg_root, h);
+ r = unit_file_get_list(arg_scope, arg_root, h, arg_states, strv_skip(argv, 1));
if (r < 0) {
unit_file_list_free(h);
return log_error_errno(r, "Failed to get unit file list: %m");
@@ -1401,7 +1429,7 @@ static int list_unit_files(int argc, char *argv[], void *userdata) {
}
HASHMAP_FOREACH(u, h, i) {
- if (!output_show_unit_file(u, strv_skip(argv, 1)))
+ if (!output_show_unit_file(u, NULL, NULL))
continue;
units[c++] = *u;
@@ -1411,6 +1439,7 @@ static int list_unit_files(int argc, char *argv[], void *userdata) {
assert(c <= n_units);
hashmap_free(h);
} else {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
sd_bus *bus;
@@ -1418,15 +1447,44 @@ static int list_unit_files(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
- r = sd_bus_call_method(
+ r = sd_bus_message_new_method_call(
bus,
+ &m,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- "ListUnitFiles",
- &error,
- &reply,
- NULL);
+ "ListUnitFilesByPatterns");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append_strv(m, arg_states);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append_strv(m, strv_skip(argv, 1));
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, 0, &error, &reply);
+ if (r < 0 && sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) {
+ /* Fallback to legacy ListUnitFiles method */
+ fallback = true;
+ log_debug_errno(r, "Failed to list unit files: %s Falling back to ListUnitsFiles method.", bus_error_message(&error, r));
+ m = sd_bus_message_unref(m);
+ sd_bus_error_free(&error);
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "ListUnitFiles");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, 0, &error, &reply);
+ }
if (r < 0)
return log_error_errno(r, "Failed to list unit files: %s", bus_error_message(&error, r));
@@ -1444,7 +1502,9 @@ static int list_unit_files(int argc, char *argv[], void *userdata) {
unit_file_state_from_string(state)
};
- if (output_show_unit_file(&units[c], strv_skip(argv, 1)))
+ if (output_show_unit_file(&units[c],
+ fallback ? arg_states : NULL,
+ fallback ? strv_skip(argv, 1) : NULL))
c++;
}
diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h
index af7a797567..3ae110c080 100644
--- a/src/systemd/sd-netlink.h
+++ b/src/systemd/sd-netlink.h
@@ -137,8 +137,10 @@ int sd_rtnl_message_route_set_dst_prefixlen(sd_netlink_message *m, unsigned char
int sd_rtnl_message_route_set_src_prefixlen(sd_netlink_message *m, unsigned char prefixlen);
int sd_rtnl_message_route_set_scope(sd_netlink_message *m, unsigned char scope);
int sd_rtnl_message_route_set_flags(sd_netlink_message *m, unsigned flags);
+int sd_rtnl_message_route_set_table(sd_netlink_message *m, unsigned char table);
int sd_rtnl_message_route_get_flags(sd_netlink_message *m, unsigned *flags);
int sd_rtnl_message_route_get_family(sd_netlink_message *m, int *family);
+int sd_rtnl_message_route_set_family(sd_netlink_message *m, int family);
int sd_rtnl_message_route_get_protocol(sd_netlink_message *m, unsigned char *protocol);
int sd_rtnl_message_route_get_scope(sd_netlink_message *m, unsigned char *scope);
int sd_rtnl_message_route_get_tos(sd_netlink_message *m, unsigned char *tos);
diff --git a/src/test/test-copy.c b/src/test/test-copy.c
index cb437754b4..68154fc4e8 100644
--- a/src/test/test-copy.c
+++ b/src/test/test-copy.c
@@ -95,6 +95,8 @@ static void test_copy_tree(void) {
char **links = STRV_MAKE("link", "file",
"link2", "dir1/file");
char **p, **link;
+ const char *unixsockp;
+ struct stat st;
log_info("%s", __func__);
@@ -102,26 +104,34 @@ static void test_copy_tree(void) {
(void) rm_rf(original_dir, REMOVE_ROOT|REMOVE_PHYSICAL);
STRV_FOREACH(p, files) {
- char *f = strjoina(original_dir, *p);
+ _cleanup_free_ char *f;
+
+ assert_se(f = strappend(original_dir, *p));
assert_se(mkdir_parents(f, 0755) >= 0);
assert_se(write_string_file(f, "file", WRITE_STRING_FILE_CREATE) == 0);
}
STRV_FOREACH_PAIR(link, p, links) {
- char *f = strjoina(original_dir, *p);
- char *l = strjoina(original_dir, *link);
+ _cleanup_free_ char *f, *l;
+
+ assert_se(f = strappend(original_dir, *p));
+ assert_se(l = strappend(original_dir, *link));
assert_se(mkdir_parents(l, 0755) >= 0);
assert_se(symlink(f, l) == 0);
}
+ unixsockp = strjoina(original_dir, "unixsock");
+ assert_se(mknod(unixsockp, S_IFSOCK|0644, 0) >= 0);
+
assert_se(copy_tree(original_dir, copy_dir, true) == 0);
STRV_FOREACH(p, files) {
- _cleanup_free_ char *buf = NULL;
+ _cleanup_free_ char *buf = NULL, *f;
size_t sz = 0;
- char *f = strjoina(copy_dir, *p);
+
+ assert_se(f = strappend(copy_dir, *p));
assert_se(access(f, F_OK) == 0);
assert_se(read_full_file(f, &buf, &sz) == 0);
@@ -129,14 +139,19 @@ static void test_copy_tree(void) {
}
STRV_FOREACH_PAIR(link, p, links) {
- _cleanup_free_ char *target = NULL;
- char *f = strjoina(original_dir, *p);
- char *l = strjoina(copy_dir, *link);
+ _cleanup_free_ char *target = NULL, *f, *l;
+
+ assert_se(f = strjoin(original_dir, *p, NULL));
+ assert_se(l = strjoin(copy_dir, *link, NULL));
assert_se(readlink_and_canonicalize(l, &target) == 0);
assert_se(path_equal(f, target));
}
+ unixsockp = strjoina(copy_dir, "unixsock");
+ assert_se(stat(unixsockp, &st) >= 0);
+ assert_se(S_ISSOCK(st.st_mode));
+
assert_se(copy_tree(original_dir, copy_dir, false) < 0);
assert_se(copy_tree("/tmp/inexistent/foo/bar/fsdoi", copy_dir, false) < 0);
diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c
index 2aee33da60..4b9a74fca4 100644
--- a/src/test/test-install-root.c
+++ b/src/test/test-install-root.c
@@ -606,7 +606,7 @@ static void test_preset_and_list(const char *root) {
assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED);
assert_se(h = hashmap_new(&string_hash_ops));
- assert_se(unit_file_get_list(UNIT_FILE_SYSTEM, root, h) >= 0);
+ assert_se(unit_file_get_list(UNIT_FILE_SYSTEM, root, h, NULL, NULL) >= 0);
p = strjoina(root, "/usr/lib/systemd/system/preset-yes.service");
q = strjoina(root, "/usr/lib/systemd/system/preset-no.service");
diff --git a/src/test/test-install.c b/src/test/test-install.c
index 50315c1d9a..0ac85f040a 100644
--- a/src/test/test-install.c
+++ b/src/test/test-install.c
@@ -50,7 +50,7 @@ int main(int argc, char* argv[]) {
log_parse_environment();
h = hashmap_new(&string_hash_ops);
- r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h);
+ r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h, NULL, NULL);
assert_se(r == 0);
HASHMAP_FOREACH(p, h, i) {
diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c
index 5d77e2959c..b53324b5e6 100644
--- a/src/test/test-path-util.c
+++ b/src/test/test-path-util.c
@@ -489,6 +489,27 @@ static void test_filename_is_valid(void) {
assert_se(filename_is_valid("o.o"));
}
+static void test_hidden_or_backup_file(void) {
+ assert_se(hidden_or_backup_file(".hidden"));
+ assert_se(hidden_or_backup_file("..hidden"));
+ assert_se(!hidden_or_backup_file("hidden."));
+
+ assert_se(hidden_or_backup_file("backup~"));
+ assert_se(hidden_or_backup_file(".backup~"));
+
+ assert_se(hidden_or_backup_file("lost+found"));
+ assert_se(hidden_or_backup_file("aquota.user"));
+ assert_se(hidden_or_backup_file("aquota.group"));
+
+ assert_se(hidden_or_backup_file("test.rpmnew"));
+ assert_se(hidden_or_backup_file("test.dpkg-old"));
+ assert_se(hidden_or_backup_file("test.dpkg-remove"));
+ assert_se(hidden_or_backup_file("test.swp"));
+
+ assert_se(!hidden_or_backup_file("test.rpmnew."));
+ assert_se(!hidden_or_backup_file("test.dpkg-old.foo"));
+}
+
int main(int argc, char **argv) {
test_path();
test_find_binary(argv[0]);
@@ -502,6 +523,7 @@ int main(int argc, char **argv) {
test_path_is_mount_point();
test_file_in_same_dir();
test_filename_is_valid();
+ test_hidden_or_backup_file();
return 0;
}
diff --git a/src/test/test-rlimit-util.c b/src/test/test-rlimit-util.c
index d9ac9368cd..62afd2de5e 100644
--- a/src/test/test-rlimit-util.c
+++ b/src/test/test-rlimit-util.c
@@ -99,6 +99,18 @@ int main(int argc, char *argv[]) {
test_rlimit_parse_format(RLIMIT_NOFILE, "", 0, 0, -EINVAL, NULL);
test_rlimit_parse_format(RLIMIT_NOFILE, "5:4", 0, 0, -EILSEQ, NULL);
test_rlimit_parse_format(RLIMIT_NOFILE, "5:4:3", 0, 0, -EINVAL, NULL);
+ test_rlimit_parse_format(RLIMIT_NICE, "20", 20, 20, 0, "20");
+ test_rlimit_parse_format(RLIMIT_NICE, "40", 40, 40, 0, "40");
+ test_rlimit_parse_format(RLIMIT_NICE, "41", 41, 41, -ERANGE, "41");
+ test_rlimit_parse_format(RLIMIT_NICE, "0", 0, 0, 0, "0");
+ test_rlimit_parse_format(RLIMIT_NICE, "-7", 27, 27, 0, "27");
+ test_rlimit_parse_format(RLIMIT_NICE, "-20", 40, 40, 0, "40");
+ test_rlimit_parse_format(RLIMIT_NICE, "-21", 41, 41, -ERANGE, "41");
+ test_rlimit_parse_format(RLIMIT_NICE, "-0", 20, 20, 0, "20");
+ test_rlimit_parse_format(RLIMIT_NICE, "+7", 13, 13, 0, "13");
+ test_rlimit_parse_format(RLIMIT_NICE, "+19", 1, 1, 0, "1");
+ test_rlimit_parse_format(RLIMIT_NICE, "+20", 0, 0, -ERANGE, "0");
+ test_rlimit_parse_format(RLIMIT_NICE, "+0", 20, 20, 0, "20");
return 0;
}
diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c
index 114ddf8478..c340673c6c 100644
--- a/src/test/test-unit-file.c
+++ b/src/test/test-unit-file.c
@@ -53,7 +53,7 @@ static int test_unit_file_get_set(void) {
h = hashmap_new(&string_hash_ops);
assert_se(h);
- r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h);
+ r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h, NULL, NULL);
if (r == -EPERM || r == -EACCES) {
printf("Skipping test: unit_file_get_list: %s", strerror(-r));
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 7b67831e54..c7ded451a2 100644
--- a/src/tty-ask-password-agent/tty-ask-password-agent.c
+++ b/src/tty-ask-password-agent/tty-ask-password-agent.c
@@ -481,7 +481,7 @@ static int show_passwords(void) {
if (de->d_type != DT_REG)
continue;
- if (hidden_file(de->d_name))
+ if (hidden_or_backup_file(de->d_name))
continue;
if (!startswith(de->d_name, "ask."))