summaryrefslogtreecommitdiff
path: root/src/basic
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2018-01-28 22:49:17 +0100
committerMichael Biebl <biebl@debian.org>2018-01-28 22:49:17 +0100
commit1d42b86df9052528a8f56b2f52d8bc2faf87b2da (patch)
tree0d80f37a1ad6f02067261ee3e7ee62e1869fcd56 /src/basic
parent52ad194e0b816b8273dd8d0fea3e6d467f6ca34e (diff)
downloadsystemd-1d42b86df9052528a8f56b2f52d8bc2faf87b2da.tar.gz
New upstream version 237
Diffstat (limited to 'src/basic')
-rw-r--r--src/basic/af-list.h2
-rw-r--r--src/basic/async.c69
-rw-r--r--src/basic/async.h2
-rw-r--r--src/basic/blockdev-util.c199
-rw-r--r--src/basic/blockdev-util.h38
-rw-r--r--src/basic/btrfs-ctree.h1
-rw-r--r--src/basic/btrfs-util.c20
-rw-r--r--src/basic/btrfs-util.h1
-rw-r--r--src/basic/build.h7
-rw-r--r--src/basic/calendarspec.c29
-rw-r--r--src/basic/cgroup-util.c12
-rw-r--r--src/basic/def.h7
-rw-r--r--src/basic/device-nodes.h5
-rw-r--r--src/basic/errno-list.c1
-rw-r--r--src/basic/errno-list.h4
-rw-r--r--src/basic/ether-addr-util.c1
-rw-r--r--src/basic/exec-util.c44
-rw-r--r--src/basic/fd-util.c23
-rw-r--r--src/basic/fileio.c39
-rw-r--r--src/basic/fs-util.c136
-rw-r--r--src/basic/fs-util.h12
-rw-r--r--src/basic/gcrypt-util.c72
-rw-r--r--src/basic/gcrypt-util.h48
-rwxr-xr-xsrc/basic/generate-af-list.sh3
-rwxr-xr-xsrc/basic/generate-arphrd-list.sh3
-rwxr-xr-xsrc/basic/generate-cap-list.sh3
-rwxr-xr-xsrc/basic/generate-errno-list.sh3
-rwxr-xr-xsrc/basic/generate-socket-protocol-list.sh6
-rw-r--r--src/basic/gunicode.c2
-rw-r--r--src/basic/hash-funcs.c2
-rw-r--r--src/basic/hostname-util.h1
-rw-r--r--src/basic/io-util.c9
-rw-r--r--src/basic/journal-importer.c1
-rw-r--r--src/basic/label.c25
-rw-r--r--src/basic/label.h2
-rw-r--r--src/basic/locale-util.h8
-rw-r--r--src/basic/log.c90
-rw-r--r--src/basic/log.h61
-rw-r--r--src/basic/macro.h10
-rw-r--r--src/basic/meson.build38
-rw-r--r--src/basic/missing.h76
-rw-r--r--src/basic/missing_syscall.h4
-rw-r--r--src/basic/mkdir-label.c21
-rw-r--r--src/basic/mkdir.c28
-rw-r--r--src/basic/mkdir.h1
-rw-r--r--src/basic/parse-util.c40
-rw-r--r--src/basic/path-util.c37
-rw-r--r--src/basic/path-util.h1
-rw-r--r--src/basic/process-util.c294
-rw-r--r--src/basic/process-util.h67
-rw-r--r--src/basic/procfs-util.c138
-rw-r--r--src/basic/procfs-util.h8
-rw-r--r--src/basic/random-util.c5
-rw-r--r--src/basic/raw-clone.h42
-rw-r--r--src/basic/rm-rf.h3
-rw-r--r--src/basic/securebits-util.c1
-rw-r--r--src/basic/securebits-util.h8
-rw-r--r--src/basic/signal-util.h7
-rw-r--r--src/basic/smack-util.c2
-rw-r--r--src/basic/socket-label.c23
-rw-r--r--src/basic/socket-protocol-list.c57
-rw-r--r--src/basic/socket-protocol-list.h26
-rw-r--r--src/basic/socket-protocol-to-name.awk9
-rw-r--r--src/basic/socket-util.c146
-rw-r--r--src/basic/socket-util.h17
-rw-r--r--src/basic/stat-util.c5
-rw-r--r--src/basic/string-util.c29
-rw-r--r--src/basic/string-util.h8
-rw-r--r--src/basic/terminal-util.c49
-rw-r--r--src/basic/time-util.c19
-rw-r--r--src/basic/unaligned.h60
-rw-r--r--src/basic/unit-name.c9
-rw-r--r--src/basic/user-util.c44
-rw-r--r--src/basic/user-util.h2
-rw-r--r--src/basic/util.c329
-rw-r--r--src/basic/util.h9
-rw-r--r--src/basic/verbs.c44
-rw-r--r--src/basic/verbs.h8
-rw-r--r--src/basic/virt.c47
79 files changed, 1987 insertions, 775 deletions
diff --git a/src/basic/af-list.h b/src/basic/af-list.h
index 65656022cc..1684bc6a0f 100644
--- a/src/basic/af-list.h
+++ b/src/basic/af-list.h
@@ -20,6 +20,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <sys/socket.h>
+
#include "string-util.h"
const char *af_to_name(int id);
diff --git a/src/basic/async.c b/src/basic/async.c
index d368b92522..b6c6d6a80b 100644
--- a/src/basic/async.c
+++ b/src/basic/async.c
@@ -27,49 +27,82 @@
#include "fd-util.h"
#include "log.h"
#include "macro.h"
+#include "process-util.h"
+#include "signal-util.h"
#include "util.h"
int asynchronous_job(void* (*func)(void *p), void *arg) {
+ sigset_t ss, saved_ss;
pthread_attr_t a;
pthread_t t;
- int r;
+ int r, k;
- /* It kinda sucks that we have to resort to threads to
- * implement an asynchronous sync(), but well, such is
- * life.
- *
- * Note that issuing this command right before exiting a
- * process will cause the process to wait for the sync() to
- * complete. This function hence is nicely asynchronous really
- * only in long running processes. */
+ /* It kinda sucks that we have to resort to threads to implement an asynchronous close(), but well, such is
+ * life. */
r = pthread_attr_init(&a);
if (r > 0)
return -r;
r = pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED);
- if (r > 0)
+ if (r > 0) {
+ r = -r;
+ goto finish;
+ }
+
+ if (sigfillset(&ss) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ /* Block all signals before forking off the thread, so that the new thread is started with all signals
+ * blocked. This way the existence of the new thread won't affect signal handling in other threads. */
+
+ r = pthread_sigmask(SIG_BLOCK, &ss, &saved_ss);
+ if (r > 0) {
+ r = -r;
goto finish;
+ }
r = pthread_create(&t, &a, func, arg);
+ k = pthread_sigmask(SIG_SETMASK, &saved_ss, NULL);
+
+ if (r > 0)
+ r = -r;
+ else if (k > 0)
+ r = -k;
+ else
+ r = 0;
+
finish:
pthread_attr_destroy(&a);
- return -r;
+ return r;
}
-static void *sync_thread(void *p) {
- sync();
- return NULL;
-}
+int asynchronous_sync(pid_t *ret_pid) {
+ int r;
-int asynchronous_sync(void) {
- log_debug("Spawning new thread for sync");
+ /* This forks off an invocation of fork() as a child process, in order to initiate synchronization to
+ * disk. Note that we implement this as helper process rather than thread as we don't want the sync() to hang our
+ * original process ever, and a thread would do that as the process can't exit with threads hanging in blocking
+ * syscalls. */
+
+ r = safe_fork("(sd-sync)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS, ret_pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ /* Child process */
+ (void) sync();
+ _exit(EXIT_SUCCESS);
+ }
- return asynchronous_job(sync_thread, NULL);
+ return 0;
}
static void *close_thread(void *p) {
+ (void) pthread_setname_np(pthread_self(), "close");
+
assert_se(close_nointr(PTR_TO_FD(p)) != -EBADF);
return NULL;
}
diff --git a/src/basic/async.h b/src/basic/async.h
index 7eac54d8b2..01c975bb30 100644
--- a/src/basic/async.h
+++ b/src/basic/async.h
@@ -22,5 +22,5 @@
int asynchronous_job(void* (*func)(void *p), void *arg);
-int asynchronous_sync(void);
+int asynchronous_sync(pid_t *ret_pid);
int asynchronous_close(int fd);
diff --git a/src/basic/blockdev-util.c b/src/basic/blockdev-util.c
new file mode 100644
index 0000000000..3a8f8d1c27
--- /dev/null
+++ b/src/basic/blockdev-util.c
@@ -0,0 +1,199 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/stat.h>
+#include <sys/statfs.h>
+
+#include "alloc-util.h"
+#include "blockdev-util.h"
+#include "btrfs-util.h"
+#include "dirent-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "missing.h"
+#include "stat-util.h"
+
+int block_get_whole_disk(dev_t d, dev_t *ret) {
+ char p[SYS_BLOCK_PATH_MAX("/partition")];
+ _cleanup_free_ char *s = NULL;
+ unsigned n, m;
+ int r;
+
+ assert(ret);
+
+ /* If it has a queue this is good enough for us */
+ xsprintf_sys_block_path(p, "/queue", d);
+ if (access(p, F_OK) >= 0) {
+ *ret = d;
+ return 0;
+ }
+
+ /* If it is a partition find the originating device */
+ xsprintf_sys_block_path(p, "/partition", d);
+ if (access(p, F_OK) < 0)
+ return -ENOENT;
+
+ /* Get parent dev_t */
+ xsprintf_sys_block_path(p, "/../dev", d);
+ r = read_one_line_file(p, &s);
+ if (r < 0)
+ return r;
+
+ r = sscanf(s, "%u:%u", &m, &n);
+ if (r != 2)
+ return -EINVAL;
+
+ /* Only return this if it is really good enough for us. */
+ xsprintf_sys_block_path(p, "/queue", makedev(m, n));
+ if (access(p, F_OK) < 0)
+ return -ENOENT;
+
+ *ret = makedev(m, n);
+ return 0;
+}
+
+int get_block_device(const char *path, dev_t *dev) {
+ struct stat st;
+ struct statfs sfs;
+
+ assert(path);
+ assert(dev);
+
+ /* Get's the block device directly backing a file system. If
+ * the block device is encrypted, returns the device mapper
+ * block device. */
+
+ if (lstat(path, &st))
+ return -errno;
+
+ if (major(st.st_dev) != 0) {
+ *dev = st.st_dev;
+ return 1;
+ }
+
+ if (statfs(path, &sfs) < 0)
+ return -errno;
+
+ if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
+ return btrfs_get_block_device(path, dev);
+
+ return 0;
+}
+
+int get_block_device_harder(const char *path, dev_t *dev) {
+ _cleanup_closedir_ DIR *d = NULL;
+ _cleanup_free_ char *t = NULL;
+ char p[SYS_BLOCK_PATH_MAX("/slaves")];
+ struct dirent *de, *found = NULL;
+ const char *q;
+ unsigned maj, min;
+ dev_t dt;
+ int r;
+
+ assert(path);
+ assert(dev);
+
+ /* Gets the backing block device for a file system, and
+ * handles LUKS encrypted file systems, looking for its
+ * immediate parent, if there is one. */
+
+ r = get_block_device(path, &dt);
+ if (r <= 0)
+ return r;
+
+ xsprintf_sys_block_path(p, "/slaves", dt);
+ d = opendir(p);
+ if (!d) {
+ if (errno == ENOENT)
+ goto fallback;
+
+ return -errno;
+ }
+
+ FOREACH_DIRENT_ALL(de, d, return -errno) {
+
+ if (dot_or_dot_dot(de->d_name))
+ continue;
+
+ if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
+ continue;
+
+ if (found) {
+ _cleanup_free_ char *u = NULL, *v = NULL, *a = NULL, *b = NULL;
+
+ /* We found a device backed by multiple other devices. We don't really support automatic
+ * discovery on such setups, with the exception of dm-verity partitions. In this case there are
+ * two backing devices: the data partition and the hash partition. We are fine with such
+ * setups, however, only if both partitions are on the same physical device. Hence, let's
+ * verify this. */
+
+ u = strjoin(p, "/", de->d_name, "/../dev");
+ if (!u)
+ return -ENOMEM;
+
+ v = strjoin(p, "/", found->d_name, "/../dev");
+ if (!v)
+ return -ENOMEM;
+
+ r = read_one_line_file(u, &a);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to read %s: %m", u);
+ goto fallback;
+ }
+
+ r = read_one_line_file(v, &b);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to read %s: %m", v);
+ goto fallback;
+ }
+
+ /* Check if the parent device is the same. If not, then the two backing devices are on
+ * different physical devices, and we don't support that. */
+ if (!streq(a, b))
+ goto fallback;
+ }
+
+ found = de;
+ }
+
+ if (!found)
+ goto fallback;
+
+ q = strjoina(p, "/", found->d_name, "/dev");
+
+ r = read_one_line_file(q, &t);
+ if (r == -ENOENT)
+ goto fallback;
+ if (r < 0)
+ return r;
+
+ if (sscanf(t, "%u:%u", &maj, &min) != 2)
+ return -EINVAL;
+
+ if (maj == 0)
+ goto fallback;
+
+ *dev = makedev(maj, min);
+ return 1;
+
+fallback:
+ *dev = dt;
+ return 1;
+}
diff --git a/src/basic/blockdev-util.h b/src/basic/blockdev-util.h
new file mode 100644
index 0000000000..642b7ce522
--- /dev/null
+++ b/src/basic/blockdev-util.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/types.h>
+
+#include "macro.h"
+#include "stdio-util.h"
+#include "string-util.h"
+
+#define SYS_BLOCK_PATH_MAX(suffix) \
+ (STRLEN("/sys/dev/block/") + DECIMAL_STR_MAX(dev_t) + 1 + DECIMAL_STR_MAX(dev_t) + strlen_ptr(suffix))
+#define xsprintf_sys_block_path(buf, suffix, devno) \
+ xsprintf(buf, "/sys/dev/block/%u:%u%s", major(devno), minor(devno), strempty(suffix))
+
+int block_get_whole_disk(dev_t d, dev_t *ret);
+
+int get_block_device(const char *path, dev_t *dev);
+
+int get_block_device_harder(const char *path, dev_t *dev);
diff --git a/src/basic/btrfs-ctree.h b/src/basic/btrfs-ctree.h
index 66bdf9736e..c5a4244e37 100644
--- a/src/basic/btrfs-ctree.h
+++ b/src/basic/btrfs-ctree.h
@@ -1,6 +1,7 @@
#pragma once
#include "macro.h"
+#include "missing.h"
#include "sparse-endian.h"
/* Stolen from btrfs' ctree.h */
diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c
index ac96e63531..19d385ab7c 100644
--- a/src/basic/btrfs-util.c
+++ b/src/basic/btrfs-util.c
@@ -38,6 +38,7 @@
#endif
#include "alloc-util.h"
+#include "blockdev-util.h"
#include "btrfs-ctree.h"
#include "btrfs-util.h"
#include "chattr-util.h"
@@ -50,7 +51,6 @@
#include "missing.h"
#include "path-util.h"
#include "rm-rf.h"
-#include "selinux-util.h"
#include "smack-util.h"
#include "sparse-endian.h"
#include "stat-util.h"
@@ -174,24 +174,6 @@ int btrfs_subvol_make(const char *path) {
return 0;
}
-int btrfs_subvol_make_label(const char *path) {
- int r;
-
- assert(path);
-
- r = mac_selinux_create_file_prepare(path, S_IFDIR);
- if (r < 0)
- return r;
-
- r = btrfs_subvol_make(path);
- mac_selinux_create_file_clear();
-
- if (r < 0)
- return r;
-
- return mac_smack_fix(path, false, false);
-}
-
int btrfs_subvol_set_read_only_fd(int fd, bool b) {
uint64_t flags, nflags;
struct stat st;
diff --git a/src/basic/btrfs-util.h b/src/basic/btrfs-util.h
index 2c78daa37b..952b3c26da 100644
--- a/src/basic/btrfs-util.h
+++ b/src/basic/btrfs-util.h
@@ -84,7 +84,6 @@ int btrfs_resize_loopback_fd(int fd, uint64_t size, bool grow_only);
int btrfs_resize_loopback(const char *path, uint64_t size, bool grow_only);
int btrfs_subvol_make(const char *path);
-int btrfs_subvol_make_label(const char *path);
int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlags flags);
int btrfs_subvol_snapshot(const char *old_path, const char *new_path, BtrfsSnapshotFlags flags);
diff --git a/src/basic/build.h b/src/basic/build.h
index ab2a838ce9..0d078efe6f 100644
--- a/src/basic/build.h
+++ b/src/basic/build.h
@@ -140,6 +140,12 @@
#define _IDN_FEATURE_ "-IDN"
#endif
+#if HAVE_PCRE2
+#define _PCRE2_FEATURE_ "+PCRE2"
+#else
+#define _PCRE2_FEATURE_ "-PCRE2"
+#endif
+
#define _CGROUP_HIEARCHY_ "default-hierarchy=" DEFAULT_HIERARCHY_NAME
#define SYSTEMD_FEATURES \
@@ -163,4 +169,5 @@
_KMOD_FEATURE_ " " \
_IDN2_FEATURE_ " " \
_IDN_FEATURE_ " " \
+ _PCRE2_FEATURE_ " " \
_CGROUP_HIEARCHY_
diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c
index e6add0c383..fd78022773 100644
--- a/src/basic/calendarspec.c
+++ b/src/basic/calendarspec.c
@@ -35,6 +35,7 @@
#include "fileio.h"
#include "macro.h"
#include "parse-util.h"
+#include "process-util.h"
#include "string-util.h"
#include "time-util.h"
@@ -156,6 +157,11 @@ static void fix_year(CalendarComponent *c) {
int calendar_spec_normalize(CalendarSpec *c) {
assert(c);
+ if (streq_ptr(c->timezone, "UTC")) {
+ c->utc = true;
+ c->timezone = mfree(c->timezone);
+ }
+
if (c->weekdays_bits <= 0 || c->weekdays_bits >= BITS_WEEKDAYS)
c->weekdays_bits = -1;
@@ -1348,9 +1354,7 @@ typedef struct SpecNextResult {
} SpecNextResult;
int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) {
- pid_t pid;
- SpecNextResult *shared;
- SpecNextResult tmp;
+ SpecNextResult *shared, tmp;
int r;
if (isempty(spec->timezone))
@@ -1360,15 +1364,12 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next)
if (shared == MAP_FAILED)
return negative_errno();
- pid = fork();
-
- if (pid == -1) {
- int fork_errno = errno;
+ r = safe_fork("(sd-calendar)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL);
+ if (r < 0) {
(void) munmap(shared, sizeof *shared);
- return -fork_errno;
+ return r;
}
-
- if (pid == 0) {
+ if (r == 0) {
if (setenv("TZ", spec->timezone, 1) != 0) {
shared->return_value = negative_errno();
_exit(EXIT_FAILURE);
@@ -1381,14 +1382,8 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next)
_exit(EXIT_SUCCESS);
}
- r = wait_for_terminate(pid, NULL);
- if (r < 0) {
- (void) munmap(shared, sizeof *shared);
- return r;
- }
-
tmp = *shared;
- if (munmap(shared, sizeof *shared) != 0)
+ if (munmap(shared, sizeof *shared) < 0)
return negative_errno();
if (tmp.return_value == 0)
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c
index f599d0f0f1..7c0ba92110 100644
--- a/src/basic/cgroup-util.c
+++ b/src/basic/cgroup-util.c
@@ -779,13 +779,11 @@ int cg_create(const char *controller, const char *path) {
if (r < 0)
return r;
- if (mkdir(fs, 0755) < 0) {
-
- if (errno == EEXIST)
- return 0;
-
- return -errno;
- }
+ r = mkdir_errno_wrapper(fs, 0755);
+ if (r == -EEXIST)
+ return 0;
+ if (r < 0)
+ return r;
r = cg_hybrid_unified();
if (r < 0)
diff --git a/src/basic/def.h b/src/basic/def.h
index 77ab735aed..43e7e17008 100644
--- a/src/basic/def.h
+++ b/src/basic/def.h
@@ -53,9 +53,10 @@
"/usr/lib/kbd/keymaps/\0"
#endif
-#define UNIX_SYSTEM_BUS_ADDRESS "unix:path=/var/run/dbus/system_bus_socket"
-#define DEFAULT_SYSTEM_BUS_ADDRESS UNIX_SYSTEM_BUS_ADDRESS
-#define UNIX_USER_BUS_ADDRESS_FMT "unix:path=%s/bus"
+/* Note that we use the new /run prefix here (instead of /var/run) since we require them to be aliases and that way we
+ * become independent of /var being mounted */
+#define DEFAULT_SYSTEM_BUS_ADDRESS "unix:path=/run/dbus/system_bus_socket"
+#define DEFAULT_USER_BUS_ADDRESS_FMT "unix:path=%s/bus"
#define PLYMOUTH_SOCKET { \
.un.sun_family = AF_UNIX, \
diff --git a/src/basic/device-nodes.h b/src/basic/device-nodes.h
index 7dd8a772a5..49f4ccc729 100644
--- a/src/basic/device-nodes.h
+++ b/src/basic/device-nodes.h
@@ -29,11 +29,6 @@
int encode_devnode_name(const char *str, char *str_enc, size_t len);
int whitelisted_char_for_devnode(char c, const char *additional);
-#define SYS_BLOCK_PATH_MAX(suffix) \
- (STRLEN("/sys/dev/block/") + DECIMAL_STR_MAX(dev_t) + 1 + DECIMAL_STR_MAX(dev_t) + strlen_ptr(suffix))
-#define xsprintf_sys_block_path(buf, suffix, devno) \
- xsprintf(buf, "/sys/dev/block/%u:%u%s", major(devno), minor(devno), strempty(suffix))
-
#define DEV_NUM_PATH_MAX \
(STRLEN("/dev/block/") + DECIMAL_STR_MAX(dev_t) + 1 + DECIMAL_STR_MAX(dev_t))
#define xsprintf_dev_num_path(buf, type, devno) \
diff --git a/src/basic/errno-list.c b/src/basic/errno-list.c
index d8eedfc0ad..7eb28b8fd1 100644
--- a/src/basic/errno-list.c
+++ b/src/basic/errno-list.c
@@ -18,6 +18,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
#include <string.h>
#include "errno-list.h"
diff --git a/src/basic/errno-list.h b/src/basic/errno-list.h
index 4e9b75a7ea..38beaf96dd 100644
--- a/src/basic/errno-list.h
+++ b/src/basic/errno-list.h
@@ -20,6 +20,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <stdbool.h>
/*
* MAX_ERRNO is defined as 4095 in linux/err.h
* We use the same value here.
@@ -28,3 +29,6 @@
const char *errno_to_name(int id);
int errno_from_name(const char *name);
+static inline bool errno_is_valid(int n) {
+ return n > 0 && n <= ERRNO_MAX;
+}
diff --git a/src/basic/ether-addr-util.c b/src/basic/ether-addr-util.c
index bbe8bf0006..bfbd1a4931 100644
--- a/src/basic/ether-addr-util.c
+++ b/src/basic/ether-addr-util.c
@@ -18,6 +18,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
#include <net/ethernet.h>
#include <stdio.h>
#include <sys/types.h>
diff --git a/src/basic/exec-util.c b/src/basic/exec-util.c
index 82ccbdf5ec..0829b3d836 100644
--- a/src/basic/exec-util.c
+++ b/src/basic/exec-util.c
@@ -48,27 +48,26 @@ assert_cc(EAGAIN == EWOULDBLOCK);
static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) {
pid_t _pid;
+ int r;
if (null_or_empty_path(path)) {
log_debug("%s is empty (a mask).", path);
return 0;
}
- _pid = fork();
- if (_pid < 0)
- return log_error_errno(errno, "Failed to fork: %m");
- if (_pid == 0) {
+ r = safe_fork("(direxec)", FORK_DEATHSIG|FORK_LOG, &_pid);
+ if (r < 0)
+ return r;
+ if (r == 0) {
char *_argv[2];
- assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
if (stdout_fd >= 0) {
/* If the fd happens to be in the right place, go along with that */
if (stdout_fd != STDOUT_FILENO &&
dup2(stdout_fd, STDOUT_FILENO) < 0)
return -errno;
- fd_cloexec(STDOUT_FILENO, false);
+ (void) fd_cloexec(STDOUT_FILENO, false);
}
if (!argv) {
@@ -83,7 +82,6 @@ static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) {
_exit(EXIT_FAILURE);
}
- log_debug("Spawned %s as " PID_FMT ".", path, _pid);
*pid = _pid;
return 1;
}
@@ -107,11 +105,6 @@ static int do_execute(
* If callbacks is nonnull, execution is serial. Otherwise, we default to parallel.
*/
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
-
- assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE, (const char* const*) directories);
if (r < 0)
return r;
@@ -153,7 +146,7 @@ static int do_execute(
return log_oom();
t = NULL;
} else {
- r = wait_for_terminate_and_warn(t, pid, true);
+ r = wait_for_terminate_and_check(t, pid, WAIT_LOG);
if (r < 0)
continue;
@@ -183,7 +176,7 @@ static int do_execute(
t = hashmap_remove(pids, PID_TO_PTR(pid));
assert(t);
- wait_for_terminate_and_warn(t, pid, true);
+ (void) wait_for_terminate_and_check(t, pid, WAIT_LOG);
}
return 0;
@@ -196,10 +189,9 @@ int execute_directories(
void* const callback_args[_STDOUT_CONSUME_MAX],
char *argv[]) {
- pid_t executor_pid;
- char *name;
char **dirs = (char**) directories;
_cleanup_close_ int fd = -1;
+ char *name;
int r;
assert(!strv_isempty(dirs));
@@ -222,24 +214,14 @@ int execute_directories(
* them to finish. Optionally a timeout is applied. If a file with the same name
* exists in more than one directory, the earliest one wins. */
- executor_pid = fork();
- if (executor_pid < 0)
- return log_error_errno(errno, "Failed to fork: %m");
-
- if (executor_pid == 0) {
+ r = safe_fork("(sd-executor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
+ if (r < 0)
+ return r;
+ if (r == 0) {
r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv);
_exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
- r = wait_for_terminate_and_warn(name, executor_pid, true);
- if (r < 0)
- return log_error_errno(r, "Execution failed: %m");
- if (r > 0) {
- /* non-zero return code from child */
- log_error("Forker process failed.");
- return -EREMOTEIO;
- }
-
if (!callbacks)
return 0;
diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c
index a4a5c6b3f6..404361e8c1 100644
--- a/src/basic/fd-util.c
+++ b/src/basic/fd-util.c
@@ -192,9 +192,9 @@ int fd_cloexec(int fd, bool cloexec) {
}
void stdio_unset_cloexec(void) {
- fd_cloexec(STDIN_FILENO, false);
- fd_cloexec(STDOUT_FILENO, false);
- fd_cloexec(STDERR_FILENO, false);
+ (void) fd_cloexec(STDIN_FILENO, false);
+ (void) fd_cloexec(STDOUT_FILENO, false);
+ (void) fd_cloexec(STDERR_FILENO, false);
}
_pure_ static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) {
@@ -227,20 +227,21 @@ int close_all_fds(const int except[], unsigned n_except) {
assert_se(getrlimit(RLIMIT_NOFILE, &rl) >= 0);
for (fd = 3; fd < (int) rl.rlim_max; fd ++) {
+ int q;
if (fd_in_set(fd, except, n_except))
continue;
- if (close_nointr(fd) < 0)
- if (errno != EBADF && r == 0)
- r = -errno;
+ q = close_nointr(fd);
+ if (q < 0 && q != -EBADF && r >= 0)
+ r = q;
}
return r;
}
FOREACH_DIRENT(de, d, return -errno) {
- int fd = -1;
+ int fd = -1, q;
if (safe_atoi(de->d_name, &fd) < 0)
/* Let's better ignore this, just in case */
@@ -255,11 +256,9 @@ int close_all_fds(const int except[], unsigned n_except) {
if (fd_in_set(fd, except, n_except))
continue;
- if (close_nointr(fd) < 0) {
- /* Valgrind has its own FD and doesn't want to have it closed */
- if (errno != EBADF && r == 0)
- r = -errno;
- }
+ q = close_nointr(fd);
+ if (q < 0 && q != -EBADF && r >= 0) /* Valgrind has its own FD and doesn't want to have it closed */
+ r = q;
}
return r;
diff --git a/src/basic/fileio.c b/src/basic/fileio.c
index 4e02d5b344..26d6174664 100644
--- a/src/basic/fileio.c
+++ b/src/basic/fileio.c
@@ -62,12 +62,30 @@ int write_string_stream_ts(
WriteStringFileFlags flags,
struct timespec *ts) {
+ bool needs_nl;
+
assert(f);
assert(line);
- fputs(line, f);
- if (!(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n"))
- fputc('\n', f);
+ if (ferror(f))
+ return -EIO;
+
+ needs_nl = !(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n");
+
+ if (needs_nl && (flags & WRITE_STRING_FILE_DISABLE_BUFFER)) {
+ /* If STDIO buffering was disabled, then let's append the newline character to the string itself, so
+ * that the write goes out in one go, instead of two */
+
+ line = strjoina(line, "\n");
+ needs_nl = false;
+ }
+
+ if (fputs(line, f) == EOF)
+ return -errno;
+
+ if (needs_nl)
+ if (fputc('\n', f) == EOF)
+ return -errno;
if (ts) {
struct timespec twice[2] = {*ts, *ts};
@@ -908,14 +926,16 @@ int write_env_file(const char *fname, char **l) {
}
int executable_is_script(const char *path, char **interpreter) {
- int r;
_cleanup_free_ char *line = NULL;
- int len;
+ size_t len;
char *ans;
+ int r;
assert(path);
r = read_one_line_file(path, &line);
+ if (r == -ENOBUFS) /* First line overly long? if so, then it's not a script */
+ return 0;
if (r < 0)
return r;
@@ -1205,8 +1225,7 @@ int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
if (!filename_is_valid(fn))
return -EINVAL;
- if (!extra)
- extra = "";
+ extra = strempty(extra);
t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1);
if (!t)
@@ -1239,8 +1258,7 @@ int tempfn_random(const char *p, const char *extra, char **ret) {
if (!filename_is_valid(fn))
return -EINVAL;
- if (!extra)
- extra = "";
+ extra = strempty(extra);
t = new(char, strlen(p) + 2 + strlen(extra) + 16 + 1);
if (!t)
@@ -1280,8 +1298,7 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) {
return r;
}
- if (!extra)
- extra = "";
+ extra = strempty(extra);
t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1);
if (!t)
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index 4ca36faf09..a8e50d4c78 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -39,6 +39,7 @@
#include "mkdir.h"
#include "parse-util.h"
#include "path-util.h"
+#include "process-util.h"
#include "stat-util.h"
#include "stdio-util.h"
#include "string-util.h"
@@ -315,43 +316,60 @@ int fd_warn_permissions(const char *path, int fd) {
}
int touch_file(const char *path, bool parents, usec_t stamp, uid_t uid, gid_t gid, mode_t mode) {
- _cleanup_close_ int fd;
- int r;
+ char fdpath[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
+ _cleanup_close_ int fd = -1;
+ int r, ret = 0;
assert(path);
- if (parents)
- mkdir_parents(path, 0755);
-
- fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY,
- IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode);
- if (fd < 0)
- return -errno;
+ /* Note that touch_file() does not follow symlinks: if invoked on an existing symlink, then it is the symlink
+ * itself which is updated, not its target
+ *
+ * Returns the first error we encounter, but tries to apply as much as possible. */
- if (mode != MODE_INVALID) {
- r = fchmod(fd, mode);
- if (r < 0)
+ if (parents)
+ (void) mkdir_parents(path, 0755);
+
+ /* Initially, we try to open the node with O_PATH, so that we get a reference to the node. This is useful in
+ * case the path refers to an existing device or socket node, as we can open it successfully in all cases, and
+ * won't trigger any driver magic or so. */
+ fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW);
+ if (fd < 0) {
+ if (errno != ENOENT)
return -errno;
- }
- if (uid != UID_INVALID || gid != GID_INVALID) {
- r = fchown(fd, uid, gid);
- if (r < 0)
+ /* if the node doesn't exist yet, we create it, but with O_EXCL, so that we only create a regular file
+ * here, and nothing else */
+ fd = open(path, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, IN_SET(mode, 0, MODE_INVALID) ? 0644 : mode);
+ if (fd < 0)
return -errno;
}
+ /* Let's make a path from the fd, and operate on that. With this logic, we can adjust the access mode,
+ * ownership and time of the file node in all cases, even if the fd refers to an O_PATH object — which is
+ * something fchown(), fchmod(), futimensat() don't allow. */
+ xsprintf(fdpath, "/proc/self/fd/%i", fd);
+
+ if (mode != MODE_INVALID)
+ if (chmod(fdpath, mode) < 0)
+ ret = -errno;
+
+ if (uid_is_valid(uid) || gid_is_valid(gid))
+ if (chown(fdpath, uid, gid) < 0 && ret >= 0)
+ ret = -errno;
+
if (stamp != USEC_INFINITY) {
struct timespec ts[2];
timespec_store(&ts[0], stamp);
ts[1] = ts[0];
- r = futimens(fd, ts);
+ r = utimensat(AT_FDCWD, fdpath, ts, 0);
} else
- r = futimens(fd, NULL);
- if (r < 0)
+ r = utimensat(AT_FDCWD, fdpath, NULL, 0);
+ if (r < 0 && ret >= 0)
return -errno;
- return 0;
+ return ret;
}
int touch(const char *path) {
@@ -593,16 +611,35 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
return r;
}
+static bool safe_transition(const struct stat *a, const struct stat *b) {
+ /* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to
+ * privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files
+ * making us believe we read something safe even though it isn't safe in the specific context we open it in. */
+
+ if (a->st_uid == 0) /* Transitioning from privileged to unprivileged is always fine */
+ return true;
+
+ return a->st_uid == b->st_uid; /* Otherwise we need to stay within the same UID */
+}
+
int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret) {
_cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL;
_cleanup_close_ int fd = -1;
unsigned max_follow = 32; /* how many symlinks to follow before giving up and returning ELOOP */
+ struct stat previous_stat;
bool exists = true;
char *todo;
int r;
assert(path);
+ /* Either the file may be missing, or we return an fd to the final object, but both make no sense */
+ if ((flags & (CHASE_NONEXISTENT|CHASE_OPEN)) == (CHASE_NONEXISTENT|CHASE_OPEN))
+ return -EINVAL;
+
+ if (isempty(path))
+ return -EINVAL;
+
/* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following
* symlinks relative to a root directory, instead of the root of the host.
*
@@ -623,13 +660,23 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
* function what to do when encountering a symlink with an absolute path as directory: prefix it by the
* specified path. */
+ /* A root directory of "/" or "" is identical to none */
+ if (isempty(original_root) || path_equal(original_root, "/"))
+ original_root = NULL;
+
if (original_root) {
r = path_make_absolute_cwd(original_root, &root);
if (r < 0)
return r;
- if (flags & CHASE_PREFIX_ROOT)
+ if (flags & CHASE_PREFIX_ROOT) {
+
+ /* We don't support relative paths in combination with a root directory */
+ if (!path_is_absolute(path))
+ return -EINVAL;
+
path = prefix_roota(root, path);
+ }
}
r = path_make_absolute_cwd(path, &buffer);
@@ -640,6 +687,11 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
if (fd < 0)
return -errno;
+ if (flags & CHASE_SAFE) {
+ if (fstat(fd, &previous_stat) < 0)
+ return -errno;
+ }
+
todo = buffer;
for (;;) {
_cleanup_free_ char *first = NULL;
@@ -678,7 +730,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
/* Two dots? Then chop off the last bit of what we already found out. */
if (path_equal(first, "/..")) {
_cleanup_free_ char *parent = NULL;
- int fd_parent = -1;
+ _cleanup_close_ int fd_parent = -1;
/* If we already are at the top, then going up will not change anything. This is in-line with
* how the kernel handles this. */
@@ -701,8 +753,19 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
if (fd_parent < 0)
return -errno;
+ if (flags & CHASE_SAFE) {
+ if (fstat(fd_parent, &st) < 0)
+ return -errno;
+
+ if (!safe_transition(&previous_stat, &st))
+ return -EPERM;
+
+ previous_stat = st;
+ }
+
safe_close(fd);
fd = fd_parent;
+ fd_parent = -1;
continue;
}
@@ -735,6 +798,12 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
if (fstat(child, &st) < 0)
return -errno;
+ if ((flags & CHASE_SAFE) &&
+ !safe_transition(&previous_stat, &st))
+ return -EPERM;
+
+ previous_stat = st;
+
if ((flags & CHASE_NO_AUTOFS) &&
fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0)
return -EREMOTE;
@@ -765,6 +834,16 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
if (fd < 0)
return -errno;
+ if (flags & CHASE_SAFE) {
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (!safe_transition(&previous_stat, &st))
+ return -EPERM;
+
+ previous_stat = st;
+ }
+
free(done);
/* Note that we do not revalidate the root, we take it as is. */
@@ -821,6 +900,19 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
done = NULL;
}
+ if (flags & CHASE_OPEN) {
+ int q;
+
+ /* Return the O_PATH fd we currently are looking to the caller. It can translate it to a proper fd by
+ * opening /proc/self/fd/xyz. */
+
+ assert(fd >= 0);
+ q = fd;
+ fd = -1;
+
+ return q;
+ }
+
return exists;
}
diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h
index a7ba61625d..4dba1ea56a 100644
--- a/src/basic/fs-util.h
+++ b/src/basic/fs-util.h
@@ -29,6 +29,7 @@
#include <unistd.h>
#include "time-util.h"
+#include "util.h"
int unlink_noerrno(const char *path);
@@ -80,22 +81,25 @@ union inotify_event_buffer {
int inotify_add_watch_fd(int fd, int what, uint32_t mask);
enum {
- CHASE_PREFIX_ROOT = 1, /* If set, the specified path will be prefixed by the specified root before beginning the iteration */
- CHASE_NONEXISTENT = 2, /* If set, it's OK if the path doesn't actually exist. */
- CHASE_NO_AUTOFS = 4, /* If set, return -EREMOTE if autofs mount point found */
+ CHASE_PREFIX_ROOT = 1U << 0, /* If set, the specified path will be prefixed by the specified root before beginning the iteration */
+ CHASE_NONEXISTENT = 1U << 1, /* If set, it's OK if the path doesn't actually exist. */
+ CHASE_NO_AUTOFS = 1U << 2, /* If set, return -EREMOTE if autofs mount point found */
+ CHASE_SAFE = 1U << 3, /* If set, return EPERM if we ever traverse from unprivileged to privileged files or directories */
+ CHASE_OPEN = 1U << 4, /* If set, return an O_PATH object to the final component */
};
int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret);
/* Useful for usage with _cleanup_(), removes a directory and frees the pointer */
static inline void rmdir_and_free(char *p) {
+ PROTECT_ERRNO;
(void) rmdir(p);
free(p);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rmdir_and_free);
static inline void unlink_and_free(char *p) {
- (void) unlink(p);
+ (void) unlink_noerrno(p);
free(p);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free);
diff --git a/src/basic/gcrypt-util.c b/src/basic/gcrypt-util.c
new file mode 100644
index 0000000000..1bfb776725
--- /dev/null
+++ b/src/basic/gcrypt-util.c
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2012 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#if HAVE_GCRYPT
+#include <gcrypt.h>
+
+#include "gcrypt-util.h"
+#include "hexdecoct.h"
+
+void initialize_libgcrypt(bool secmem) {
+ const char *p;
+ if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
+ return;
+
+ p = gcry_check_version("1.4.5");
+ assert(p);
+
+ /* Turn off "secmem". Clients which wish to make use of this
+ * feature should initialize the library manually */
+ if (!secmem)
+ gcry_control(GCRYCTL_DISABLE_SECMEM);
+ gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
+}
+
+int string_hashsum(const char *s, size_t len, int md_algorithm, char **out) {
+ gcry_md_hd_t md = NULL;
+ size_t hash_size;
+ void *hash;
+ char *enc;
+
+ initialize_libgcrypt(false);
+
+ hash_size = gcry_md_get_algo_dlen(md_algorithm);
+ assert(hash_size > 0);
+
+ gcry_md_open(&md, md_algorithm, 0);
+ if (!md)
+ return -EIO;
+
+ gcry_md_write(md, s, len);
+
+ hash = gcry_md_read(md, 0);
+ if (!hash)
+ return -EIO;
+
+ enc = hexmem(hash, hash_size);
+ if (!enc)
+ return -ENOMEM;
+
+ *out = enc;
+ return 0;
+}
+#endif
diff --git a/src/basic/gcrypt-util.h b/src/basic/gcrypt-util.h
new file mode 100644
index 0000000000..69faf08e56
--- /dev/null
+++ b/src/basic/gcrypt-util.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Zbigniew Jędrzejewski-Szmek
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#if HAVE_GCRYPT
+#include <gcrypt.h>
+
+void initialize_libgcrypt(bool secmem);
+int string_hashsum(const char *s, size_t len, int md_algorithm, char **out);
+#endif
+
+static inline int string_hashsum_sha224(const char *s, size_t len, char **out) {
+#if HAVE_GCRYPT
+ return string_hashsum(s, len, GCRY_MD_SHA224, out);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
+
+static inline int string_hashsum_sha256(const char *s, size_t len, char **out) {
+#if HAVE_GCRYPT
+ return string_hashsum(s, len, GCRY_MD_SHA256, out);
+#else
+ return -EOPNOTSUPP;
+#endif
+}
diff --git a/src/basic/generate-af-list.sh b/src/basic/generate-af-list.sh
index 8d9cdd1836..fa74198e58 100755
--- a/src/basic/generate-af-list.sh
+++ b/src/basic/generate-af-list.sh
@@ -1,4 +1,5 @@
-#!/bin/sh -eu
+#!/bin/sh
+set -eu
$1 -E -dM -include sys/socket.h - </dev/null | \
grep -Ev 'AF_UNSPEC|AF_MAX' | \
diff --git a/src/basic/generate-arphrd-list.sh b/src/basic/generate-arphrd-list.sh
index ee207fb38e..e4e7271c4d 100755
--- a/src/basic/generate-arphrd-list.sh
+++ b/src/basic/generate-arphrd-list.sh
@@ -1,4 +1,5 @@
-#!/bin/sh -eu
+#!/bin/sh
+set -eu
$1 -dM -include net/if_arp.h - </dev/null | \
awk '/^#define[ \t]+ARPHRD_[^ \t]+[ \t]+[^ \t]/ { print $2; }' | \
diff --git a/src/basic/generate-cap-list.sh b/src/basic/generate-cap-list.sh
index 1d4a562e7c..0628d2425f 100755
--- a/src/basic/generate-cap-list.sh
+++ b/src/basic/generate-cap-list.sh
@@ -1,4 +1,5 @@
-#!/bin/sh -eu
+#!/bin/sh
+set -eu
$1 -dM -include linux/capability.h -include "$2" -include "$3" - </dev/null | \
awk '/^#define[ \t]+CAP_[A-Z_]+[ \t]+/ { print $2; }' | \
diff --git a/src/basic/generate-errno-list.sh b/src/basic/generate-errno-list.sh
index e2bab8b320..953d5e37b8 100755
--- a/src/basic/generate-errno-list.sh
+++ b/src/basic/generate-errno-list.sh
@@ -1,4 +1,5 @@
-#!/bin/sh -eu
+#!/bin/sh
+set -eu
$1 -dM -include errno.h - </dev/null | \
awk '/^#define[ \t]+E[^ _]+[ \t]+/ { print $2; }'
diff --git a/src/basic/generate-socket-protocol-list.sh b/src/basic/generate-socket-protocol-list.sh
new file mode 100755
index 0000000000..a9b1e0fb57
--- /dev/null
+++ b/src/basic/generate-socket-protocol-list.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+set -eu
+
+$1 -dM -include netinet/in.h - </dev/null | \
+ awk '/^#define[ \t]+IPPROTO_[^ \t]+[ \t]+[^ \t]/ { print $2; }' | \
+ sed -e 's/IPPROTO_//'
diff --git a/src/basic/gunicode.c b/src/basic/gunicode.c
index e6ac0545a4..8aff4a0fc5 100644
--- a/src/basic/gunicode.c
+++ b/src/basic/gunicode.c
@@ -4,8 +4,6 @@
* Copyright 2000, 2005 Red Hat, Inc.
*/
-#include <stdlib.h>
-
#include "gunicode.h"
#define unichar uint32_t
diff --git a/src/basic/hash-funcs.c b/src/basic/hash-funcs.c
index e69f81558d..5267758769 100644
--- a/src/basic/hash-funcs.c
+++ b/src/basic/hash-funcs.c
@@ -19,6 +19,8 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <string.h>
+
#include "hash-funcs.h"
void string_hash_func(const void *p, struct siphash *state) {
diff --git a/src/basic/hostname-util.h b/src/basic/hostname-util.h
index d837d6f28c..edae52e3db 100644
--- a/src/basic/hostname-util.h
+++ b/src/basic/hostname-util.h
@@ -21,6 +21,7 @@
***/
#include <stdbool.h>
+#include <stdio.h>
#include "macro.h"
diff --git a/src/basic/io-util.c b/src/basic/io-util.c
index 77c9bdc739..08ad42ed95 100644
--- a/src/basic/io-util.c
+++ b/src/basic/io-util.c
@@ -33,6 +33,7 @@ int flush_fd(int fd) {
.fd = fd,
.events = POLLIN,
};
+ int count = 0;
/* Read from the specified file descriptor, until POLLIN is not set anymore, throwing away everything
* read. Note that some file descriptors (notable IP sockets) will trigger POLLIN even when no data can be read
@@ -52,7 +53,7 @@ int flush_fd(int fd) {
return -errno;
} else if (r == 0)
- return 0;
+ return count;
l = read(fd, buf, sizeof(buf));
if (l < 0) {
@@ -61,11 +62,13 @@ int flush_fd(int fd) {
continue;
if (errno == EAGAIN)
- return 0;
+ return count;
return -errno;
} else if (l == 0)
- return 0;
+ return count;
+
+ count += (int) l;
}
}
diff --git a/src/basic/journal-importer.c b/src/basic/journal-importer.c
index 6942c370cb..11054589e3 100644
--- a/src/basic/journal-importer.c
+++ b/src/basic/journal-importer.c
@@ -18,6 +18,7 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <errno.h>
#include <unistd.h>
#include "alloc-util.h"
diff --git a/src/basic/label.c b/src/basic/label.c
index ce81d286ab..18c9a23fea 100644
--- a/src/basic/label.c
+++ b/src/basic/label.c
@@ -22,6 +22,7 @@
#include <sys/stat.h>
#include <unistd.h>
+#include "btrfs-util.h"
#include "label.h"
#include "macro.h"
#include "selinux-util.h"
@@ -41,16 +42,17 @@ int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
return 0;
}
-int mkdir_label(const char *path, mode_t mode) {
+int symlink_label(const char *old_path, const char *new_path) {
int r;
- assert(path);
+ assert(old_path);
+ assert(new_path);
- r = mac_selinux_create_file_prepare(path, S_IFDIR);
+ r = mac_selinux_create_file_prepare(new_path, S_IFLNK);
if (r < 0)
return r;
- if (mkdir(path, mode) < 0)
+ if (symlink(old_path, new_path) < 0)
r = -errno;
mac_selinux_create_file_clear();
@@ -58,26 +60,23 @@ int mkdir_label(const char *path, mode_t mode) {
if (r < 0)
return r;
- return mac_smack_fix(path, false, false);
+ return mac_smack_fix(new_path, false, false);
}
-int symlink_label(const char *old_path, const char *new_path) {
+int btrfs_subvol_make_label(const char *path) {
int r;
- assert(old_path);
- assert(new_path);
+ assert(path);
- r = mac_selinux_create_file_prepare(new_path, S_IFLNK);
+ r = mac_selinux_create_file_prepare(path, S_IFDIR);
if (r < 0)
return r;
- if (symlink(old_path, new_path) < 0)
- r = -errno;
-
+ r = btrfs_subvol_make(path);
mac_selinux_create_file_clear();
if (r < 0)
return r;
- return mac_smack_fix(new_path, false, false);
+ return mac_smack_fix(path, false, false);
}
diff --git a/src/basic/label.h b/src/basic/label.h
index 86447c2e98..d73dacec4f 100644
--- a/src/basic/label.h
+++ b/src/basic/label.h
@@ -27,3 +27,5 @@ int label_fix(const char *path, bool ignore_enoent, bool ignore_erofs);
int mkdir_label(const char *path, mode_t mode);
int symlink_label(const char *old_path, const char *new_path);
+
+int btrfs_subvol_make_label(const char *path);
diff --git a/src/basic/locale-util.h b/src/basic/locale-util.h
index 60ce017a15..f75dcbc3d1 100644
--- a/src/basic/locale-util.h
+++ b/src/basic/locale-util.h
@@ -22,6 +22,7 @@
#include <libintl.h>
#include <stdbool.h>
+#include <locale.h>
#include "macro.h"
@@ -75,3 +76,10 @@ LocaleVariable locale_variable_from_string(const char *s) _pure_;
int get_keymaps(char ***l);
bool keymap_is_valid(const char *name);
+
+static inline void freelocalep(locale_t *p) {
+ if (*p == (locale_t) 0)
+ return;
+
+ freelocale(*p);
+}
diff --git a/src/basic/log.c b/src/basic/log.c
index 20d9588e2f..12ee65a5c3 100644
--- a/src/basic/log.c
+++ b/src/basic/log.c
@@ -54,6 +54,7 @@
#include "syslog-util.h"
#include "terminal-util.h"
#include "time-util.h"
+#include "utf8.h"
#include "util.h"
#define SNDBUF_SIZE (8*1024*1024)
@@ -76,40 +77,40 @@ static bool show_location = false;
static bool upgrade_syslog_to_journal = false;
static bool always_reopen_console = false;
static bool open_when_needed = false;
+static bool prohibit_ipc = false;
/* Akin to glibc's __abort_msg; which is private and we hence cannot
* use here. */
static char *log_abort_msg = NULL;
-void log_close_console(void) {
+static void log_close_console(void) {
if (console_fd < 0)
return;
- if (getpid_cached() == 1) {
- if (console_fd >= 3)
- safe_close(console_fd);
+ if (console_fd >= 3)
+ safe_close(console_fd);
- console_fd = -1;
- }
+ console_fd = -1;
}
static int log_open_console(void) {
- if (console_fd >= 0)
+ if (!always_reopen_console) {
+ console_fd = STDERR_FILENO;
return 0;
+ }
- if (always_reopen_console) {
+ if (console_fd < 3) {
console_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
if (console_fd < 0)
return console_fd;
- } else
- console_fd = STDERR_FILENO;
+ }
return 0;
}
-void log_close_kmsg(void) {
+static void log_close_kmsg(void) {
kmsg_fd = safe_close(kmsg_fd);
}
@@ -125,7 +126,7 @@ static int log_open_kmsg(void) {
return 0;
}
-void log_close_syslog(void) {
+static void log_close_syslog(void) {
syslog_fd = safe_close(syslog_fd);
}
@@ -197,7 +198,7 @@ fail:
return r;
}
-void log_close_journal(void) {
+static void log_close_journal(void) {
journal_fd = safe_close(journal_fd);
}
@@ -239,7 +240,8 @@ int log_open(void) {
/* If we don't use the console we close it here, to not get
* killed by SAK. If we don't use syslog we close it here so
* that we are not confused by somebody deleting the socket in
- * the fs. If we don't use /dev/kmsg we still keep it open,
+ * the fs, and to make sure we don't use it if prohibit_ipc is
+ * set. If we don't use /dev/kmsg we still keep it open,
* because there is no reason to close it. */
if (log_target == LOG_TARGET_NULL) {
@@ -249,11 +251,12 @@ int log_open(void) {
return 0;
}
- if (!IN_SET(log_target, LOG_TARGET_AUTO, LOG_TARGET_SAFE) ||
+ if (log_target != LOG_TARGET_AUTO ||
getpid_cached() == 1 ||
isatty(STDERR_FILENO) <= 0) {
- if (IN_SET(log_target, LOG_TARGET_AUTO,
+ if (!prohibit_ipc &&
+ IN_SET(log_target, LOG_TARGET_AUTO,
LOG_TARGET_JOURNAL_OR_KMSG,
LOG_TARGET_JOURNAL)) {
r = log_open_journal();
@@ -264,7 +267,8 @@ int log_open(void) {
}
}
- if (IN_SET(log_target, LOG_TARGET_SYSLOG_OR_KMSG,
+ if (!prohibit_ipc &&
+ IN_SET(log_target, LOG_TARGET_SYSLOG_OR_KMSG,
LOG_TARGET_SYSLOG)) {
r = log_open_syslog();
if (r >= 0) {
@@ -275,7 +279,6 @@ int log_open(void) {
}
if (IN_SET(log_target, LOG_TARGET_AUTO,
- LOG_TARGET_SAFE,
LOG_TARGET_JOURNAL_OR_KMSG,
LOG_TARGET_SYSLOG_OR_KMSG,
LOG_TARGET_KMSG)) {
@@ -627,7 +630,6 @@ int log_dispatch_internal(
if (k <= 0 &&
IN_SET(log_target, LOG_TARGET_AUTO,
- LOG_TARGET_SAFE,
LOG_TARGET_SYSLOG_OR_KMSG,
LOG_TARGET_JOURNAL_OR_KMSG,
LOG_TARGET_KMSG)) {
@@ -1219,17 +1221,18 @@ static const char *const log_target_table[_LOG_TARGET_MAX] = {
[LOG_TARGET_SYSLOG] = "syslog",
[LOG_TARGET_SYSLOG_OR_KMSG] = "syslog-or-kmsg",
[LOG_TARGET_AUTO] = "auto",
- [LOG_TARGET_SAFE] = "safe",
- [LOG_TARGET_NULL] = "null"
+ [LOG_TARGET_NULL] = "null",
};
DEFINE_STRING_TABLE_LOOKUP(log_target, LogTarget);
void log_received_signal(int level, const struct signalfd_siginfo *si) {
- if (si->ssi_pid > 0) {
+ assert(si);
+
+ if (pid_is_valid(si->ssi_pid)) {
_cleanup_free_ char *p = NULL;
- get_process_comm(si->ssi_pid, &p);
+ (void) get_process_comm(si->ssi_pid, &p);
log_full(level,
"Received SIG%s from PID %"PRIu32" (%s).",
@@ -1239,7 +1242,6 @@ void log_received_signal(int level, const struct signalfd_siginfo *si) {
log_full(level,
"Received SIG%s.",
signal_to_string(si->ssi_signo));
-
}
int log_syntax_internal(
@@ -1289,8 +1291,37 @@ int log_syntax_internal(
NULL);
}
+int log_syntax_invalid_utf8_internal(
+ const char *unit,
+ int level,
+ const char *config_file,
+ unsigned config_line,
+ const char *file,
+ int line,
+ const char *func,
+ const char *rvalue) {
+
+ _cleanup_free_ char *p = NULL;
+
+ if (rvalue)
+ p = utf8_escape_invalid(rvalue);
+
+ log_syntax_internal(unit, level, config_file, config_line, 0, file, line, func,
+ "String is not UTF-8 clean, ignoring assignment: %s", strna(p));
+
+ return -EINVAL;
+}
+
void log_set_upgrade_syslog_to_journal(bool b) {
upgrade_syslog_to_journal = b;
+
+ /* Make the change effective immediately */
+ if (b) {
+ if (log_target == LOG_TARGET_SYSLOG)
+ log_target = LOG_TARGET_JOURNAL;
+ else if (log_target == LOG_TARGET_SYSLOG_OR_KMSG)
+ log_target = LOG_TARGET_JOURNAL_OR_KMSG;
+ }
}
void log_set_always_reopen_console(bool b) {
@@ -1300,3 +1331,14 @@ void log_set_always_reopen_console(bool b) {
void log_set_open_when_needed(bool b) {
open_when_needed = b;
}
+
+void log_set_prohibit_ipc(bool b) {
+ prohibit_ipc = b;
+}
+
+int log_emergency_level(void) {
+ /* Returns the log level to use for log_emergency() logging. We use LOG_EMERG only when we are PID 1, as only
+ * then the system of the whole system is obviously affected. */
+
+ return getpid_cached() == 1 ? LOG_EMERG : LOG_ERR;
+}
diff --git a/src/basic/log.h b/src/basic/log.h
index aa5976c3c0..5b5a25bd6d 100644
--- a/src/basic/log.h
+++ b/src/basic/log.h
@@ -20,18 +20,16 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
-#include <sys/signalfd.h>
-#include <sys/socket.h>
#include <syslog.h>
-#include "sd-id128.h"
-
#include "macro.h"
-#include "process-util.h"
+
+/* Some structures we reference but don't want to pull in headers for */
+struct iovec;
+struct signalfd_siginfo;
typedef enum LogRealm {
LOG_REALM_SYSTEMD,
@@ -52,7 +50,6 @@ typedef enum LogTarget{
LOG_TARGET_SYSLOG,
LOG_TARGET_SYSLOG_OR_KMSG,
LOG_TARGET_AUTO, /* console if stderr is tty, JOURNAL_OR_KMSG otherwise */
- LOG_TARGET_SAFE, /* console if stderr is tty, KMSG otherwise */
LOG_TARGET_NULL,
_LOG_TARGET_MAX,
_LOG_TARGET_INVALID = -1
@@ -97,11 +94,6 @@ int log_open(void);
void log_close(void);
void log_forget_fds(void);
-void log_close_syslog(void);
-void log_close_journal(void);
-void log_close_kmsg(void);
-void log_close_console(void);
-
void log_parse_environment_realm(LogRealm realm);
#define log_parse_environment() \
log_parse_environment_realm(LOG_REALM)
@@ -194,7 +186,7 @@ int log_struct_iovec_internal(
const char *file,
int line,
const char *func,
- const struct iovec input_iovec[],
+ const struct iovec *input_iovec,
size_t n_input_iovec);
/* This modifies the buffer passed! */
@@ -240,9 +232,9 @@ void log_assert_failed_return_realm(
/* Logging with level */
#define log_full_errno_realm(realm, level, error, ...) \
({ \
- int _level = (level), _e = (error); \
- (log_get_max_level_realm((realm)) >= LOG_PRI(_level)) \
- ? log_internal_realm(LOG_REALM_PLUS_LEVEL((realm), _level), _e, \
+ int _level = (level), _e = (error), _realm = (realm); \
+ (log_get_max_level_realm(_realm) >= LOG_PRI(_level)) \
+ ? log_internal_realm(LOG_REALM_PLUS_LEVEL(_realm, _level), _e, \
__FILE__, __LINE__, __func__, __VA_ARGS__) \
: -abs(_e); \
})
@@ -252,13 +244,15 @@ void log_assert_failed_return_realm(
#define log_full(level, ...) log_full_errno((level), 0, __VA_ARGS__)
+int log_emergency_level(void);
+
/* Normal logging */
#define log_debug(...) log_full(LOG_DEBUG, __VA_ARGS__)
#define log_info(...) log_full(LOG_INFO, __VA_ARGS__)
#define log_notice(...) log_full(LOG_NOTICE, __VA_ARGS__)
#define log_warning(...) log_full(LOG_WARNING, __VA_ARGS__)
#define log_error(...) log_full(LOG_ERR, __VA_ARGS__)
-#define log_emergency(...) log_full(getpid_cached() == 1 ? LOG_EMERG : LOG_ERR, __VA_ARGS__)
+#define log_emergency(...) log_full(log_emergency_level(), __VA_ARGS__)
/* Logging triggered by an errno-like error */
#define log_debug_errno(error, ...) log_full_errno(LOG_DEBUG, error, __VA_ARGS__)
@@ -266,7 +260,7 @@ void log_assert_failed_return_realm(
#define log_notice_errno(error, ...) log_full_errno(LOG_NOTICE, error, __VA_ARGS__)
#define log_warning_errno(error, ...) log_full_errno(LOG_WARNING, error, __VA_ARGS__)
#define log_error_errno(error, ...) log_full_errno(LOG_ERR, error, __VA_ARGS__)
-#define log_emergency_errno(error, ...) log_full_errno(getpid_cached() == 1 ? LOG_EMERG : LOG_ERR, error, __VA_ARGS__)
+#define log_emergency_errno(error, ...) log_full_errno(log_emergency_level(), error, __VA_ARGS__)
#ifdef LOG_TRACE
# define log_trace(...) log_debug(__VA_ARGS__)
@@ -302,10 +296,20 @@ LogTarget log_target_from_string(const char *s) _pure_;
void log_received_signal(int level, const struct signalfd_siginfo *si);
+/* If turned on, any requests for a log target involving "syslog" will be implicitly upgraded to the equivalent journal target */
void log_set_upgrade_syslog_to_journal(bool b);
+
+/* If turned on, and log_open() is called, we'll not use STDERR_FILENO for logging ever, but rather open /dev/console */
void log_set_always_reopen_console(bool b);
+
+/* If turned on, we'll open the log stream implicitly if needed on each individual log call. This is normally not
+ * desired as we want to reuse our logging streams. It is useful however */
void log_set_open_when_needed(bool b);
+/* If turned on, then we'll never use IPC-based logging, i.e. never log to syslog or the journal. We'll only log to
+ * stderr, the console or kmsg */
+void log_set_prohibit_ipc(bool b);
+
int log_syntax_internal(
const char *unit,
int level,
@@ -317,6 +321,16 @@ int log_syntax_internal(
const char *func,
const char *format, ...) _printf_(9, 10);
+int log_syntax_invalid_utf8_internal(
+ const char *unit,
+ int level,
+ const char *config_file,
+ unsigned config_line,
+ const char *file,
+ int line,
+ const char *func,
+ const char *rvalue);
+
#define log_syntax(unit, level, config_file, config_line, error, ...) \
({ \
int _level = (level), _e = (error); \
@@ -328,10 +342,9 @@ int log_syntax_internal(
#define log_syntax_invalid_utf8(unit, level, config_file, config_line, rvalue) \
({ \
int _level = (level); \
- if (log_get_max_level() >= LOG_PRI(_level)) { \
- _cleanup_free_ char *_p = NULL; \
- _p = utf8_escape_invalid(rvalue); \
- log_syntax_internal(unit, _level, config_file, config_line, 0, __FILE__, __LINE__, __func__, \
- "String is not UTF-8 clean, ignoring assignment: %s", strna(_p)); \
- } \
+ (log_get_max_level() >= LOG_PRI(_level)) \
+ ? log_syntax_invalid_utf8_internal(unit, _level, config_file, config_line, __FILE__, __LINE__, __func__, rvalue) \
+ : -EINVAL; \
})
+
+#define DEBUG_LOGGING _unlikely_(log_get_max_level() >= LOG_DEBUG)
diff --git a/src/basic/macro.h b/src/basic/macro.h
index 02d22de833..89bdd852a9 100644
--- a/src/basic/macro.h
+++ b/src/basic/macro.h
@@ -139,11 +139,17 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
return 1UL << (sizeof(u) * 8 - __builtin_clzl(u - 1UL));
}
+#ifndef __COVERITY__
+# define VOID_0 ((void)0)
+#else
+# define VOID_0 ((void*)0)
+#endif
+
#define ELEMENTSOF(x) \
__extension__ (__builtin_choose_expr( \
!__builtin_types_compatible_p(typeof(x), typeof(&*(x))), \
sizeof(x)/sizeof((x)[0]), \
- (void)0))
+ VOID_0))
/*
* STRLEN - return the length of a string literal, minus the trailing NUL byte.
@@ -181,7 +187,7 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) {
__builtin_constant_p(_B) && \
__builtin_types_compatible_p(typeof(_A), typeof(_B)), \
((_A) > (_B)) ? (_A) : (_B), \
- (void)0))
+ VOID_0))
/* takes two types and returns the size of the larger one */
#define MAXSIZE(A, B) (sizeof(union _packed_ { typeof(A) a; typeof(B) b; }))
diff --git a/src/basic/meson.build b/src/basic/meson.build
index a37e279e57..44cd31ecbe 100644
--- a/src/basic/meson.build
+++ b/src/basic/meson.build
@@ -15,7 +15,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with systemd; If not, see <http://www.gnu.org/licenses/>.
-basic_sources_plain = files('''
+basic_sources = files('''
MurmurHash2.c
MurmurHash2.h
af-list.c
@@ -35,6 +35,8 @@ basic_sources_plain = files('''
bitmap.c
bitmap.h
blkid-util.h
+ blockdev-util.c
+ blockdev-util.h
bpf-program.c
bpf-program.h
btrfs-ctree.h
@@ -148,6 +150,8 @@ basic_sources_plain = files('''
proc-cmdline.h
process-util.c
process-util.h
+ procfs-util.c
+ procfs-util.h
random-util.c
random-util.h
ratelimit.c
@@ -176,6 +180,8 @@ basic_sources_plain = files('''
smack-util.c
smack-util.h
socket-label.c
+ socket-protocol-list.c
+ socket-protocol-list.h
socket-util.c
socket-util.h
sparse-endian.h
@@ -201,10 +207,10 @@ basic_sources_plain = files('''
time-util.h
umask-util.h
unaligned.h
- unit-name.c
- unit-name.h
unit-def.c
unit-def.h
+ unit-name.c
+ unit-name.h
user-util.c
user-util.h
utf8.c
@@ -255,11 +261,19 @@ errno_list_txt = custom_target(
command : [generate_errno_list, cpp],
capture : true)
+generate_socket_protocol_list = find_program('generate-socket-protocol-list.sh')
+socket_protocol_list_txt = custom_target(
+ 'socket-protocol-list.txt',
+ output : 'socket-protocol-list.txt',
+ command : [generate_socket_protocol_list, cpp],
+ capture : true)
+
generated_gperf_headers = []
foreach item : [['af', af_list_txt, 'af', ''],
['arphrd', arphrd_list_txt, 'arphrd', 'ARPHRD_'],
['cap', cap_list_txt, 'capability', ''],
- ['errno', errno_list_txt, 'errno', '']]
+ ['errno', errno_list_txt, 'errno', ''],
+ ['socket-protocol', socket_protocol_list_txt, 'socket_protocol', 'IPPROTO_']]
fname = '@0@-from-name.gperf'.format(item[0])
gperf_file = custom_target(
@@ -294,7 +308,7 @@ foreach item : [['af', af_list_txt, 'af', ''],
generated_gperf_headers += [target1, target2]
endforeach
-basic_sources = basic_sources_plain + [missing_h] + generated_gperf_headers
+basic_sources += [missing_h] + generated_gperf_headers
libbasic = static_library(
'basic',
@@ -303,6 +317,16 @@ libbasic = static_library(
dependencies : [threads,
libcap,
libblkid,
- libselinux,
- ],
+ libselinux],
+ c_args : ['-fvisibility=default'],
install : false)
+
+# A convenience library that is separate from libbasic to avoid
+# unnecessary linking to libgcrypt.
+libbasic_gcrypt = static_library(
+ 'basic-gcrypt',
+ 'gcrypt-util.c',
+ 'gcrypt-util.h',
+ include_directories : includes,
+ dependencies : [libgcrypt],
+ c_args : ['-fvisibility=default'])
diff --git a/src/basic/missing.h b/src/basic/missing.h
index 790f9f55a5..1280e6c410 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -206,6 +206,32 @@ struct sockaddr_vm {
#endif
#if ! HAVE_LINUX_BTRFS_H
+#define BTRFS_IOC_QGROUP_ASSIGN _IOW(BTRFS_IOCTL_MAGIC, 41, \
+ struct btrfs_ioctl_qgroup_assign_args)
+#define BTRFS_IOC_QGROUP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 42, \
+ struct btrfs_ioctl_qgroup_create_args)
+#define BTRFS_IOC_QUOTA_RESCAN _IOW(BTRFS_IOCTL_MAGIC, 44, \
+ struct btrfs_ioctl_quota_rescan_args)
+#define BTRFS_IOC_QUOTA_RESCAN_STATUS _IOR(BTRFS_IOCTL_MAGIC, 45, \
+ struct btrfs_ioctl_quota_rescan_args)
+
+struct btrfs_ioctl_quota_rescan_args {
+ __u64 flags;
+ __u64 progress;
+ __u64 reserved[6];
+};
+
+struct btrfs_ioctl_qgroup_assign_args {
+ __u64 assign;
+ __u64 src;
+ __u64 dst;
+};
+
+struct btrfs_ioctl_qgroup_create_args {
+ __u64 create;
+ __u64 qgroupid;
+};
+
struct btrfs_ioctl_vol_args {
int64_t fd;
char name[BTRFS_PATH_NAME_MAX + 1];
@@ -543,6 +569,38 @@ struct btrfs_ioctl_quota_ctl_args {
#define PR_SET_CHILD_SUBREAPER 36
#endif
+#ifndef PR_SET_MM_ARG_START
+#define PR_SET_MM_ARG_START 8
+#endif
+
+#ifndef PR_SET_MM_ARG_END
+#define PR_SET_MM_ARG_END 9
+#endif
+
+#ifndef PR_SET_MM_ENV_START
+#define PR_SET_MM_ENV_START 10
+#endif
+
+#ifndef PR_SET_MM_ENV_END
+#define PR_SET_MM_ENV_END 11
+#endif
+
+#ifndef EFIVARFS_MAGIC
+#define EFIVARFS_MAGIC 0xde5e81e4
+#endif
+
+#ifndef SMACK_MAGIC
+#define SMACK_MAGIC 0x43415d53
+#endif
+
+#ifndef DM_DEFERRED_REMOVE
+#define DM_DEFERRED_REMOVE (1 << 17)
+#endif
+
+#ifndef MAX_HANDLE_SZ
+#define MAX_HANDLE_SZ 128
+#endif
+
#if ! HAVE_SECURE_GETENV
# if HAVE___SECURE_GETENV
# define secure_getenv __secure_getenv
@@ -563,6 +621,10 @@ struct btrfs_ioctl_quota_ctl_args {
# define SO_REUSEPORT 15
#endif
+#ifndef SO_PEERGROUPS
+# define SO_PEERGROUPS 59
+#endif
+
#ifndef EVIOCREVOKE
# define EVIOCREVOKE _IOW('E', 0x91, int)
#endif
@@ -653,18 +715,28 @@ struct input_mask {
#define IFLA_MACVLAN_MAX (__IFLA_MACVLAN_MAX - 1)
#endif
-#if !HAVE_IFLA_IPVLAN_MODE
+#if !HAVE_IFLA_IPVLAN_FLAGS
#define IFLA_IPVLAN_UNSPEC 0
#define IFLA_IPVLAN_MODE 1
-#define __IFLA_IPVLAN_MAX 2
+#define IFLA_IPVLAN_FLAGS 2
+#define __IFLA_IPVLAN_MAX 3
#define IFLA_IPVLAN_MAX (__IFLA_IPVLAN_MAX - 1)
#define IPVLAN_MODE_L2 0
#define IPVLAN_MODE_L3 1
+#define IPVLAN_MODE_L3S 2
#define IPVLAN_MAX 2
#endif
+#if !HAVE_IPVLAN_F_PRIVATE
+#define IPVLAN_F_PRIVATE 0x01
+#define IPVLAN_F_VEPA 0x02
+#define __IPVLAN_F_PRIVATE_MAX 3
+
+#define HAVE_IPVLAN_F_PRIVATE_MAX (__HAVE_IPVLAN_F_PRIVATE_MAX - 1)
+#endif
+
#if !HAVE_IFLA_VTI_REMOTE
#define IFLA_VTI_UNSPEC 0
#define IFLA_VTI_LINK 1
diff --git a/src/basic/missing_syscall.h b/src/basic/missing_syscall.h
index fd82c11e94..c938d0d976 100644
--- a/src/basic/missing_syscall.h
+++ b/src/basic/missing_syscall.h
@@ -330,10 +330,14 @@ static inline ssize_t copy_file_range(int fd_in, loff_t *off_in,
# define __NR_bpf 321
# elif defined __aarch64__
# define __NR_bpf 280
+# elif defined __arm__
+# define __NR_bpf 386
# elif defined __sparc__
# define __NR_bpf 349
# elif defined __s390__
# define __NR_bpf 351
+# elif defined __tilegx__
+# define __NR_bpf 280
# else
# warning "__NR_bpf not defined for your architecture"
# endif
diff --git a/src/basic/mkdir-label.c b/src/basic/mkdir-label.c
index 16eb7ce265..6f3a46f467 100644
--- a/src/basic/mkdir-label.c
+++ b/src/basic/mkdir-label.c
@@ -20,11 +20,32 @@
***/
#include <stdio.h>
+#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "label.h"
+#include "macro.h"
#include "mkdir.h"
+#include "selinux-util.h"
+#include "smack-util.h"
+
+int mkdir_label(const char *path, mode_t mode) {
+ int r;
+
+ assert(path);
+
+ r = mac_selinux_create_file_prepare(path, S_IFDIR);
+ if (r < 0)
+ return r;
+
+ r = mkdir_errno_wrapper(path, mode);
+ mac_selinux_create_file_clear();
+ if (r < 0)
+ return r;
+
+ return mac_smack_fix(path, false, false);
+}
int mkdir_safe_label(const char *path, mode_t mode, uid_t uid, gid_t gid, bool follow_symlink) {
return mkdir_safe_internal(path, mode, uid, gid, follow_symlink, mkdir_label);
diff --git a/src/basic/mkdir.c b/src/basic/mkdir.c
index 4386b38c4a..de4746c867 100644
--- a/src/basic/mkdir.c
+++ b/src/basic/mkdir.c
@@ -35,6 +35,8 @@ int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, boo
struct stat st;
int r;
+ assert(_mkdir != mkdir);
+
if (_mkdir(path, mode) >= 0) {
r = chmod_and_chown(path, mode, uid, gid);
if (r < 0)
@@ -68,8 +70,14 @@ int mkdir_safe_internal(const char *path, mode_t mode, uid_t uid, gid_t gid, boo
return 0;
}
+int mkdir_errno_wrapper(const char *pathname, mode_t mode) {
+ if (mkdir(pathname, mode) < 0)
+ return -errno;
+ return 0;
+}
+
int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid, bool follow_symlink) {
- return mkdir_safe_internal(path, mode, uid, gid, follow_symlink, mkdir);
+ return mkdir_safe_internal(path, mode, uid, gid, follow_symlink, mkdir_errno_wrapper);
}
int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) {
@@ -77,6 +85,7 @@ int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mk
int r;
assert(path);
+ assert(_mkdir != mkdir);
if (prefix && !path_startswith(path, prefix))
return -ENOTDIR;
@@ -104,8 +113,7 @@ int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mk
e = p + strcspn(p, "/");
p = e + strspn(e, "/");
- /* Is this the last component? If so, then we're
- * done */
+ /* Is this the last component? If so, then we're done */
if (*p == 0)
return 0;
@@ -116,13 +124,13 @@ int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, mk
continue;
r = _mkdir(t, mode);
- if (r < 0 && errno != EEXIST)
- return -errno;
+ if (r < 0 && r != -EEXIST)
+ return r;
}
}
int mkdir_parents(const char *path, mode_t mode) {
- return mkdir_parents_internal(NULL, path, mode, mkdir);
+ return mkdir_parents_internal(NULL, path, mode, mkdir_errno_wrapper);
}
int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_func_t _mkdir) {
@@ -130,17 +138,19 @@ int mkdir_p_internal(const char *prefix, const char *path, mode_t mode, mkdir_fu
/* Like mkdir -p */
+ assert(_mkdir != mkdir);
+
r = mkdir_parents_internal(prefix, path, mode, _mkdir);
if (r < 0)
return r;
r = _mkdir(path, mode);
- if (r < 0 && (errno != EEXIST || is_dir(path, true) <= 0))
- return -errno;
+ if (r < 0 && (r != -EEXIST || is_dir(path, true) <= 0))
+ return r;
return 0;
}
int mkdir_p(const char *path, mode_t mode) {
- return mkdir_p_internal(NULL, path, mode, mkdir);
+ return mkdir_p_internal(NULL, path, mode, mkdir_errno_wrapper);
}
diff --git a/src/basic/mkdir.h b/src/basic/mkdir.h
index 04a537f8a8..d6c2d579a3 100644
--- a/src/basic/mkdir.h
+++ b/src/basic/mkdir.h
@@ -23,6 +23,7 @@
#include <sys/types.h>
+int mkdir_errno_wrapper(const char *pathname, mode_t mode);
int mkdir_safe(const char *path, mode_t mode, uid_t uid, gid_t gid, bool follow_symlink);
int mkdir_parents(const char *path, mode_t mode);
int mkdir_p(const char *path, mode_t mode);
diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c
index d03f60e01a..2c22753dea 100644
--- a/src/basic/parse-util.c
+++ b/src/basic/parse-util.c
@@ -28,6 +28,7 @@
#include "alloc-util.h"
#include "errno-list.h"
#include "extract-word.h"
+#include "locale-util.h"
#include "macro.h"
#include "parse-util.h"
#include "process-util.h"
@@ -83,7 +84,7 @@ int parse_mode(const char *s, mode_t *ret) {
l = strtol(s, &x, 8);
if (errno > 0)
return -errno;
- if (!x || x == s || *x)
+ if (!x || x == s || *x != 0)
return -EINVAL;
if (l < 0 || l > 07777)
return -ERANGE;
@@ -283,7 +284,8 @@ int parse_errno(const char *t) {
if (r < 0)
return r;
- if (e < 0 || e > ERRNO_MAX)
+ /* 0 is also allowed here */
+ if (!errno_is_valid(e) && e != 0)
return -ERANGE;
return e;
@@ -390,7 +392,7 @@ int safe_atou(const char *s, unsigned *ret_u) {
l = strtoul(s, &x, 0);
if (errno > 0)
return -errno;
- if (!x || x == s || *x)
+ if (!x || x == s || *x != 0)
return -EINVAL;
if (s[0] == '-')
return -ERANGE;
@@ -412,7 +414,7 @@ int safe_atoi(const char *s, int *ret_i) {
l = strtol(s, &x, 0);
if (errno > 0)
return -errno;
- if (!x || x == s || *x)
+ if (!x || x == s || *x != 0)
return -EINVAL;
if ((long) (int) l != l)
return -ERANGE;
@@ -434,7 +436,7 @@ int safe_atollu(const char *s, long long unsigned *ret_llu) {
l = strtoull(s, &x, 0);
if (errno > 0)
return -errno;
- if (!x || x == s || *x)
+ if (!x || x == s || *x != 0)
return -EINVAL;
if (*s == '-')
return -ERANGE;
@@ -454,7 +456,7 @@ int safe_atolli(const char *s, long long int *ret_lli) {
l = strtoll(s, &x, 0);
if (errno > 0)
return -errno;
- if (!x || x == s || *x)
+ if (!x || x == s || *x != 0)
return -EINVAL;
*ret_lli = l;
@@ -474,7 +476,7 @@ int safe_atou8(const char *s, uint8_t *ret) {
l = strtoul(s, &x, 0);
if (errno > 0)
return -errno;
- if (!x || x == s || *x)
+ if (!x || x == s || *x != 0)
return -EINVAL;
if (s[0] == '-')
return -ERANGE;
@@ -498,7 +500,7 @@ int safe_atou16(const char *s, uint16_t *ret) {
l = strtoul(s, &x, 0);
if (errno > 0)
return -errno;
- if (!x || x == s || *x)
+ if (!x || x == s || *x != 0)
return -EINVAL;
if (s[0] == '-')
return -ERANGE;
@@ -520,7 +522,7 @@ int safe_atoi16(const char *s, int16_t *ret) {
l = strtol(s, &x, 0);
if (errno > 0)
return -errno;
- if (!x || x == s || *x)
+ if (!x || x == s || *x != 0)
return -EINVAL;
if ((long) (int16_t) l != l)
return -ERANGE;
@@ -530,9 +532,9 @@ int safe_atoi16(const char *s, int16_t *ret) {
}
int safe_atod(const char *s, double *ret_d) {
+ _cleanup_(freelocalep) locale_t loc = (locale_t) 0;
char *x = NULL;
double d = 0;
- locale_t loc;
assert(s);
assert(ret_d);
@@ -543,16 +545,11 @@ int safe_atod(const char *s, double *ret_d) {
errno = 0;
d = strtod_l(s, &x, loc);
- if (errno > 0) {
- freelocale(loc);
+ if (errno > 0)
return -errno;
- }
- if (!x || x == s || *x) {
- freelocale(loc);
+ if (!x || x == s || *x != 0)
return -EINVAL;
- }
- freelocale(loc);
*ret_d = (double) d;
return 0;
}
@@ -595,19 +592,20 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
int parse_percent_unbounded(const char *p) {
const char *pc, *n;
- unsigned v;
- int r;
+ int r, v;
pc = endswith(p, "%");
if (!pc)
return -EINVAL;
n = strndupa(p, pc - p);
- r = safe_atou(n, &v);
+ r = safe_atoi(n, &v);
if (r < 0)
return r;
+ if (v < 0)
+ return -ERANGE;
- return (int) v;
+ return v;
}
int parse_percent(const char *p) {
diff --git a/src/basic/path-util.c b/src/basic/path-util.c
index ab4778d4ed..df94629385 100644
--- a/src/basic/path-util.c
+++ b/src/basic/path-util.c
@@ -81,14 +81,36 @@ char *path_make_absolute(const char *p, const char *prefix) {
/* Makes every item in the list an absolute path by prepending
* the prefix, if specified and necessary */
- if (path_is_absolute(p) || !prefix)
+ if (path_is_absolute(p) || isempty(prefix))
return strdup(p);
- return strjoin(prefix, "/", p);
+ if (endswith(prefix, "/"))
+ return strjoin(prefix, p);
+ else
+ return strjoin(prefix, "/", p);
+}
+
+int safe_getcwd(char **ret) {
+ char *cwd;
+
+ cwd = get_current_dir_name();
+ if (!cwd)
+ return negative_errno();
+
+ /* Let's make sure the directory is really absolute, to protect us from the logic behind
+ * CVE-2018-1000001 */
+ if (cwd[0] != '/') {
+ free(cwd);
+ return -ENOMEDIUM;
+ }
+
+ *ret = cwd;
+ return 0;
}
int path_make_absolute_cwd(const char *p, char **ret) {
char *c;
+ int r;
assert(p);
assert(ret);
@@ -101,11 +123,14 @@ int path_make_absolute_cwd(const char *p, char **ret) {
else {
_cleanup_free_ char *cwd = NULL;
- cwd = get_current_dir_name();
- if (!cwd)
- return negative_errno();
+ r = safe_getcwd(&cwd);
+ if (r < 0)
+ return r;
- c = strjoin(cwd, "/", p);
+ if (endswith(cwd, "/"))
+ c = strjoin(cwd, p);
+ else
+ c = strjoin(cwd, "/", p);
}
if (!c)
return -ENOMEM;
diff --git a/src/basic/path-util.h b/src/basic/path-util.h
index f79cdf928e..89c285e076 100644
--- a/src/basic/path-util.h
+++ b/src/basic/path-util.h
@@ -41,6 +41,7 @@ bool is_path(const char *p) _pure_;
int path_split_and_make_absolute(const char *p, char ***ret);
bool path_is_absolute(const char *p) _pure_;
char* path_make_absolute(const char *p, const char *prefix);
+int safe_getcwd(char **ret);
int path_make_absolute_cwd(const char *p, char **ret);
int path_make_relative(const char *from_dir, const char *to_path, char **_r);
char* path_kill_slashes(char *path);
diff --git a/src/basic/process-util.c b/src/basic/process-util.c
index 17c94f44a0..dc7c9ef9ef 100644
--- a/src/basic/process-util.c
+++ b/src/basic/process-util.c
@@ -56,6 +56,7 @@
#include "stat-util.h"
#include "string-table.h"
#include "string-util.h"
+#include "terminal-util.h"
#include "user-util.h"
#include "util.h"
@@ -296,10 +297,17 @@ int rename_process(const char name[]) {
if (isempty(name))
return -EINVAL; /* let's not confuse users unnecessarily with an empty name */
+ if (!is_main_thread())
+ return -EPERM; /* Let's not allow setting the process name from other threads than the main one, as we
+ * cache things without locking, and we make assumptions that PR_SET_NAME sets the
+ * process name that isn't correct on any other threads */
+
l = strlen(name);
- /* First step, change the comm field. */
- (void) prctl(PR_SET_NAME, name);
+ /* First step, change the comm field. The main thread's comm is identical to the process comm. This means we
+ * can use PR_SET_NAME, which sets the thread name for the calling thread. */
+ if (prctl(PR_SET_NAME, name) < 0)
+ log_debug_errno(errno, "PR_SET_NAME failed: %m");
if (l > 15) /* Linux process names can be 15 chars at max */
truncated = true;
@@ -679,32 +687,43 @@ int wait_for_terminate(pid_t pid, siginfo_t *status) {
* A warning is emitted if the process terminates abnormally,
* and also if it returns non-zero unless check_exit_code is true.
*/
-int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code) {
- int r;
+int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags) {
+ _cleanup_free_ char *buffer = NULL;
siginfo_t status;
+ int r, prio;
- assert(name);
assert(pid > 1);
+ if (!name) {
+ r = get_process_comm(pid, &buffer);
+ if (r < 0)
+ log_debug_errno(r, "Failed to acquire process name of " PID_FMT ", ignoring: %m", pid);
+ else
+ name = buffer;
+ }
+
+ prio = flags & WAIT_LOG_ABNORMAL ? LOG_ERR : LOG_DEBUG;
+
r = wait_for_terminate(pid, &status);
if (r < 0)
- return log_warning_errno(r, "Failed to wait for %s: %m", name);
+ return log_full_errno(prio, r, "Failed to wait for %s: %m", strna(name));
if (status.si_code == CLD_EXITED) {
- if (status.si_status != 0)
- log_full(check_exit_code ? LOG_WARNING : LOG_DEBUG,
- "%s failed with error code %i.", name, status.si_status);
+ if (status.si_status != EXIT_SUCCESS)
+ log_full(flags & WAIT_LOG_NON_ZERO_EXIT_STATUS ? LOG_ERR : LOG_DEBUG,
+ "%s failed with exit status %i.", strna(name), status.si_status);
else
log_debug("%s succeeded.", name);
return status.si_status;
+
} else if (IN_SET(status.si_code, CLD_KILLED, CLD_DUMPED)) {
- log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status));
+ log_full(prio, "%s terminated by signal %s.", strna(name), signal_to_string(status.si_status));
return -EPROTO;
}
- log_warning("%s failed due to unknown reason.", name);
+ log_full(prio, "%s failed due to unknown reason.", strna(name));
return -EPROTO;
}
@@ -777,6 +796,8 @@ void sigkill_wait(pid_t pid) {
}
void sigkill_waitp(pid_t *pid) {
+ PROTECT_ERRNO;
+
if (!pid)
return;
if (*pid <= 1)
@@ -928,6 +949,17 @@ noreturn void freeze(void) {
sync();
+ /* Let's not freeze right away, but keep reaping zombies. */
+ for (;;) {
+ int r;
+ siginfo_t si = {};
+
+ r = waitid(P_ALL, 0, &si, WEXITED);
+ if (r < 0 && errno != EINTR)
+ break;
+ }
+
+ /* waitid() failed with an unexpected error, things are really borked. Freeze now! */
for (;;)
pause();
}
@@ -1075,7 +1107,7 @@ int ioprio_parse_priority(const char *s, int *ret) {
static pid_t cached_pid = CACHED_PID_UNSET;
-static void reset_cached_pid(void) {
+void reset_cached_pid(void) {
/* Invoked in the child after a fork(), i.e. at the first moment the PID changed */
cached_pid = CACHED_PID_UNSET;
}
@@ -1134,6 +1166,244 @@ int must_be_root(void) {
return -EPERM;
}
+int safe_fork_full(
+ const char *name,
+ const int except_fds[],
+ size_t n_except_fds,
+ ForkFlags flags,
+ pid_t *ret_pid) {
+
+ pid_t original_pid, pid;
+ sigset_t saved_ss, ss;
+ bool block_signals = false;
+ int prio, r;
+
+ /* A wrapper around fork(), that does a couple of important initializations in addition to mere forking. Always
+ * returns the child's PID in *ret_pid. Returns == 0 in the child, and > 0 in the parent. */
+
+ prio = flags & FORK_LOG ? LOG_ERR : LOG_DEBUG;
+
+ original_pid = getpid_cached();
+
+ if (flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG)) {
+
+ /* We temporarily block all signals, so that the new child has them blocked initially. This way, we can
+ * be sure that SIGTERMs are not lost we might send to the child. */
+
+ if (sigfillset(&ss) < 0)
+ return log_full_errno(prio, errno, "Failed to reset signal set: %m");
+
+ block_signals = true;
+
+ } else if (flags & FORK_WAIT) {
+
+ /* Let's block SIGCHLD at least, so that we can safely watch for the child process */
+
+ if (sigemptyset(&ss) < 0)
+ return log_full_errno(prio, errno, "Failed to clear signal set: %m");
+
+ if (sigaddset(&ss, SIGCHLD) < 0)
+ return log_full_errno(prio, errno, "Failed to add SIGCHLD to signal set: %m");
+
+ block_signals = true;
+ }
+
+ if (block_signals)
+ if (sigprocmask(SIG_SETMASK, &ss, &saved_ss) < 0)
+ return log_full_errno(prio, errno, "Failed to set signal mask: %m");
+
+ if (flags & FORK_NEW_MOUNTNS)
+ pid = raw_clone(SIGCHLD|CLONE_NEWNS);
+ else
+ pid = fork();
+ if (pid < 0) {
+ r = -errno;
+
+ if (block_signals) /* undo what we did above */
+ (void) sigprocmask(SIG_SETMASK, &saved_ss, NULL);
+
+ return log_full_errno(prio, r, "Failed to fork: %m");
+ }
+ if (pid > 0) {
+ /* We are in the parent process */
+
+ log_debug("Successfully forked off '%s' as PID " PID_FMT ".", strna(name), pid);
+
+ if (flags & FORK_WAIT) {
+ r = wait_for_terminate_and_check(name, pid, (flags & FORK_LOG ? WAIT_LOG : 0));
+ if (r < 0)
+ return r;
+ if (r != EXIT_SUCCESS) /* exit status > 0 should be treated as failure, too */
+ return -EPROTO;
+ }
+
+ if (block_signals) /* undo what we did above */
+ (void) sigprocmask(SIG_SETMASK, &saved_ss, NULL);
+
+ if (ret_pid)
+ *ret_pid = pid;
+
+ return 1;
+ }
+
+ /* We are in the child process */
+
+ if (flags & FORK_REOPEN_LOG) {
+ /* Close the logs if requested, before we log anything. And make sure we reopen it if needed. */
+ log_close();
+ log_set_open_when_needed(true);
+ }
+
+ if (name) {
+ r = rename_process(name);
+ if (r < 0)
+ log_full_errno(flags & FORK_LOG ? LOG_WARNING : LOG_DEBUG,
+ r, "Failed to rename process, ignoring: %m");
+ }
+
+ if (flags & FORK_DEATHSIG)
+ if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) {
+ log_full_errno(prio, errno, "Failed to set death signal: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (flags & FORK_RESET_SIGNALS) {
+ r = reset_all_signal_handlers();
+ if (r < 0) {
+ log_full_errno(prio, r, "Failed to reset signal handlers: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ /* This implicitly undoes the signal mask stuff we did before the fork()ing above */
+ r = reset_signal_mask();
+ if (r < 0) {
+ log_full_errno(prio, r, "Failed to reset signal mask: %m");
+ _exit(EXIT_FAILURE);
+ }
+ } else if (block_signals) { /* undo what we did above */
+ if (sigprocmask(SIG_SETMASK, &saved_ss, NULL) < 0) {
+ log_full_errno(prio, errno, "Failed to restore signal mask: %m");
+ _exit(EXIT_FAILURE);
+ }
+ }
+
+ if (flags & FORK_DEATHSIG) {
+ pid_t ppid;
+ /* Let's see if the parent PID is still the one we started from? If not, then the parent
+ * already died by the time we set PR_SET_PDEATHSIG, hence let's emulate the effect */
+
+ ppid = getppid();
+ if (ppid == 0)
+ /* Parent is in a differn't PID namespace. */;
+ else if (ppid != original_pid) {
+ log_debug("Parent died early, raising SIGTERM.");
+ (void) raise(SIGTERM);
+ _exit(EXIT_FAILURE);
+ }
+ }
+
+ if (flags & FORK_CLOSE_ALL_FDS) {
+ /* Close the logs here in case it got reopened above, as close_all_fds() would close them for us */
+ log_close();
+
+ r = close_all_fds(except_fds, n_except_fds);
+ if (r < 0) {
+ log_full_errno(prio, r, "Failed to close all file descriptors: %m");
+ _exit(EXIT_FAILURE);
+ }
+ }
+
+ /* When we were asked to reopen the logs, do so again now */
+ if (flags & FORK_REOPEN_LOG) {
+ log_open();
+ log_set_open_when_needed(false);
+ }
+
+ if (flags & FORK_NULL_STDIO) {
+ r = make_null_stdio();
+ if (r < 0) {
+ log_full_errno(prio, r, "Failed to connect stdin/stdout to /dev/null: %m");
+ _exit(EXIT_FAILURE);
+ }
+ }
+
+ if (ret_pid)
+ *ret_pid = getpid_cached();
+
+ return 0;
+}
+
+int fork_agent(const char *name, const int except[], unsigned n_except, pid_t *ret_pid, const char *path, ...) {
+ bool stdout_is_tty, stderr_is_tty;
+ unsigned n, i;
+ va_list ap;
+ char **l;
+ int r;
+
+ assert(path);
+
+ /* Spawns a temporary TTY agent, making sure it goes away when we go away */
+
+ r = safe_fork_full(name, except, n_except, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS, ret_pid);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ return 0;
+
+ /* In the child: */
+
+ stdout_is_tty = isatty(STDOUT_FILENO);
+ stderr_is_tty = isatty(STDERR_FILENO);
+
+ if (!stdout_is_tty || !stderr_is_tty) {
+ int fd;
+
+ /* Detach from stdout/stderr. and reopen
+ * /dev/tty for them. This is important to
+ * ensure that when systemctl is started via
+ * popen() or a similar call that expects to
+ * read EOF we actually do generate EOF and
+ * not delay this indefinitely by because we
+ * keep an unused copy of stdin around. */
+ fd = open("/dev/tty", O_WRONLY);
+ if (fd < 0) {
+ log_error_errno(errno, "Failed to open /dev/tty: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) {
+ log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) {
+ log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (fd > STDERR_FILENO)
+ close(fd);
+ }
+
+ /* Count arguments */
+ va_start(ap, path);
+ for (n = 0; va_arg(ap, char*); n++)
+ ;
+ va_end(ap);
+
+ /* Allocate strv */
+ l = alloca(sizeof(char *) * (n + 1));
+
+ /* Fill in arguments */
+ va_start(ap, path);
+ for (i = 0; i <= n; i++)
+ l[i] = va_arg(ap, char*);
+ va_end(ap);
+
+ execv(path, l);
+ _exit(EXIT_FAILURE);
+}
+
static const char *const ioprio_class_table[] = {
[IOPRIO_CLASS_NONE] = "none",
[IOPRIO_CLASS_RT] = "realtime",
diff --git a/src/basic/process-util.h b/src/basic/process-util.h
index 1b7e692060..9fabe4a5be 100644
--- a/src/basic/process-util.h
+++ b/src/basic/process-util.h
@@ -21,6 +21,7 @@
***/
#include <alloca.h>
+#include <errno.h>
#include <sched.h>
#include <signal.h>
#include <stdbool.h>
@@ -61,7 +62,16 @@ int get_process_environ(pid_t pid, char **environ);
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);
+
+typedef enum WaitFlags {
+ WAIT_LOG_ABNORMAL = 1U << 0,
+ WAIT_LOG_NON_ZERO_EXIT_STATUS = 1U << 1,
+
+ /* A shortcut for requesting the most complete logging */
+ WAIT_LOG = WAIT_LOG_ABNORMAL|WAIT_LOG_NON_ZERO_EXIT_STATUS,
+} WaitFlags;
+
+int wait_for_terminate_and_check(const char *name, pid_t pid, WaitFlags flags);
int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout);
void sigkill_wait(pid_t pid);
@@ -106,8 +116,13 @@ int sigchld_code_from_string(const char *s) _pure_;
int sched_policy_to_string_alloc(int i, char **s);
int sched_policy_from_string(const char *s);
-#define PTR_TO_PID(p) ((pid_t) ((uintptr_t) p))
-#define PID_TO_PTR(p) ((void*) ((uintptr_t) p))
+static inline pid_t PTR_TO_PID(const void *p) {
+ return (pid_t) ((uintptr_t) p);
+}
+
+static inline void* PID_TO_PTR(pid_t pid) {
+ return (void*) ((uintptr_t) pid);
+}
void valgrind_summary_hack(void);
@@ -137,8 +152,54 @@ static inline bool pid_is_valid(pid_t p) {
return p > 0;
}
+static inline int sched_policy_to_string_alloc_with_check(int n, char **s) {
+ if (!sched_policy_is_valid(n))
+ return -EINVAL;
+
+ return sched_policy_to_string_alloc(n, s);
+}
+
int ioprio_parse_priority(const char *s, int *ret);
pid_t getpid_cached(void);
+void reset_cached_pid(void);
int must_be_root(void);
+
+typedef enum ForkFlags {
+ FORK_RESET_SIGNALS = 1U << 0,
+ FORK_CLOSE_ALL_FDS = 1U << 1,
+ FORK_DEATHSIG = 1U << 2,
+ FORK_NULL_STDIO = 1U << 3,
+ FORK_REOPEN_LOG = 1U << 4,
+ FORK_LOG = 1U << 5,
+ FORK_WAIT = 1U << 6,
+ FORK_NEW_MOUNTNS = 1U << 7,
+} ForkFlags;
+
+int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);
+
+static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) {
+ return safe_fork_full(name, NULL, 0, flags, ret_pid);
+}
+
+int fork_agent(const char *name, const int except[], unsigned n_except, pid_t *pid, const char *path, ...);
+
+#if SIZEOF_PID_T == 4
+/* The highest possibly (theoretic) pid_t value on this architecture. */
+#define PID_T_MAX ((pid_t) INT32_MAX)
+/* The maximum number of concurrent processes Linux allows on this architecture, as well as the highest valid PID value
+ * the kernel will potentially assign. This reflects a value compiled into the kernel (PID_MAX_LIMIT), and sets the
+ * upper boundary on what may be written to the /proc/sys/kernel/pid_max sysctl (but do note that the sysctl is off by
+ * 1, since PID 0 can never exist and there can hence only be one process less than the limit would suggest). Since
+ * these values are documented in proc(5) we feel quite confident that they are stable enough for the near future at
+ * least to define them here too. */
+#define TASKS_MAX 4194303U
+#elif SIZEOF_PID_T == 2
+#define PID_T_MAX ((pid_t) INT16_MAX)
+#define TASKS_MAX 32767U
+#else
+#error "Unknown pid_t size"
+#endif
+
+assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX)
diff --git a/src/basic/procfs-util.c b/src/basic/procfs-util.c
new file mode 100644
index 0000000000..9bb42cc7ba
--- /dev/null
+++ b/src/basic/procfs-util.c
@@ -0,0 +1,138 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <errno.h>
+
+#include "alloc-util.h"
+#include "fileio.h"
+#include "parse-util.h"
+#include "process-util.h"
+#include "procfs-util.h"
+#include "stdio-util.h"
+#include "string-util.h"
+
+int procfs_tasks_get_limit(uint64_t *ret) {
+ _cleanup_free_ char *value = NULL;
+ uint64_t pid_max, threads_max;
+ int r;
+
+ assert(ret);
+
+ /* So there are two sysctl files that control the system limit of processes:
+ *
+ * 1. kernel.threads-max: this is probably the sysctl that makes more sense, as it directly puts a limit on
+ * concurrent tasks.
+ *
+ * 2. kernel.pid_max: this limits the numeric range PIDs can take, and thus indirectly also limits the number
+ * of concurrent threads. AFAICS it's primarily a compatibility concept: some crappy old code used a signed
+ * 16bit type for PIDs, hence the kernel provides a way to ensure the PIDs never go beyond INT16_MAX by
+ * default.
+ *
+ * By default #2 is set to much lower values than #1, hence the limit people come into contact with first, as
+ * it's the lowest boundary they need to bump when they want higher number of processes.
+ *
+ * Also note the weird definition of #2: PIDs assigned will be kept below this value, which means the number of
+ * tasks that can be created is one lower, as PID 0 is not a valid process ID. */
+
+ r = read_one_line_file("/proc/sys/kernel/pid_max", &value);
+ if (r < 0)
+ return r;
+
+ r = safe_atou64(value, &pid_max);
+ if (r < 0)
+ return r;
+
+ value = mfree(value);
+ r = read_one_line_file("/proc/sys/kernel/threads-max", &value);
+ if (r < 0)
+ return r;
+
+ r = safe_atou64(value, &threads_max);
+ if (r < 0)
+ return r;
+
+ /* Subtract one from pid_max, since PID 0 is not a valid PID */
+ *ret = MIN(pid_max-1, threads_max);
+ return 0;
+}
+
+int procfs_tasks_set_limit(uint64_t limit) {
+ char buffer[DECIMAL_STR_MAX(uint64_t)+1];
+ _cleanup_free_ char *value = NULL;
+ uint64_t pid_max;
+ int r;
+
+ if (limit == 0) /* This makes no sense, we are userspace and hence count as tasks too, and we want to live,
+ * hence the limit conceptually has to be above 0. Also, most likely if anyone asks for a zero
+ * limit he/she probably means "no limit", hence let's better refuse this to avoid
+ * confusion. */
+ return -EINVAL;
+
+ /* The Linux kernel doesn't allow this value to go below 20, hence don't allow this either, higher values than
+ * TASKS_MAX are not accepted by the pid_max sysctl. We'll treat anything this high as "unbounded" and hence
+ * set it to the maximum. */
+ limit = CLAMP(limit, 20U, TASKS_MAX);
+
+ r = read_one_line_file("/proc/sys/kernel/pid_max", &value);
+ if (r < 0)
+ return r;
+ r = safe_atou64(value, &pid_max);
+ if (r < 0)
+ return r;
+
+ /* As pid_max is about the numeric pid_t range we'll bump it if necessary, but only ever increase it, never
+ * decrease it, as threads-max is the much more relevant sysctl. */
+ if (limit > pid_max-1) {
+ sprintf(buffer, "%" PRIu64, limit+1); /* Add one, since PID 0 is not a valid PID */
+ r = write_string_file("/proc/sys/kernel/pid_max", buffer, WRITE_STRING_FILE_DISABLE_BUFFER);
+ if (r < 0)
+ return r;
+ }
+
+ sprintf(buffer, "%" PRIu64, limit);
+ r = write_string_file("/proc/sys/kernel/threads-max", buffer, WRITE_STRING_FILE_DISABLE_BUFFER);
+ if (r < 0) {
+ uint64_t threads_max;
+
+ /* Hmm, we couldn't write this? If so, maybe it was already set properly? In that case let's not
+ * generate an error */
+
+ value = mfree(value);
+ if (read_one_line_file("/proc/sys/kernel/threads-max", &value) < 0)
+ return r; /* return original error */
+
+ if (safe_atou64(value, &threads_max) < 0)
+ return r; /* return original error */
+
+ if (MIN(pid_max-1, threads_max) != limit)
+ return r; /* return original error */
+
+ /* Yay! Value set already matches what we were trying to set, hence consider this a success. */
+ }
+
+ return 0;
+}
+
+int procfs_tasks_get_current(uint64_t *ret) {
+ _cleanup_free_ char *value = NULL;
+ const char *p, *nr;
+ size_t n;
+ int r;
+
+ assert(ret);
+
+ r = read_one_line_file("/proc/loadavg", &value);
+ if (r < 0)
+ return r;
+
+ /* Look for the second part of the fourth field, which is separated by a slash from the first part. None of the
+ * earlier fields use a slash, hence let's use this to find the right spot. */
+ p = strchr(value, '/');
+ if (!p)
+ return -EINVAL;
+
+ p++;
+ n = strspn(p, DIGITS);
+ nr = strndupa(p, n);
+
+ return safe_atou64(nr, ret);
+}
diff --git a/src/basic/procfs-util.h b/src/basic/procfs-util.h
new file mode 100644
index 0000000000..7466acd7f3
--- /dev/null
+++ b/src/basic/procfs-util.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include <inttypes.h>
+
+int procfs_tasks_get_limit(uint64_t *ret);
+int procfs_tasks_set_limit(uint64_t limit);
+int procfs_tasks_get_current(uint64_t *ret);
diff --git a/src/basic/random-util.c b/src/basic/random-util.c
index 1bc8000896..7457815fa2 100644
--- a/src/basic/random-util.c
+++ b/src/basic/random-util.c
@@ -21,11 +21,12 @@
#include <elf.h>
#include <errno.h>
#include <fcntl.h>
+#include <linux/random.h>
#include <stdbool.h>
+#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/time.h>
-#include <linux/random.h>
-#include <stdint.h>
#if HAVE_SYS_AUXV_H
# include <sys/auxv.h>
diff --git a/src/basic/raw-clone.h b/src/basic/raw-clone.h
index f01b73a8fe..8c95380305 100644
--- a/src/basic/raw-clone.h
+++ b/src/basic/raw-clone.h
@@ -30,28 +30,27 @@
* raw_clone() - uses clone to create a new process with clone flags
* @flags: Flags to pass to the clone system call
*
- * Uses the clone system call to create a new process with the cloning
- * flags and termination signal passed in the flags parameter. Opposed
- * to glibc's clone funtion, using this function does not set up a
- * separate stack for the child, but relies on copy-on-write semantics
- * on the one stack at a common virtual address, just as fork does.
+ * Uses the clone system call to create a new process with the cloning flags and termination signal passed in the flags
+ * parameter. Opposed to glibc's clone funtion, using this function does not set up a separate stack for the child, but
+ * relies on copy-on-write semantics on the one stack at a common virtual address, just as fork does.
*
- * To obtain copy-on-write semantics, flags must not contain CLONE_VM,
- * and thus CLONE_THREAD and CLONE_SIGHAND (which require CLONE_VM) are
- * not usabale.
- * Additionally, as this function does not pass the ptid, newtls and ctid
- * parameters to the kernel, flags must not contain CLONE_PARENT_SETTID,
- * CLONE_CHILD_SETTID, CLONE_CHILD_CLEARTID or CLONE_SETTLS.
+ * To obtain copy-on-write semantics, flags must not contain CLONE_VM, and thus CLONE_THREAD and CLONE_SIGHAND (which
+ * require CLONE_VM) are not usable.
+ *
+ * Additionally, as this function does not pass the ptid, newtls and ctid parameters to the kernel, flags must not
+ * contain CLONE_PARENT_SETTID, CLONE_CHILD_SETTID, CLONE_CHILD_CLEARTID or CLONE_SETTLS.
*
* Returns: 0 in the child process and the child process id in the parent.
*/
-static inline int raw_clone(unsigned long flags) {
+static inline pid_t raw_clone(unsigned long flags) {
+ pid_t ret;
+
assert((flags & (CLONE_VM|CLONE_PARENT_SETTID|CLONE_CHILD_SETTID|
CLONE_CHILD_CLEARTID|CLONE_SETTLS)) == 0);
#if defined(__s390x__) || defined(__s390__) || defined(__CRIS__)
/* On s390/s390x and cris the order of the first and second arguments
* of the raw clone() system call is reversed. */
- return (int) syscall(__NR_clone, NULL, flags);
+ ret = (pid_t) syscall(__NR_clone, NULL, flags);
#elif defined(__sparc__) && defined(__arch64__)
{
/**
@@ -60,8 +59,8 @@ static inline int raw_clone(unsigned long flags) {
* %o1. Inline assembly is needed to get the flag returned
* in %o1.
*/
- int in_child;
- int child_pid;
+ int in_child, child_pid;
+
asm volatile("mov %2, %%g1\n\t"
"mov %3, %%o0\n\t"
"mov 0 , %%o1\n\t"
@@ -71,12 +70,15 @@ static inline int raw_clone(unsigned long flags) {
"=r"(in_child), "=r"(child_pid) :
"i"(__NR_clone), "r"(flags) :
"%o1", "%o0", "%g1" );
- if (in_child)
- return 0;
- else
- return child_pid;
+
+ ret = in_child ? 0 : child_pid;
}
#else
- return (int) syscall(__NR_clone, flags, NULL);
+ ret = (pid_t) syscall(__NR_clone, flags, NULL);
#endif
+
+ if (ret == 0)
+ reset_cached_pid();
+
+ return ret;
}
diff --git a/src/basic/rm-rf.h b/src/basic/rm-rf.h
index 1127e326b2..ad63e9be40 100644
--- a/src/basic/rm-rf.h
+++ b/src/basic/rm-rf.h
@@ -22,6 +22,8 @@
#include <sys/stat.h>
+#include "util.h"
+
typedef enum RemoveFlags {
REMOVE_ONLY_DIRECTORIES = 1,
REMOVE_ROOT = 2,
@@ -34,6 +36,7 @@ int rm_rf(const char *path, RemoveFlags flags);
/* Useful for usage with _cleanup_(), destroys a directory and frees the pointer */
static inline void rm_rf_physical_and_free(char *p) {
+ PROTECT_ERRNO;
(void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL);
free(p);
}
diff --git a/src/basic/securebits-util.c b/src/basic/securebits-util.c
index b5f6418a6c..441d386f9e 100644
--- a/src/basic/securebits-util.c
+++ b/src/basic/securebits-util.c
@@ -19,6 +19,7 @@
***/
#include <errno.h>
+#include <stdio.h>
#include "alloc-util.h"
#include "extract-word.h"
diff --git a/src/basic/securebits-util.h b/src/basic/securebits-util.h
index aaa192f0a5..069d215488 100644
--- a/src/basic/securebits-util.h
+++ b/src/basic/securebits-util.h
@@ -24,6 +24,14 @@
int secure_bits_to_string_alloc(int i, char **s);
int secure_bits_from_string(const char *s);
+
static inline bool secure_bits_is_valid(int i) {
return ((SECURE_ALL_BITS | SECURE_ALL_LOCKS) & i) == i;
}
+
+static inline int secure_bits_to_string_alloc_with_check(int n, char **s) {
+ if (!secure_bits_is_valid(n))
+ return -EINVAL;
+
+ return secure_bits_to_string_alloc(n, s);
+}
diff --git a/src/basic/signal-util.h b/src/basic/signal-util.h
index 76b239b1fc..f6c3396ebe 100644
--- a/src/basic/signal-util.h
+++ b/src/basic/signal-util.h
@@ -55,3 +55,10 @@ static inline void block_signals_reset(sigset_t *ss) {
static inline bool SIGNAL_VALID(int signo) {
return signo > 0 && signo < _NSIG;
}
+
+static inline const char* signal_to_string_with_check(int n) {
+ if (!SIGNAL_VALID(n))
+ return NULL;
+
+ return signal_to_string(n);
+}
diff --git a/src/basic/smack-util.c b/src/basic/smack-util.c
index e0f1c9f1c7..f0018f013f 100644
--- a/src/basic/smack-util.c
+++ b/src/basic/smack-util.c
@@ -136,7 +136,7 @@ int mac_smack_apply_pid(pid_t pid, const char *label) {
int mac_smack_fix(const char *path, bool ignore_enoent, bool ignore_erofs) {
struct stat st;
- int r = 0;
+ int r;
assert(path);
diff --git a/src/basic/socket-label.c b/src/basic/socket-label.c
index 20be406371..97f3ebe2af 100644
--- a/src/basic/socket-label.c
+++ b/src/basic/socket-label.c
@@ -29,6 +29,7 @@
#include "alloc-util.h"
#include "fd-util.h"
+#include "fs-util.h"
#include "log.h"
#include "macro.h"
#include "missing.h"
@@ -51,6 +52,7 @@ int socket_address_listen(
const char *label) {
_cleanup_close_ int fd = -1;
+ const char *p;
int r, one;
assert(a);
@@ -112,19 +114,23 @@ int socket_address_listen(
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
return -errno;
- if (socket_address_family(a) == AF_UNIX && a->sockaddr.un.sun_path[0] != 0) {
+ p = socket_address_get_path(a);
+ if (p) {
/* Create parents */
- (void) mkdir_parents_label(a->sockaddr.un.sun_path, directory_mode);
+ (void) mkdir_parents_label(p, directory_mode);
/* Enforce the right access mode for the socket */
RUN_WITH_UMASK(~socket_mode) {
r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size);
if (r == -EADDRINUSE) {
/* Unlink and try again */
- unlink(a->sockaddr.un.sun_path);
- if (bind(fd, &a->sockaddr.sa, a->size) < 0)
- return -errno;
- } else if (r < 0)
+
+ if (unlink(p) < 0)
+ return r; /* didn't work, return original error */
+
+ r = mac_selinux_bind(fd, &a->sockaddr.sa, a->size);
+ }
+ if (r < 0)
return r;
}
} else {
@@ -136,6 +142,11 @@ int socket_address_listen(
if (listen(fd, backlog) < 0)
return -errno;
+ /* Let's trigger an inotify event on the socket node, so that anyone waiting for this socket to be connectable
+ * gets notified */
+ if (p)
+ (void) touch(p);
+
r = fd;
fd = -1;
diff --git a/src/basic/socket-protocol-list.c b/src/basic/socket-protocol-list.c
new file mode 100644
index 0000000000..9ab93d1c7e
--- /dev/null
+++ b/src/basic/socket-protocol-list.c
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+/***
+ This file is part of systemd.
+
+ Copyright 2014 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <netinet/in.h>
+#include <string.h>
+
+#include "socket-protocol-list.h"
+#include "macro.h"
+
+static const struct socket_protocol_name* lookup_socket_protocol(register const char *str, register GPERF_LEN_TYPE len);
+
+#include "socket-protocol-from-name.h"
+#include "socket-protocol-to-name.h"
+
+const char *socket_protocol_to_name(int id) {
+
+ if (id < 0)
+ return NULL;
+
+ if (id >= (int) ELEMENTSOF(socket_protocol_names))
+ return NULL;
+
+ return socket_protocol_names[id];
+}
+
+int socket_protocol_from_name(const char *name) {
+ const struct socket_protocol_name *sc;
+
+ assert(name);
+
+ sc = lookup_socket_protocol(name, strlen(name));
+ if (!sc)
+ return 0;
+
+ return sc->id;
+}
+
+int socket_protocol_max(void) {
+ return ELEMENTSOF(socket_protocol_names);
+}
diff --git a/src/basic/socket-protocol-list.h b/src/basic/socket-protocol-list.h
new file mode 100644
index 0000000000..12fd053382
--- /dev/null
+++ b/src/basic/socket-protocol-list.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+const char *socket_protocol_to_name(int id);
+int socket_protocol_from_name(const char *name);
+
+int socket_protocol_max(void);
diff --git a/src/basic/socket-protocol-to-name.awk b/src/basic/socket-protocol-to-name.awk
new file mode 100644
index 0000000000..4848a7631a
--- /dev/null
+++ b/src/basic/socket-protocol-to-name.awk
@@ -0,0 +1,9 @@
+BEGIN{
+ print "static const char* const socket_protocol_names[] = { "
+}
+!/HOPOPTS/ {
+ printf " [IPPROTO_%s] = \"%s\",\n", $1, tolower($1)
+}
+END{
+ print "};"
+}
diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c
index a458fc2902..2c70cade14 100644
--- a/src/basic/socket-util.c
+++ b/src/basic/socket-util.c
@@ -41,6 +41,7 @@
#include "missing.h"
#include "parse-util.h"
#include "path-util.h"
+#include "process-util.h"
#include "socket-util.h"
#include "string-table.h"
#include "string-util.h"
@@ -55,9 +56,19 @@
# define IDN_FLAGS 0
#endif
+static const char* const socket_address_type_table[] = {
+ [SOCK_STREAM] = "Stream",
+ [SOCK_DGRAM] = "Datagram",
+ [SOCK_RAW] = "Raw",
+ [SOCK_RDM] = "ReliableDatagram",
+ [SOCK_SEQPACKET] = "SequentialPacket",
+ [SOCK_DCCP] = "DatagramCongestionControl",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(socket_address_type, int);
+
int socket_address_parse(SocketAddress *a, const char *s) {
char *e, *n;
- unsigned u;
int r;
assert(a);
@@ -67,6 +78,8 @@ int socket_address_parse(SocketAddress *a, const char *s) {
a->type = SOCK_STREAM;
if (*s == '[') {
+ uint16_t port;
+
/* IPv6 in [x:.....:z]:p notation */
e = strchr(s+1, ']');
@@ -84,15 +97,12 @@ int socket_address_parse(SocketAddress *a, const char *s) {
return -EINVAL;
e++;
- r = safe_atou(e, &u);
+ r = parse_ip_port(e, &port);
if (r < 0)
return r;
- if (u <= 0 || u > 0xFFFF)
- return -EINVAL;
-
a->sockaddr.in6.sin6_family = AF_INET6;
- a->sockaddr.in6.sin6_port = htobe16((uint16_t)u);
+ a->sockaddr.in6.sin6_port = htobe16(port);
a->size = sizeof(struct sockaddr_in6);
} else if (*s == '/') {
@@ -123,12 +133,13 @@ int socket_address_parse(SocketAddress *a, const char *s) {
} else if (startswith(s, "vsock:")) {
/* AF_VSOCK socket in vsock:cid:port notation */
const char *cid_start = s + STRLEN("vsock:");
+ unsigned port;
e = strchr(cid_start, ':');
if (!e)
return -EINVAL;
- r = safe_atou(e+1, &u);
+ r = safe_atou(e+1, &port);
if (r < 0)
return r;
@@ -141,19 +152,18 @@ int socket_address_parse(SocketAddress *a, const char *s) {
a->sockaddr.vm.svm_cid = VMADDR_CID_ANY;
a->sockaddr.vm.svm_family = AF_VSOCK;
- a->sockaddr.vm.svm_port = u;
+ a->sockaddr.vm.svm_port = port;
a->size = sizeof(struct sockaddr_vm);
} else {
+ uint16_t port;
+
e = strchr(s, ':');
if (e) {
- r = safe_atou(e+1, &u);
+ r = parse_ip_port(e + 1, &port);
if (r < 0)
return r;
- if (u <= 0 || u > 0xFFFF)
- return -EINVAL;
-
n = strndupa(s, e-s);
/* IPv4 in w.x.y.z:p notation? */
@@ -164,7 +174,7 @@ int socket_address_parse(SocketAddress *a, const char *s) {
if (r > 0) {
/* Gotcha, it's a traditional IPv4 address */
a->sockaddr.in.sin_family = AF_INET;
- a->sockaddr.in.sin_port = htobe16((uint16_t)u);
+ a->sockaddr.in.sin_port = htobe16(port);
a->size = sizeof(struct sockaddr_in);
} else {
unsigned idx;
@@ -178,7 +188,7 @@ int socket_address_parse(SocketAddress *a, const char *s) {
return -EINVAL;
a->sockaddr.in6.sin6_family = AF_INET6;
- a->sockaddr.in6.sin6_port = htobe16((uint16_t)u);
+ a->sockaddr.in6.sin6_port = htobe16(port);
a->sockaddr.in6.sin6_scope_id = idx;
a->sockaddr.in6.sin6_addr = in6addr_any;
a->size = sizeof(struct sockaddr_in6);
@@ -186,21 +196,18 @@ int socket_address_parse(SocketAddress *a, const char *s) {
} else {
/* Just a port */
- r = safe_atou(s, &u);
+ r = parse_ip_port(s, &port);
if (r < 0)
return r;
- if (u <= 0 || u > 0xFFFF)
- return -EINVAL;
-
if (socket_ipv6_is_supported()) {
a->sockaddr.in6.sin6_family = AF_INET6;
- a->sockaddr.in6.sin6_port = htobe16((uint16_t)u);
+ a->sockaddr.in6.sin6_port = htobe16(port);
a->sockaddr.in6.sin6_addr = in6addr_any;
a->size = sizeof(struct sockaddr_in6);
} else {
a->sockaddr.in.sin_family = AF_INET;
- a->sockaddr.in.sin_port = htobe16((uint16_t)u);
+ a->sockaddr.in.sin_port = htobe16(port);
a->sockaddr.in.sin_addr.s_addr = INADDR_ANY;
a->size = sizeof(struct sockaddr_in);
}
@@ -528,22 +535,25 @@ bool socket_address_matches_fd(const SocketAddress *a, int fd) {
return socket_address_equal(a, &b);
}
-int sockaddr_port(const struct sockaddr *_sa, unsigned *port) {
+int sockaddr_port(const struct sockaddr *_sa, unsigned *ret_port) {
union sockaddr_union *sa = (union sockaddr_union*) _sa;
+ /* Note, this returns the port as 'unsigned' rather than 'uint16_t', as AF_VSOCK knows larger ports */
+
assert(sa);
switch (sa->sa.sa_family) {
+
case AF_INET:
- *port = be16toh(sa->in.sin_port);
+ *ret_port = be16toh(sa->in.sin_port);
return 0;
case AF_INET6:
- *port = be16toh(sa->in6.sin6_port);
+ *ret_port = be16toh(sa->in6.sin6_port);
return 0;
case AF_VSOCK:
- *port = sa->vm.svm_port;
+ *ret_port = sa->vm.svm_port;
return 0;
default:
@@ -807,6 +817,18 @@ static const char* const socket_address_bind_ipv6_only_table[_SOCKET_ADDRESS_BIN
DEFINE_STRING_TABLE_LOOKUP(socket_address_bind_ipv6_only, SocketAddressBindIPv6Only);
+SocketAddressBindIPv6Only parse_socket_address_bind_ipv6_only_or_bool(const char *n) {
+ int r;
+
+ r = parse_boolean(n);
+ if (r > 0)
+ return SOCKET_ADDRESS_IPV6_ONLY;
+ if (r == 0)
+ return SOCKET_ADDRESS_BOTH;
+
+ return socket_address_bind_ipv6_only_from_string(n);
+}
+
bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b) {
assert(a);
assert(b);
@@ -941,58 +963,80 @@ int getpeercred(int fd, struct ucred *ucred) {
if (n != sizeof(struct ucred))
return -EIO;
- /* Check if the data is actually useful and not suppressed due
- * to namespacing issues */
- if (u.pid <= 0)
- return -ENODATA;
- if (u.uid == UID_INVALID)
- return -ENODATA;
- if (u.gid == GID_INVALID)
+ /* Check if the data is actually useful and not suppressed due to namespacing issues */
+ if (!pid_is_valid(u.pid))
return -ENODATA;
+ /* Note that we don't check UID/GID here, as namespace translation works differently there: instead of
+ * receiving in "invalid" user/group we get the overflow UID/GID. */
+
*ucred = u;
return 0;
}
int getpeersec(int fd, char **ret) {
+ _cleanup_free_ char *s = NULL;
socklen_t n = 64;
- char *s;
- int r;
assert(fd >= 0);
assert(ret);
- s = new0(char, n);
- if (!s)
- return -ENOMEM;
+ for (;;) {
+ s = new0(char, n+1);
+ if (!s)
+ return -ENOMEM;
- r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
- if (r < 0) {
- free(s);
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n) >= 0)
+ break;
if (errno != ERANGE)
return -errno;
- s = new0(char, n);
- if (!s)
- return -ENOMEM;
-
- r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
- if (r < 0) {
- free(s);
- return -errno;
- }
+ s = mfree(s);
}
- if (isempty(s)) {
- free(s);
+ if (isempty(s))
return -EOPNOTSUPP;
- }
*ret = s;
+ s = NULL;
+
return 0;
}
+int getpeergroups(int fd, gid_t **ret) {
+ socklen_t n = sizeof(gid_t) * 64;
+ _cleanup_free_ gid_t *d = NULL;
+
+ assert(fd);
+ assert(ret);
+
+ for (;;) {
+ d = malloc(n);
+ if (!d)
+ return -ENOMEM;
+
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERGROUPS, d, &n) >= 0)
+ break;
+
+ if (errno != ERANGE)
+ return -errno;
+
+ d = mfree(d);
+ }
+
+ assert_se(n % sizeof(gid_t) == 0);
+ n /= sizeof(gid_t);
+
+ if ((socklen_t) (int) n != n)
+ return -E2BIG;
+
+ *ret = d;
+ d = NULL;
+
+ return (int) n;
+}
+
int send_one_fd_sa(
int transport_fd,
int fd,
diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h
index 272e74b0cc..49c937aef5 100644
--- a/src/basic/socket-util.h
+++ b/src/basic/socket-util.h
@@ -36,16 +36,26 @@
#include "util.h"
union sockaddr_union {
+ /* The minimal, abstract version */
struct sockaddr sa;
+
+ /* The libc provided version that allocates "enough room" for every protocol */
+ struct sockaddr_storage storage;
+
+ /* Protoctol-specific implementations */
struct sockaddr_in in;
struct sockaddr_in6 in6;
struct sockaddr_un un;
struct sockaddr_nl nl;
- struct sockaddr_storage storage;
struct sockaddr_ll ll;
struct sockaddr_vm vm;
+
/* Ensure there is enough space to store Infiniband addresses */
uint8_t ll_buffer[offsetof(struct sockaddr_ll, sll_addr) + CONST_MAX(ETH_ALEN, INFINIBAND_ALEN)];
+
+ /* Ensure there is enough space after the AF_UNIX sun_path for one more NUL byte, just to be sure that the path
+ * component is always followed by at least one NUL byte. */
+ uint8_t un_buffer[sizeof(struct sockaddr_un) + 1];
};
typedef struct SocketAddress {
@@ -72,6 +82,9 @@ typedef enum SocketAddressBindIPv6Only {
#define socket_address_family(a) ((a)->sockaddr.sa.sa_family)
+const char* socket_address_type_to_string(int t) _const_;
+int socket_address_type_from_string(const char *s) _pure_;
+
int socket_address_parse(SocketAddress *a, const char *s);
int socket_address_parse_and_warn(SocketAddress *a, const char *s);
int socket_address_parse_netlink(SocketAddress *a, const char *s);
@@ -117,6 +130,7 @@ int getnameinfo_pretty(int fd, char **ret);
const char* socket_address_bind_ipv6_only_to_string(SocketAddressBindIPv6Only b) _const_;
SocketAddressBindIPv6Only socket_address_bind_ipv6_only_from_string(const char *s) _pure_;
+SocketAddressBindIPv6Only parse_socket_address_bind_ipv6_only_or_bool(const char *s);
int netlink_family_to_string_alloc(int b, char **s);
int netlink_family_from_string(const char *s) _pure_;
@@ -134,6 +148,7 @@ bool address_label_valid(const char *p);
int getpeercred(int fd, struct ucred *ucred);
int getpeersec(int fd, char **ret);
+int getpeergroups(int fd, gid_t **ret);
int send_one_fd_sa(int transport_fd,
int fd,
diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c
index 96fc8b3787..3a54103f1b 100644
--- a/src/basic/stat-util.c
+++ b/src/basic/stat-util.c
@@ -21,10 +21,11 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
#include <linux/magic.h>
+#include <sched.h>
+#include <sys/stat.h>
#include <sys/statvfs.h>
+#include <sys/types.h>
#include <unistd.h>
#include "dirent-util.h"
diff --git a/src/basic/string-util.c b/src/basic/string-util.c
index 7e2f596edc..9f2c01d864 100644
--- a/src/basic/string-util.c
+++ b/src/basic/string-util.c
@@ -30,6 +30,7 @@
#include "gunicode.h"
#include "macro.h"
#include "string-util.h"
+#include "terminal-util.h"
#include "utf8.h"
#include "util.h"
@@ -648,7 +649,17 @@ char *strreplace(const char *text, const char *old_string, const char *new_strin
return ret;
}
-char *strip_tab_ansi(char **ibuf, size_t *_isz) {
+static void advance_offsets(ssize_t diff, size_t offsets[2], size_t shift[2], size_t size) {
+ if (!offsets)
+ return;
+
+ if ((size_t) diff < offsets[0])
+ shift[0] += size;
+ if ((size_t) diff < offsets[1])
+ shift[1] += size;
+}
+
+char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
const char *i, *begin = NULL;
enum {
STATE_OTHER,
@@ -656,7 +667,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
STATE_BRACKET
} state = STATE_OTHER;
char *obuf = NULL;
- size_t osz = 0, isz;
+ size_t osz = 0, isz, shift[2] = {};
FILE *f;
assert(ibuf);
@@ -684,15 +695,18 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
break;
else if (*i == '\x1B')
state = STATE_ESCAPE;
- else if (*i == '\t')
+ else if (*i == '\t') {
fputs(" ", f);
- else
+ advance_offsets(i - *ibuf, highlight, shift, 7);
+ } else
fputc(*i, f);
+
break;
case STATE_ESCAPE:
if (i >= *ibuf + isz) { /* EOT */
fputc('\x1B', f);
+ advance_offsets(i - *ibuf, highlight, shift, 1);
break;
} else if (*i == '[') {
state = STATE_BRACKET;
@@ -700,6 +714,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
} else {
fputc('\x1B', f);
fputc(*i, f);
+ advance_offsets(i - *ibuf, highlight, shift, 1);
state = STATE_OTHER;
}
@@ -711,6 +726,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
(!(*i >= '0' && *i <= '9') && !IN_SET(*i, ';', 'm'))) {
fputc('\x1B', f);
fputc('[', f);
+ advance_offsets(i - *ibuf, highlight, shift, 2);
state = STATE_OTHER;
i = begin-1;
} else if (*i == 'm')
@@ -732,6 +748,11 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
if (_isz)
*_isz = osz;
+ if (highlight) {
+ highlight[0] += shift[0];
+ highlight[1] += shift[1];
+ }
+
return obuf;
}
diff --git a/src/basic/string-util.h b/src/basic/string-util.h
index 09a737ad37..08eda4fce0 100644
--- a/src/basic/string-util.h
+++ b/src/basic/string-util.h
@@ -52,15 +52,15 @@ static inline bool streq_ptr(const char *a, const char *b) {
}
static inline const char* strempty(const char *s) {
- return s ? s : "";
+ return s ?: "";
}
static inline const char* strnull(const char *s) {
- return s ? s : "(null)";
+ return s ?: "(null)";
}
static inline const char *strna(const char *s) {
- return s ? s : "n/a";
+ return s ?: "n/a";
}
static inline bool isempty(const char *p) {
@@ -177,7 +177,7 @@ char* strshorten(char *s, size_t l);
char *strreplace(const char *text, const char *old_string, const char *new_string);
-char *strip_tab_ansi(char **p, size_t *l);
+char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]);
char *strextend_with_separator(char **x, const char *separator, ...) _sentinel_;
diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c
index 48ee799ad4..42336e8fdf 100644
--- a/src/basic/terminal-util.c
+++ b/src/basic/terminal-util.c
@@ -875,31 +875,30 @@ bool on_tty(void) {
}
int make_stdio(int fd) {
- int r, s, t;
+ int r = 0;
assert(fd >= 0);
- r = dup2(fd, STDIN_FILENO);
- s = dup2(fd, STDOUT_FILENO);
- t = dup2(fd, STDERR_FILENO);
+ if (dup2(fd, STDIN_FILENO) < 0 && r >= 0)
+ r = -errno;
+ if (dup2(fd, STDOUT_FILENO) < 0 && r >= 0)
+ r = -errno;
+ if (dup2(fd, STDERR_FILENO) < 0 && r >= 0)
+ r = -errno;
if (fd >= 3)
safe_close(fd);
- if (r < 0 || s < 0 || t < 0)
- return -errno;
-
- /* Explicitly unset O_CLOEXEC, since if fd was < 3, then
- * dup2() was a NOP and the bit hence possibly set. */
+ /* Explicitly unset O_CLOEXEC, since if fd was < 3, then dup2() was a NOP and the bit hence possibly set. */
stdio_unset_cloexec();
- return 0;
+ return r;
}
int make_null_stdio(void) {
int null_fd;
- null_fd = open("/dev/null", O_RDWR|O_NOCTTY);
+ null_fd = open("/dev/null", O_RDWR|O_NOCTTY|O_CLOEXEC);
if (null_fd < 0)
return -errno;
@@ -1094,7 +1093,6 @@ int ptsname_namespace(int pty, char **ret) {
int openpt_in_namespace(pid_t pid, int flags) {
_cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
_cleanup_close_pair_ int pair[2] = { -1, -1 };
- siginfo_t si;
pid_t child;
int r;
@@ -1107,11 +1105,10 @@ int openpt_in_namespace(pid_t pid, int flags) {
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
return -errno;
- child = fork();
- if (child < 0)
- return -errno;
-
- if (child == 0) {
+ r = safe_fork("(sd-openpt)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child);
+ if (r < 0)
+ return r;
+ if (r == 0) {
int master;
pair[0] = safe_close(pair[0]);
@@ -1135,10 +1132,10 @@ int openpt_in_namespace(pid_t pid, int flags) {
pair[1] = safe_close(pair[1]);
- r = wait_for_terminate(child, &si);
+ r = wait_for_terminate_and_check("(sd-openpt)", child, 0);
if (r < 0)
return r;
- if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
+ if (r != EXIT_SUCCESS)
return -EIO;
return receive_one_fd(pair[0], 0);
@@ -1147,7 +1144,6 @@ int openpt_in_namespace(pid_t pid, int flags) {
int open_terminal_in_namespace(pid_t pid, const char *name, int mode) {
_cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
_cleanup_close_pair_ int pair[2] = { -1, -1 };
- siginfo_t si;
pid_t child;
int r;
@@ -1158,11 +1154,10 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode) {
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
return -errno;
- child = fork();
- if (child < 0)
- return -errno;
-
- if (child == 0) {
+ r = safe_fork("(sd-terminal)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &child);
+ if (r < 0)
+ return r;
+ if (r == 0) {
int master;
pair[0] = safe_close(pair[0]);
@@ -1183,10 +1178,10 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode) {
pair[1] = safe_close(pair[1]);
- r = wait_for_terminate(child, &si);
+ r = wait_for_terminate_and_check("(sd-terminal)", child, 0);
if (r < 0)
return r;
- if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS)
+ if (r != EXIT_SUCCESS)
return -EIO;
return receive_one_fd(pair[0], 0);
diff --git a/src/basic/time-util.c b/src/basic/time-util.c
index d56576ddbe..4a341e208f 100644
--- a/src/basic/time-util.c
+++ b/src/basic/time-util.c
@@ -38,6 +38,7 @@
#include "macro.h"
#include "parse-util.h"
#include "path-util.h"
+#include "process-util.h"
#include "string-util.h"
#include "strv.h"
#include "time-util.h"
@@ -887,7 +888,6 @@ int parse_timestamp(const char *t, usec_t *usec) {
char *last_space, *tz = NULL;
ParseTimestampResult *shared, tmp;
int r;
- pid_t pid;
last_space = strrchr(t, ' ');
if (last_space != NULL && timezone_is_valid(last_space + 1))
@@ -900,15 +900,12 @@ int parse_timestamp(const char *t, usec_t *usec) {
if (shared == MAP_FAILED)
return negative_errno();
- pid = fork();
-
- if (pid == -1) {
- int fork_errno = errno;
+ r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL);
+ if (r < 0) {
(void) munmap(shared, sizeof *shared);
- return -fork_errno;
+ return r;
}
-
- if (pid == 0) {
+ if (r == 0) {
bool with_tz = true;
if (setenv("TZ", tz, 1) != 0) {
@@ -931,12 +928,6 @@ int parse_timestamp(const char *t, usec_t *usec) {
_exit(EXIT_SUCCESS);
}
- r = wait_for_terminate(pid, NULL);
- if (r < 0) {
- (void) munmap(shared, sizeof *shared);
- return r;
- }
-
tmp = *shared;
if (munmap(shared, sizeof *shared) != 0)
return negative_errno();
diff --git a/src/basic/unaligned.h b/src/basic/unaligned.h
index 201b3d227e..73302b4239 100644
--- a/src/basic/unaligned.h
+++ b/src/basic/unaligned.h
@@ -26,89 +26,77 @@
/* BE */
static inline uint16_t unaligned_read_be16(const void *_u) {
- const uint8_t *u = _u;
+ const struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u;
- return (((uint16_t) u[0]) << 8) |
- ((uint16_t) u[1]);
+ return be16toh(u->x);
}
static inline uint32_t unaligned_read_be32(const void *_u) {
- const uint8_t *u = _u;
+ const struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u;
- return (((uint32_t) unaligned_read_be16(u)) << 16) |
- ((uint32_t) unaligned_read_be16(u + 2));
+ return be32toh(u->x);
}
static inline uint64_t unaligned_read_be64(const void *_u) {
- const uint8_t *u = _u;
+ const struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u;
- return (((uint64_t) unaligned_read_be32(u)) << 32) |
- ((uint64_t) unaligned_read_be32(u + 4));
+ return be64toh(u->x);
}
static inline void unaligned_write_be16(void *_u, uint16_t a) {
- uint8_t *u = _u;
+ struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u;
- u[0] = (uint8_t) (a >> 8);
- u[1] = (uint8_t) a;
+ u->x = be16toh(a);
}
static inline void unaligned_write_be32(void *_u, uint32_t a) {
- uint8_t *u = _u;
+ struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u;
- unaligned_write_be16(u, (uint16_t) (a >> 16));
- unaligned_write_be16(u + 2, (uint16_t) a);
+ u->x = be32toh(a);
}
static inline void unaligned_write_be64(void *_u, uint64_t a) {
- uint8_t *u = _u;
+ struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u;
- unaligned_write_be32(u, (uint32_t) (a >> 32));
- unaligned_write_be32(u + 4, (uint32_t) a);
+ u->x = be64toh(a);
}
/* LE */
static inline uint16_t unaligned_read_le16(const void *_u) {
- const uint8_t *u = _u;
+ const struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u;
- return (((uint16_t) u[1]) << 8) |
- ((uint16_t) u[0]);
+ return le16toh(u->x);
}
static inline uint32_t unaligned_read_le32(const void *_u) {
- const uint8_t *u = _u;
+ const struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u;
- return (((uint32_t) unaligned_read_le16(u + 2)) << 16) |
- ((uint32_t) unaligned_read_le16(u));
+ return le32toh(u->x);
}
static inline uint64_t unaligned_read_le64(const void *_u) {
- const uint8_t *u = _u;
+ const struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u;
- return (((uint64_t) unaligned_read_le32(u + 4)) << 32) |
- ((uint64_t) unaligned_read_le32(u));
+ return le64toh(u->x);
}
static inline void unaligned_write_le16(void *_u, uint16_t a) {
- uint8_t *u = _u;
+ struct __attribute__((packed, may_alias)) { uint16_t x; } *u = _u;
- u[0] = (uint8_t) a;
- u[1] = (uint8_t) (a >> 8);
+ u->x = le16toh(a);
}
static inline void unaligned_write_le32(void *_u, uint32_t a) {
- uint8_t *u = _u;
+ struct __attribute__((packed, may_alias)) { uint32_t x; } *u = _u;
- unaligned_write_le16(u, (uint16_t) a);
- unaligned_write_le16(u + 2, (uint16_t) (a >> 16));
+ u->x = le32toh(a);
}
static inline void unaligned_write_le64(void *_u, uint64_t a) {
- uint8_t *u = _u;
+ struct __attribute__((packed, may_alias)) { uint64_t x; } *u = _u;
- unaligned_write_le32(u, (uint32_t) a);
- unaligned_write_le32(u + 4, (uint32_t) (a >> 32));
+ u->x = le64toh(a);
}
#if __BYTE_ORDER == __BIG_ENDIAN
diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c
index 403f288b57..0fa0472ee1 100644
--- a/src/basic/unit-name.c
+++ b/src/basic/unit-name.c
@@ -28,6 +28,7 @@
#include "glob-util.h"
#include "hexdecoct.h"
#include "path-util.h"
+#include "special.h"
#include "string-util.h"
#include "strv.h"
#include "unit-name.h"
@@ -673,7 +674,7 @@ int slice_build_parent_slice(const char *slice, char **ret) {
if (!slice_name_is_valid(slice))
return -EINVAL;
- if (streq(slice, "-.slice")) {
+ if (streq(slice, SPECIAL_ROOT_SLICE)) {
*ret = NULL;
return 0;
}
@@ -686,7 +687,7 @@ int slice_build_parent_slice(const char *slice, char **ret) {
if (dash)
strcpy(dash, ".slice");
else {
- r = free_and_strdup(&s, "-.slice");
+ r = free_and_strdup(&s, SPECIAL_ROOT_SLICE);
if (r < 0) {
free(s);
return r;
@@ -710,7 +711,7 @@ int slice_build_subslice(const char *slice, const char*name, char **ret) {
if (!unit_prefix_is_valid(name))
return -EINVAL;
- if (streq(slice, "-.slice"))
+ if (streq(slice, SPECIAL_ROOT_SLICE))
subslice = strappend(name, ".slice");
else {
char *e;
@@ -735,7 +736,7 @@ bool slice_name_is_valid(const char *name) {
if (!unit_name_is_valid(name, UNIT_NAME_PLAIN))
return false;
- if (streq(name, "-.slice"))
+ if (streq(name, SPECIAL_ROOT_SLICE))
return true;
e = endswith(name, ".slice");
diff --git a/src/basic/user-util.c b/src/basic/user-util.c
index abb0b76866..17a9b5a8f1 100644
--- a/src/basic/user-util.c
+++ b/src/basic/user-util.c
@@ -137,7 +137,8 @@ int get_user_creds(
return 0;
}
- if (STR_IN_SET(*username, NOBODY_USER_NAME, "65534")) {
+ if (synthesize_nobody() &&
+ STR_IN_SET(*username, NOBODY_USER_NAME, "65534")) {
*username = NOBODY_USER_NAME;
if (uid)
@@ -243,7 +244,8 @@ int get_group_creds(const char **groupname, gid_t *gid) {
return 0;
}
- if (STR_IN_SET(*groupname, NOBODY_GROUP_NAME, "65534")) {
+ if (synthesize_nobody() &&
+ STR_IN_SET(*groupname, NOBODY_GROUP_NAME, "65534")) {
*groupname = NOBODY_GROUP_NAME;
if (gid)
@@ -283,7 +285,8 @@ char* uid_to_name(uid_t uid) {
/* Shortcut things to avoid NSS lookups */
if (uid == 0)
return strdup("root");
- if (uid == UID_NOBODY)
+ if (synthesize_nobody() &&
+ uid == UID_NOBODY)
return strdup(NOBODY_USER_NAME);
if (uid_is_valid(uid)) {
@@ -323,7 +326,8 @@ char* gid_to_name(gid_t gid) {
if (gid == 0)
return strdup("root");
- if (gid == GID_NOBODY)
+ if (synthesize_nobody() &&
+ gid == GID_NOBODY)
return strdup(NOBODY_GROUP_NAME);
if (gid_is_valid(gid)) {
@@ -358,8 +362,9 @@ char* gid_to_name(gid_t gid) {
}
int in_gid(gid_t gid) {
+ long ngroups_max;
gid_t *gids;
- int ngroups_max, r, i;
+ int r, i;
if (getgid() == gid)
return 1;
@@ -373,7 +378,7 @@ int in_gid(gid_t gid) {
ngroups_max = sysconf(_SC_NGROUPS_MAX);
assert(ngroups_max > 0);
- gids = alloca(sizeof(gid_t) * ngroups_max);
+ gids = newa(gid_t, ngroups_max);
r = getgroups(ngroups_max, gids);
if (r < 0)
@@ -426,7 +431,8 @@ int get_home_dir(char **_h) {
*_h = h;
return 0;
}
- if (u == UID_NOBODY) {
+ if (synthesize_nobody() &&
+ u == UID_NOBODY) {
h = strdup("/");
if (!h)
return -ENOMEM;
@@ -481,7 +487,8 @@ int get_shell(char **_s) {
*_s = s;
return 0;
}
- if (u == UID_NOBODY) {
+ if (synthesize_nobody() &&
+ u == UID_NOBODY) {
s = strdup("/sbin/nologin");
if (!s)
return -ENOMEM;
@@ -689,3 +696,24 @@ int maybe_setgroups(size_t size, const gid_t *list) {
return 0;
}
+
+bool synthesize_nobody(void) {
+
+#ifdef NOLEGACY
+ return true;
+#else
+ /* Returns true when we shall synthesize the "nobody" user (which we do by default). This can be turned off by
+ * touching /etc/systemd/dont-synthesize-nobody in order to provide upgrade compatibility with legacy systems
+ * that used the "nobody" user name and group name for other UIDs/GIDs than 65534.
+ *
+ * Note that we do not employ any kind of synchronization on the following caching variable. If the variable is
+ * accessed in multi-threaded programs in the worst case it might happen that we initialize twice, but that
+ * shouldn't matter as each initialization should come to the same result. */
+ static int cache = -1;
+
+ if (cache < 0)
+ cache = access("/etc/systemd/dont-synthesize-nobody", F_OK) < 0;
+
+ return cache;
+#endif
+}
diff --git a/src/basic/user-util.h b/src/basic/user-util.h
index 79adf91ee9..5f0391f2b8 100644
--- a/src/basic/user-util.h
+++ b/src/basic/user-util.h
@@ -97,3 +97,5 @@ bool valid_gecos(const char *d);
bool valid_home(const char *p);
int maybe_setgroups(size_t size, const gid_t *list);
+
+bool synthesize_nobody(void);
diff --git a/src/basic/util.c b/src/basic/util.c
index 8f9f2b902b..c7f1513f3e 100644
--- a/src/basic/util.c
+++ b/src/basic/util.c
@@ -52,6 +52,7 @@
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
+#include "procfs-util.h"
#include "set.h"
#include "signal-util.h"
#include "stat-util.h"
@@ -61,6 +62,7 @@
#include "umask-util.h"
#include "user-util.h"
#include "util.h"
+#include "virt.h"
int saved_argc = 0;
char **saved_argv = NULL;
@@ -118,45 +120,6 @@ int socket_from_display(const char *display, char **path) {
return 0;
}
-int block_get_whole_disk(dev_t d, dev_t *ret) {
- char p[SYS_BLOCK_PATH_MAX("/partition")];
- _cleanup_free_ char *s = NULL;
- int r;
- unsigned n, m;
-
- assert(ret);
-
- /* If it has a queue this is good enough for us */
- xsprintf_sys_block_path(p, "/queue", d);
- if (access(p, F_OK) >= 0) {
- *ret = d;
- return 0;
- }
-
- /* If it is a partition find the originating device */
- xsprintf_sys_block_path(p, "/partition", d);
- if (access(p, F_OK) < 0)
- return -ENOENT;
-
- /* Get parent dev_t */
- xsprintf_sys_block_path(p, "/../dev", d);
- r = read_one_line_file(p, &s);
- if (r < 0)
- return r;
-
- r = sscanf(s, "%u:%u", &m, &n);
- if (r != 2)
- return -EINVAL;
-
- /* Only return this if it is really good enough for us. */
- xsprintf_sys_block_path(p, "/queue", makedev(m, n));
- if (access(p, F_OK) < 0)
- return -ENOENT;
-
- *ret = makedev(m, n);
- return 0;
-}
-
bool kexec_loaded(void) {
_cleanup_free_ char *s = NULL;
@@ -184,112 +147,6 @@ int prot_from_flags(int flags) {
}
}
-int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...) {
- bool stdout_is_tty, stderr_is_tty;
- pid_t parent_pid, agent_pid;
- sigset_t ss, saved_ss;
- unsigned n, i;
- va_list ap;
- char **l;
-
- assert(pid);
- assert(path);
-
- /* Spawns a temporary TTY agent, making sure it goes away when
- * we go away */
-
- parent_pid = getpid_cached();
-
- /* First we temporarily block all signals, so that the new
- * child has them blocked initially. This way, we can be sure
- * that SIGTERMs are not lost we might send to the agent. */
- assert_se(sigfillset(&ss) >= 0);
- assert_se(sigprocmask(SIG_SETMASK, &ss, &saved_ss) >= 0);
-
- agent_pid = fork();
- if (agent_pid < 0) {
- assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
- return -errno;
- }
-
- if (agent_pid != 0) {
- assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
- *pid = agent_pid;
- return 0;
- }
-
- /* In the child:
- *
- * Make sure the agent goes away when the parent dies */
- if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
- _exit(EXIT_FAILURE);
-
- /* Make sure we actually can kill the agent, if we need to, in
- * case somebody invoked us from a shell script that trapped
- * SIGTERM or so... */
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
-
- /* Check whether our parent died before we were able
- * to set the death signal and unblock the signals */
- if (getppid() != parent_pid)
- _exit(EXIT_SUCCESS);
-
- /* Don't leak fds to the agent */
- close_all_fds(except, n_except);
-
- stdout_is_tty = isatty(STDOUT_FILENO);
- stderr_is_tty = isatty(STDERR_FILENO);
-
- if (!stdout_is_tty || !stderr_is_tty) {
- int fd;
-
- /* Detach from stdout/stderr. and reopen
- * /dev/tty for them. This is important to
- * ensure that when systemctl is started via
- * popen() or a similar call that expects to
- * read EOF we actually do generate EOF and
- * not delay this indefinitely by because we
- * keep an unused copy of stdin around. */
- fd = open("/dev/tty", O_WRONLY);
- if (fd < 0) {
- log_error_errno(errno, "Failed to open /dev/tty: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) {
- log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) {
- log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (fd > STDERR_FILENO)
- close(fd);
- }
-
- /* Count arguments */
- va_start(ap, path);
- for (n = 0; va_arg(ap, char*); n++)
- ;
- va_end(ap);
-
- /* Allocate strv */
- l = alloca(sizeof(char *) * (n + 1));
-
- /* Fill in arguments */
- va_start(ap, path);
- for (i = 0; i <= n; i++)
- l[i] = va_arg(ap, char*);
- va_end(ap);
-
- execv(path, l);
- _exit(EXIT_FAILURE);
-}
-
bool in_initrd(void) {
struct statfs s;
@@ -617,31 +474,22 @@ uint64_t physical_memory_scale(uint64_t v, uint64_t max) {
uint64_t system_tasks_max(void) {
-#if SIZEOF_PID_T == 4
-#define TASKS_MAX ((uint64_t) (INT32_MAX-1))
-#elif SIZEOF_PID_T == 2
-#define TASKS_MAX ((uint64_t) (INT16_MAX-1))
-#else
-#error "Unknown pid_t size"
-#endif
-
- _cleanup_free_ char *value = NULL, *root = NULL;
uint64_t a = TASKS_MAX, b = TASKS_MAX;
+ _cleanup_free_ char *root = NULL;
/* Determine the maximum number of tasks that may run on this system. We check three sources to determine this
* limit:
*
- * a) the maximum value for the pid_t type
+ * a) the maximum tasks value the kernel allows on this architecture
* b) the cgroups pids_max attribute for the system
- * c) the kernel's configure maximum PID value
+ * c) the kernel's configured maximum PID value
*
* And then pick the smallest of the three */
- if (read_one_line_file("/proc/sys/kernel/pid_max", &value) >= 0)
- (void) safe_atou64(value, &a);
+ (void) procfs_tasks_get_limit(&a);
if (cg_get_root_path(&root) >= 0) {
- value = mfree(value);
+ _cleanup_free_ char *value = NULL;
if (cg_get_attribute("pids", root, "pids.max", &value) >= 0)
(void) safe_atou64(value, &b);
@@ -699,131 +547,76 @@ int version(void) {
return 0;
}
-int get_block_device(const char *path, dev_t *dev) {
- struct stat st;
- struct statfs sfs;
-
- assert(path);
- assert(dev);
-
- /* Get's the block device directly backing a file system. If
- * the block device is encrypted, returns the device mapper
- * block device. */
-
- if (lstat(path, &st))
- return -errno;
-
- if (major(st.st_dev) != 0) {
- *dev = st.st_dev;
- return 1;
- }
-
- if (statfs(path, &sfs) < 0)
- return -errno;
-
- if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC))
- return btrfs_get_block_device(path, dev);
-
- return 0;
+/* This is a direct translation of str_verscmp from boot.c */
+static bool is_digit(int c) {
+ return c >= '0' && c <= '9';
}
-int get_block_device_harder(const char *path, dev_t *dev) {
- _cleanup_closedir_ DIR *d = NULL;
- _cleanup_free_ char *t = NULL;
- char p[SYS_BLOCK_PATH_MAX("/slaves")];
- struct dirent *de, *found = NULL;
- const char *q;
- unsigned maj, min;
- dev_t dt;
- int r;
-
- assert(path);
- assert(dev);
+static int c_order(int c) {
+ if (c == 0 || is_digit(c))
+ return 0;
- /* Gets the backing block device for a file system, and
- * handles LUKS encrypted file systems, looking for its
- * immediate parent, if there is one. */
+ if ((c >= 'a') && (c <= 'z'))
+ return c;
- r = get_block_device(path, &dt);
- if (r <= 0)
- return r;
+ return c + 0x10000;
+}
- xsprintf_sys_block_path(p, "/slaves", dt);
- d = opendir(p);
- if (!d) {
- if (errno == ENOENT)
- goto fallback;
+int str_verscmp(const char *s1, const char *s2) {
+ const char *os1, *os2;
- return -errno;
- }
+ assert(s1);
+ assert(s2);
- FOREACH_DIRENT_ALL(de, d, return -errno) {
+ os1 = s1;
+ os2 = s2;
- if (dot_or_dot_dot(de->d_name))
- continue;
+ while (*s1 || *s2) {
+ int first;
- if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
- continue;
+ while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) {
+ int order;
- if (found) {
- _cleanup_free_ char *u = NULL, *v = NULL, *a = NULL, *b = NULL;
-
- /* We found a device backed by multiple other devices. We don't really support automatic
- * discovery on such setups, with the exception of dm-verity partitions. In this case there are
- * two backing devices: the data partition and the hash partition. We are fine with such
- * setups, however, only if both partitions are on the same physical device. Hence, let's
- * verify this. */
-
- u = strjoin(p, "/", de->d_name, "/../dev");
- if (!u)
- return -ENOMEM;
-
- v = strjoin(p, "/", found->d_name, "/../dev");
- if (!v)
- return -ENOMEM;
-
- r = read_one_line_file(u, &a);
- if (r < 0) {
- log_debug_errno(r, "Failed to read %s: %m", u);
- goto fallback;
- }
-
- r = read_one_line_file(v, &b);
- if (r < 0) {
- log_debug_errno(r, "Failed to read %s: %m", v);
- goto fallback;
- }
-
- /* Check if the parent device is the same. If not, then the two backing devices are on
- * different physical devices, and we don't support that. */
- if (!streq(a, b))
- goto fallback;
+ order = c_order(*s1) - c_order(*s2);
+ if (order != 0)
+ return order;
+ s1++;
+ s2++;
}
- found = de;
- }
-
- if (!found)
- goto fallback;
+ while (*s1 == '0')
+ s1++;
+ while (*s2 == '0')
+ s2++;
+
+ first = 0;
+ while (is_digit(*s1) && is_digit(*s2)) {
+ if (first == 0)
+ first = *s1 - *s2;
+ s1++;
+ s2++;
+ }
- q = strjoina(p, "/", found->d_name, "/dev");
+ if (is_digit(*s1))
+ return 1;
+ if (is_digit(*s2))
+ return -1;
- r = read_one_line_file(q, &t);
- if (r == -ENOENT)
- goto fallback;
- if (r < 0)
- return r;
+ if (first != 0)
+ return first;
+ }
- if (sscanf(t, "%u:%u", &maj, &min) != 2)
- return -EINVAL;
+ return strcmp(os1, os2);
+}
- if (maj == 0)
- goto fallback;
+/* Turn off core dumps but only if we're running outside of a container. */
+void disable_coredumps(void) {
+ int r;
- *dev = makedev(maj, min);
- return 1;
+ if (detect_container() > 0)
+ return;
-fallback:
- *dev = dt;
- return 1;
+ r = write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0);
+ if (r < 0)
+ log_debug_errno(r, "Failed to turn off coredumps, ignoring: %m");
}
diff --git a/src/basic/util.h b/src/basic/util.h
index a79907de3e..9d1b10756b 100644
--- a/src/basic/util.h
+++ b/src/basic/util.h
@@ -71,8 +71,6 @@ bool plymouth_running(void);
bool display_is_local(const char *display) _pure_;
int socket_from_display(const char *display, char **path);
-int block_get_whole_disk(dev_t d, dev_t *ret);
-
#define NULSTR_FOREACH(i, l) \
for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)
@@ -86,8 +84,6 @@ bool kexec_loaded(void);
int prot_from_flags(int flags) _const_;
-int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...);
-
bool in_initrd(void);
void in_initrd_force(bool value);
@@ -194,5 +190,6 @@ int update_reboot_parameter_and_warn(const char *param);
int version(void);
-int get_block_device(const char *path, dev_t *dev);
-int get_block_device_harder(const char *path, dev_t *dev);
+int str_verscmp(const char *s1, const char *s2);
+
+void disable_coredumps(void);
diff --git a/src/basic/verbs.c b/src/basic/verbs.c
index cb42e6dd08..47644670da 100644
--- a/src/basic/verbs.c
+++ b/src/basic/verbs.c
@@ -22,13 +22,48 @@
#include <getopt.h>
#include <stdbool.h>
#include <stddef.h>
+#include <string.h>
+#include "env-util.h"
#include "log.h"
#include "macro.h"
+#include "process-util.h"
#include "string-util.h"
#include "verbs.h"
#include "virt.h"
+/* Wraps running_in_chroot() which is used in various places, but also adds an environment variable check so external
+ * processes can reliably force this on.
+ */
+bool running_in_chroot_or_offline(void) {
+ int r;
+
+ /* Added to support use cases like rpm-ostree, where from %post scripts we only want to execute "preset", but
+ * not "start"/"restart" for example.
+ *
+ * See ENVIRONMENT.md for docs.
+ */
+ r = getenv_bool("SYSTEMD_OFFLINE");
+ if (r < 0 && r != -ENXIO)
+ log_debug_errno(r, "Failed to parse $SYSTEMD_OFFLINE: %m");
+ else if (r >= 0)
+ return r > 0;
+
+ /* We've had this condition check for a long time which basically checks for legacy chroot case like Fedora's
+ * "mock", which is used for package builds. We don't want to try to start systemd services there, since
+ * without --new-chroot we don't even have systemd running, and even if we did, adding a concept of background
+ * daemons to builds would be an enormous change, requiring considering things like how the journal output is
+ * handled, etc. And there's really not a use case today for a build talking to a service.
+ *
+ * Note this call itself also looks for a different variable SYSTEMD_IGNORE_CHROOT=1.
+ */
+ r = running_in_chroot();
+ if (r < 0)
+ log_debug_errno(r, "running_in_chroot(): %m");
+
+ return r > 0;
+}
+
int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
const Verb *verb;
const char *name;
@@ -84,12 +119,15 @@ int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
return -EINVAL;
}
- if ((verb->flags & VERB_NOCHROOT) && running_in_chroot() > 0) {
- log_info("Running in chroot, ignoring request.");
+ if ((verb->flags & VERB_ONLINE_ONLY) && running_in_chroot_or_offline()) {
+ if (name)
+ log_info("Running in chroot, ignoring request: %s", name);
+ else
+ log_info("Running in chroot, ignoring request.");
return 0;
}
- if (verb->flags & VERB_MUSTBEROOT) {
+ if (verb->flags & VERB_MUST_BE_ROOT) {
r = must_be_root();
if (r < 0)
return r;
diff --git a/src/basic/verbs.h b/src/basic/verbs.h
index 5f44a18f8e..d9259fc45f 100644
--- a/src/basic/verbs.h
+++ b/src/basic/verbs.h
@@ -23,9 +23,9 @@
#define VERB_ANY ((unsigned) -1)
typedef enum VerbFlags {
- VERB_DEFAULT = 1 << 0,
- VERB_NOCHROOT = 1 << 1,
- VERB_MUSTBEROOT = 1 << 2,
+ VERB_DEFAULT = 1 << 0,
+ VERB_ONLINE_ONLY = 1 << 1,
+ VERB_MUST_BE_ROOT = 1 << 2,
} VerbFlags;
typedef struct {
@@ -35,4 +35,6 @@ typedef struct {
int (* const dispatch)(int argc, char *argv[], void *userdata);
} Verb;
+bool running_in_chroot_or_offline(void);
+
int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata);
diff --git a/src/basic/virt.c b/src/basic/virt.c
index b0db28add6..f4796b53bc 100644
--- a/src/basic/virt.c
+++ b/src/basic/virt.c
@@ -18,6 +18,9 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#if defined(__i386__) || defined(__x86_64__)
+#include <cpuid.h>
+#endif
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
@@ -56,30 +59,14 @@ static int detect_vm_cpuid(void) {
{ "bhyve bhyve ", VIRTUALIZATION_BHYVE },
};
- uint32_t eax, ecx;
+ uint32_t eax, ebx, ecx, edx;
bool hypervisor;
/* http://lwn.net/Articles/301888/ */
-#if defined (__i386__)
-#define REG_a "eax"
-#define REG_b "ebx"
-#elif defined (__amd64__)
-#define REG_a "rax"
-#define REG_b "rbx"
-#endif
-
/* First detect whether there is a hypervisor */
- eax = 1;
- __asm__ __volatile__ (
- /* ebx/rbx is being used for PIC! */
- " push %%"REG_b" \n\t"
- " cpuid \n\t"
- " pop %%"REG_b" \n\t"
-
- : "=a" (eax), "=c" (ecx)
- : "0" (eax)
- );
+ if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0)
+ return VIRTUALIZATION_NONE;
hypervisor = !!(ecx & 0x80000000U);
@@ -91,17 +78,11 @@ static int detect_vm_cpuid(void) {
unsigned j;
/* There is a hypervisor, see what it is */
- eax = 0x40000000U;
- __asm__ __volatile__ (
- /* ebx/rbx is being used for PIC! */
- " push %%"REG_b" \n\t"
- " cpuid \n\t"
- " mov %%ebx, %1 \n\t"
- " pop %%"REG_b" \n\t"
-
- : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
- : "0" (eax)
- );
+ __cpuid(0x40000000U, eax, ebx, ecx, edx);
+
+ sig.sig32[0] = ebx;
+ sig.sig32[1] = ecx;
+ sig.sig32[2] = edx;
log_debug("Virtualization found, CPUID=%s", sig.text);
@@ -241,8 +222,10 @@ static int detect_vm_xen_dom0(void) {
if (r == 0) {
unsigned long features;
- r = safe_atolu(domcap, &features);
- if (r == 0) {
+ /* Here, we need to use sscanf() instead of safe_atoul()
+ * as the string lacks the leading "0x". */
+ r = sscanf(domcap, "%lx", &features);
+ if (r == 1) {
r = !!(features & (1U << XENFEAT_dom0));
log_debug("Virtualization XEN, found %s with value %08lx, "
"XENFEAT_dom0 (indicating the 'hardware domain') is%s set.",