summaryrefslogtreecommitdiff
path: root/src/basic
diff options
context:
space:
mode:
Diffstat (limited to 'src/basic')
-rw-r--r--src/basic/alloc-util.h8
-rw-r--r--src/basic/architecture.c10
-rw-r--r--src/basic/architecture.h12
-rw-r--r--src/basic/audit-util.c7
-rw-r--r--src/basic/bitmap.c6
-rw-r--r--src/basic/calendarspec.c43
-rw-r--r--src/basic/calendarspec.h1
-rw-r--r--src/basic/capability-util.c6
-rw-r--r--src/basic/cgroup-util.c248
-rw-r--r--src/basic/cgroup-util.h26
-rw-r--r--src/basic/def.h2
-rw-r--r--src/basic/env-util.c3
-rw-r--r--src/basic/escape.c2
-rw-r--r--src/basic/exit-status.c33
-rw-r--r--src/basic/exit-status.h14
-rw-r--r--src/basic/fileio.c55
-rw-r--r--src/basic/fileio.h2
-rw-r--r--src/basic/fs-util.c281
-rw-r--r--src/basic/fs-util.h5
-rw-r--r--src/basic/gunicode.c2
-rw-r--r--src/basic/hostname-util.c1
-rw-r--r--src/basic/list.h2
-rw-r--r--src/basic/log.c61
-rw-r--r--src/basic/log.h10
-rw-r--r--src/basic/missing.h61
-rw-r--r--src/basic/mount-util.c180
-rw-r--r--src/basic/mount-util.h11
-rw-r--r--src/basic/parse-util.c27
-rw-r--r--src/basic/parse-util.h3
-rw-r--r--src/basic/path-util.c93
-rw-r--r--src/basic/path-util.h3
-rw-r--r--src/basic/prioq.c4
-rw-r--r--src/basic/proc-cmdline.c11
-rw-r--r--src/basic/proc-cmdline.h4
-rw-r--r--src/basic/process-util.h5
-rw-r--r--src/basic/replace-var.c3
-rw-r--r--src/basic/rm-rf.c14
-rw-r--r--src/basic/set.h8
-rw-r--r--src/basic/socket-util.c33
-rw-r--r--src/basic/socket-util.h4
-rw-r--r--src/basic/special.h3
-rw-r--r--src/basic/strbuf.c3
-rw-r--r--src/basic/string-util.c63
-rw-r--r--src/basic/string-util.h7
-rw-r--r--src/basic/strv.c9
-rw-r--r--src/basic/strv.h16
-rw-r--r--src/basic/terminal-util.c35
-rw-r--r--src/basic/terminal-util.h53
-rw-r--r--src/basic/time-util.c150
-rw-r--r--src/basic/time-util.h5
-rw-r--r--src/basic/user-util.c161
-rw-r--r--src/basic/user-util.h24
-rw-r--r--src/basic/util.c2
-rw-r--r--src/basic/util.h4
-rw-r--r--src/basic/virt.c79
-rw-r--r--src/basic/virt.h2
56 files changed, 1586 insertions, 334 deletions
diff --git a/src/basic/alloc-util.h b/src/basic/alloc-util.h
index ceeee519b7..a44dd473c1 100644
--- a/src/basic/alloc-util.h
+++ b/src/basic/alloc-util.h
@@ -43,6 +43,14 @@ static inline void *mfree(void *memory) {
return NULL;
}
+#define free_and_replace(a, b) \
+ ({ \
+ free(a); \
+ (a) = (b); \
+ (b) = NULL; \
+ 0; \
+ })
+
void* memdup(const void *p, size_t l) _alloc_(2);
static inline void freep(void *p) {
diff --git a/src/basic/architecture.c b/src/basic/architecture.c
index b1c8e91f50..b74dc0db78 100644
--- a/src/basic/architecture.c
+++ b/src/basic/architecture.c
@@ -123,6 +123,14 @@ int uname_architecture(void) {
{ "crisv32", ARCHITECTURE_CRIS },
#elif defined(__nios2__)
{ "nios2", ARCHITECTURE_NIOS2 },
+#elif defined(__riscv__)
+ { "riscv32", ARCHITECTURE_RISCV32 },
+ { "riscv64", ARCHITECTURE_RISCV64 },
+# if __SIZEOF_POINTER__ == 4
+ { "riscv", ARCHITECTURE_RISCV32 },
+# elif __SIZEOF_POINTER__ == 8
+ { "riscv", ARCHITECTURE_RISCV64 },
+# endif
#else
#error "Please register your architecture here!"
#endif
@@ -174,6 +182,8 @@ static const char *const architecture_table[_ARCHITECTURE_MAX] = {
[ARCHITECTURE_TILEGX] = "tilegx",
[ARCHITECTURE_CRIS] = "cris",
[ARCHITECTURE_NIOS2] = "nios2",
+ [ARCHITECTURE_RISCV32] = "riscv32",
+ [ARCHITECTURE_RISCV64] = "riscv64",
};
DEFINE_STRING_TABLE_LOOKUP(architecture, int);
diff --git a/src/basic/architecture.h b/src/basic/architecture.h
index b3e4d85906..5a77c31932 100644
--- a/src/basic/architecture.h
+++ b/src/basic/architecture.h
@@ -58,6 +58,8 @@ enum {
ARCHITECTURE_TILEGX,
ARCHITECTURE_CRIS,
ARCHITECTURE_NIOS2,
+ ARCHITECTURE_RISCV32,
+ ARCHITECTURE_RISCV64,
_ARCHITECTURE_MAX,
_ARCHITECTURE_INVALID = -1
};
@@ -191,6 +193,16 @@ int uname_architecture(void);
#elif defined(__nios2__)
# define native_architecture() ARCHITECTURE_NIOS2
# define LIB_ARCH_TUPLE "nios2-linux-gnu"
+#elif defined(__riscv__)
+# if __SIZEOF_POINTER__ == 4
+# define native_architecture() ARCHITECTURE_RISCV32
+# define LIB_ARCH_TUPLE "riscv32-linux-gnu"
+# elif __SIZEOF_POINTER__ == 8
+# define native_architecture() ARCHITECTURE_RISCV64
+# define LIB_ARCH_TUPLE "riscv64-linux-gnu"
+# else
+# error "Unrecognized riscv architecture variant"
+# endif
#else
# error "Please register your architecture here!"
#endif
diff --git a/src/basic/audit-util.c b/src/basic/audit-util.c
index 5741fecdd6..d1c9695973 100644
--- a/src/basic/audit-util.c
+++ b/src/basic/audit-util.c
@@ -92,8 +92,11 @@ bool use_audit(void) {
int fd;
fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_AUDIT);
- if (fd < 0)
- cached_use = errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT;
+ if (fd < 0) {
+ cached_use = !IN_SET(errno, EAFNOSUPPORT, EPROTONOSUPPORT, EPERM);
+ if (errno == EPERM)
+ log_debug_errno(errno, "Audit access prohibited, won't talk to audit");
+ }
else {
cached_use = true;
safe_close(fd);
diff --git a/src/basic/bitmap.c b/src/basic/bitmap.c
index f4b12fc261..f6212e6151 100644
--- a/src/basic/bitmap.c
+++ b/src/basic/bitmap.c
@@ -58,10 +58,8 @@ Bitmap *bitmap_copy(Bitmap *b) {
return NULL;
ret->bitmaps = newdup(uint64_t, b->bitmaps, b->n_bitmaps);
- if (!ret->bitmaps) {
- free(ret);
- return NULL;
- }
+ if (!ret->bitmaps)
+ return mfree(ret);
ret->n_bitmaps = ret->bitmaps_allocated = b->n_bitmaps;
return ret;
diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c
index e4cfab364e..fda293fcb9 100644
--- a/src/basic/calendarspec.c
+++ b/src/basic/calendarspec.c
@@ -302,6 +302,17 @@ int calendar_spec_to_string(const CalendarSpec *c, char **p) {
if (c->utc)
fputs(" UTC", f);
+ else if (IN_SET(c->dst, 0, 1)) {
+
+ /* If daylight saving is explicitly on or off, let's show the used timezone. */
+
+ tzset();
+
+ if (!isempty(tzname[c->dst])) {
+ fputc(' ', f);
+ fputs(tzname[c->dst], f);
+ }
+ }
r = fflush_and_check(f);
if (r < 0) {
@@ -747,9 +758,9 @@ fail:
}
int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
+ const char *utc;
CalendarSpec *c;
int r;
- const char *utc;
assert(p);
assert(spec);
@@ -760,11 +771,39 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
c = new0(CalendarSpec, 1);
if (!c)
return -ENOMEM;
+ c->dst = -1;
utc = endswith_no_case(p, " UTC");
if (utc) {
c->utc = true;
p = strndupa(p, utc - p);
+ } else {
+ const char *e = NULL;
+ int j;
+
+ tzset();
+
+ /* Check if the local timezone was specified? */
+ for (j = 0; j <= 1; j++) {
+ if (isempty(tzname[j]))
+ continue;
+
+ e = endswith_no_case(p, tzname[j]);
+ if(!e)
+ continue;
+ if (e == p)
+ continue;
+ if (e[-1] != ' ')
+ continue;
+
+ break;
+ }
+
+ /* Found one of the two timezones specified? */
+ if (IN_SET(j, 0, 1)) {
+ p = strndupa(p, e - p - 1);
+ c->dst = j;
+ }
}
if (strcaseeq(p, "minutely")) {
@@ -1017,7 +1056,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) {
for (;;) {
/* Normalize the current date */
(void) mktime_or_timegm(&c, spec->utc);
- c.tm_isdst = -1;
+ c.tm_isdst = spec->dst;
c.tm_year += 1900;
r = find_matching_component(spec->year, &c.tm_year);
diff --git a/src/basic/calendarspec.h b/src/basic/calendarspec.h
index f6472c1244..c6087228fd 100644
--- a/src/basic/calendarspec.h
+++ b/src/basic/calendarspec.h
@@ -37,6 +37,7 @@ typedef struct CalendarComponent {
typedef struct CalendarSpec {
int weekdays_bits;
bool utc;
+ int dst;
CalendarComponent *year;
CalendarComponent *month;
diff --git a/src/basic/capability-util.c b/src/basic/capability-util.c
index d4c5bd6937..c3de20a0e8 100644
--- a/src/basic/capability-util.c
+++ b/src/basic/capability-util.c
@@ -31,6 +31,7 @@
#include "log.h"
#include "macro.h"
#include "parse-util.h"
+#include "user-util.h"
#include "util.h"
int have_effective_cap(int value) {
@@ -295,8 +296,9 @@ int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) {
if (setresgid(gid, gid, gid) < 0)
return log_error_errno(errno, "Failed to change group ID: %m");
- if (setgroups(0, NULL) < 0)
- return log_error_errno(errno, "Failed to drop auxiliary groups list: %m");
+ r = maybe_setgroups(0, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to drop auxiliary groups list: %m");
/* Ensure we keep the permitted caps across the setresuid() */
if (prctl(PR_SET_KEEPCAPS, 1) < 0)
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c
index 472e24b7a3..134e6e3664 100644
--- a/src/basic/cgroup-util.c
+++ b/src/basic/cgroup-util.c
@@ -28,6 +28,7 @@
#include <sys/stat.h>
#include <sys/statfs.h>
#include <sys/types.h>
+#include <sys/xattr.h>
#include <unistd.h>
#include "alloc-util.h"
@@ -134,6 +135,20 @@ int cg_read_event(const char *controller, const char *path, const char *event,
return -ENOENT;
}
+bool cg_ns_supported(void) {
+ static thread_local int enabled = -1;
+
+ if (enabled >= 0)
+ return enabled;
+
+ if (access("/proc/self/ns/cgroup", F_OK) == 0)
+ enabled = 1;
+ else
+ enabled = 0;
+
+ return enabled;
+}
+
int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) {
_cleanup_free_ char *fs = NULL;
int r;
@@ -609,7 +624,7 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch
if (!cg_controller_is_valid(controller))
return -EINVAL;
- unified = cg_unified();
+ unified = cg_all_unified();
if (unified < 0)
return unified;
@@ -637,7 +652,7 @@ static int controller_is_accessible(const char *controller) {
if (!cg_controller_is_valid(controller))
return -EINVAL;
- unified = cg_unified();
+ unified = cg_all_unified();
if (unified < 0)
return unified;
if (unified > 0) {
@@ -855,7 +870,7 @@ int cg_set_task_access(
if (r < 0)
return r;
- unified = cg_unified();
+ unified = cg_unified(controller);
if (unified < 0)
return unified;
if (unified)
@@ -869,6 +884,43 @@ int cg_set_task_access(
return 0;
}
+int cg_set_xattr(const char *controller, const char *path, const char *name, const void *value, size_t size, int flags) {
+ _cleanup_free_ char *fs = NULL;
+ int r;
+
+ assert(path);
+ assert(name);
+ assert(value || size <= 0);
+
+ r = cg_get_path(controller, path, NULL, &fs);
+ if (r < 0)
+ return r;
+
+ if (setxattr(fs, name, value, size, flags) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int cg_get_xattr(const char *controller, const char *path, const char *name, void *value, size_t size) {
+ _cleanup_free_ char *fs = NULL;
+ ssize_t n;
+ int r;
+
+ assert(path);
+ assert(name);
+
+ r = cg_get_path(controller, path, NULL, &fs);
+ if (r < 0)
+ return r;
+
+ n = getxattr(fs, name, value, size);
+ if (n < 0)
+ return -errno;
+
+ return (int) n;
+}
+
int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
_cleanup_fclose_ FILE *f = NULL;
char line[LINE_MAX];
@@ -879,18 +931,17 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
assert(path);
assert(pid >= 0);
- unified = cg_unified();
+ if (controller) {
+ if (!cg_controller_is_valid(controller))
+ return -EINVAL;
+ } else
+ controller = SYSTEMD_CGROUP_CONTROLLER;
+
+ unified = cg_unified(controller);
if (unified < 0)
return unified;
- if (unified == 0) {
- if (controller) {
- if (!cg_controller_is_valid(controller))
- return -EINVAL;
- } else
- controller = SYSTEMD_CGROUP_CONTROLLER;
-
+ if (unified == 0)
cs = strlen(controller);
- }
fs = procfs_file_alloca(pid, "cgroup");
f = fopen(fs, "re");
@@ -955,7 +1006,7 @@ int cg_install_release_agent(const char *controller, const char *agent) {
assert(agent);
- unified = cg_unified();
+ unified = cg_unified(controller);
if (unified < 0)
return unified;
if (unified) /* doesn't apply to unified hierarchy */
@@ -1006,7 +1057,7 @@ int cg_uninstall_release_agent(const char *controller) {
_cleanup_free_ char *fs = NULL;
int r, unified;
- unified = cg_unified();
+ unified = cg_unified(controller);
if (unified < 0)
return unified;
if (unified) /* Doesn't apply to unified hierarchy */
@@ -1062,7 +1113,7 @@ int cg_is_empty_recursive(const char *controller, const char *path) {
if (controller && (isempty(path) || path_equal(path, "/")))
return false;
- unified = cg_unified();
+ unified = cg_unified(controller);
if (unified < 0)
return unified;
@@ -1653,7 +1704,7 @@ int cg_path_get_slice(const char *p, char **slice) {
if (!e) {
char *s;
- s = strdup("-.slice");
+ s = strdup(SPECIAL_ROOT_SLICE);
if (!s)
return -ENOMEM;
@@ -1808,7 +1859,7 @@ int cg_slice_to_path(const char *unit, char **ret) {
assert(unit);
assert(ret);
- if (streq(unit, "-.slice")) {
+ if (streq(unit, SPECIAL_ROOT_SLICE)) {
char *x;
x = strdup("");
@@ -1891,6 +1942,49 @@ int cg_get_attribute(const char *controller, const char *path, const char *attri
return read_one_line_file(p, ret);
}
+int cg_get_keyed_attribute(const char *controller, const char *path, const char *attribute, const char **keys, char **values) {
+ _cleanup_free_ char *filename = NULL, *content = NULL;
+ char *line, *p;
+ int i, r;
+
+ for (i = 0; keys[i]; i++)
+ values[i] = NULL;
+
+ r = cg_get_path(controller, path, attribute, &filename);
+ if (r < 0)
+ return r;
+
+ r = read_full_file(filename, &content, NULL);
+ if (r < 0)
+ return r;
+
+ p = content;
+ while ((line = strsep(&p, "\n"))) {
+ char *key;
+
+ key = strsep(&line, " ");
+
+ for (i = 0; keys[i]; i++) {
+ if (streq(key, keys[i])) {
+ values[i] = strdup(line);
+ break;
+ }
+ }
+ }
+
+ for (i = 0; keys[i]; i++) {
+ if (!values[i]) {
+ for (i = 0; keys[i]; i++) {
+ free(values[i]);
+ values[i] = NULL;
+ }
+ return -ENOENT;
+ }
+ }
+
+ return 0;
+}
+
int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path) {
CGroupController c;
int r, unified;
@@ -1905,7 +1999,7 @@ int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path
return r;
/* If we are in the unified hierarchy, we are done now */
- unified = cg_unified();
+ unified = cg_all_unified();
if (unified < 0)
return unified;
if (unified > 0)
@@ -1935,7 +2029,7 @@ int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_m
if (r < 0)
return r;
- unified = cg_unified();
+ unified = cg_all_unified();
if (unified < 0)
return unified;
if (unified > 0)
@@ -1987,7 +2081,7 @@ int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to
return r;
}
- unified = cg_unified();
+ unified = cg_all_unified();
if (unified < 0)
return unified;
if (unified > 0)
@@ -2020,7 +2114,7 @@ int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root)
if (r < 0)
return r;
- unified = cg_unified();
+ unified = cg_all_unified();
if (unified < 0)
return unified;
if (unified > 0)
@@ -2046,7 +2140,7 @@ int cg_mask_supported(CGroupMask *ret) {
* includes controllers we can make sense of and that are
* actually accessible. */
- unified = cg_unified();
+ unified = cg_all_unified();
if (unified < 0)
return unified;
if (unified > 0) {
@@ -2087,10 +2181,10 @@ int cg_mask_supported(CGroupMask *ret) {
mask |= CGROUP_CONTROLLER_TO_MASK(v);
}
- /* Currently, we only support the memory, io and pids
+ /* Currently, we support the cpu, memory, io and pids
* controller in the unified hierarchy, mask
* everything else off. */
- mask &= CGROUP_MASK_MEMORY | CGROUP_MASK_IO | CGROUP_MASK_PIDS;
+ mask &= CGROUP_MASK_CPU | CGROUP_MASK_MEMORY | CGROUP_MASK_IO | CGROUP_MASK_PIDS;
} else {
CGroupController c;
@@ -2167,9 +2261,10 @@ int cg_kernel_controllers(Set *controllers) {
return 0;
}
-static thread_local int unified_cache = -1;
+static thread_local CGroupUnified unified_cache = CGROUP_UNIFIED_UNKNOWN;
+
+static int cg_update_unified(void) {
-int cg_unified(void) {
struct statfs fs;
/* Checks if we support the unified hierarchy. Returns an
@@ -2177,24 +2272,47 @@ int cg_unified(void) {
* have any other trouble determining if the unified hierarchy
* is supported. */
- if (unified_cache >= 0)
- return unified_cache;
+ if (unified_cache >= CGROUP_UNIFIED_NONE)
+ return 0;
if (statfs("/sys/fs/cgroup/", &fs) < 0)
return -errno;
if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC))
- unified_cache = true;
- else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC))
- unified_cache = false;
- else
+ unified_cache = CGROUP_UNIFIED_ALL;
+ else if (F_TYPE_EQUAL(fs.f_type, TMPFS_MAGIC)) {
+ if (statfs("/sys/fs/cgroup/systemd/", &fs) < 0)
+ return -errno;
+
+ unified_cache = F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC) ?
+ CGROUP_UNIFIED_SYSTEMD : CGROUP_UNIFIED_NONE;
+ } else
return -ENOMEDIUM;
- return unified_cache;
+ return 0;
+}
+
+int cg_unified(const char *controller) {
+
+ int r;
+
+ r = cg_update_unified();
+ if (r < 0)
+ return r;
+
+ if (streq_ptr(controller, SYSTEMD_CGROUP_CONTROLLER))
+ return unified_cache >= CGROUP_UNIFIED_SYSTEMD;
+ else
+ return unified_cache >= CGROUP_UNIFIED_ALL;
+}
+
+int cg_all_unified(void) {
+
+ return cg_unified(NULL);
}
void cg_unified_flush(void) {
- unified_cache = -1;
+ unified_cache = CGROUP_UNIFIED_UNKNOWN;
}
int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) {
@@ -2207,7 +2325,7 @@ int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) {
if (supported == 0)
return 0;
- unified = cg_unified();
+ unified = cg_all_unified();
if (unified < 0)
return unified;
if (!unified) /* on the legacy hiearchy there's no joining of controllers defined */
@@ -2246,7 +2364,7 @@ bool cg_is_unified_wanted(void) {
/* If the hierarchy is already mounted, then follow whatever
* was chosen for it. */
- unified = cg_unified();
+ unified = cg_all_unified();
if (unified >= 0)
return unified;
@@ -2276,6 +2394,50 @@ bool cg_is_legacy_wanted(void) {
return !cg_is_unified_wanted();
}
+bool cg_is_unified_systemd_controller_wanted(void) {
+ static thread_local int wanted = -1;
+ int r, unified;
+
+ /* If the unified hierarchy is requested in full, no need to
+ * bother with this. */
+ if (cg_is_unified_wanted())
+ return 0;
+
+ /* If the hierarchy is already mounted, then follow whatever
+ * was chosen for it. */
+ unified = cg_unified(SYSTEMD_CGROUP_CONTROLLER);
+ if (unified >= 0)
+ return unified;
+
+ /* Otherwise, let's see what the kernel command line has to
+ * say. Since checking that is expensive, let's cache the
+ * result. */
+ if (wanted >= 0)
+ return wanted;
+
+ r = get_proc_cmdline_key("systemd.legacy_systemd_cgroup_controller", NULL);
+ if (r > 0)
+ wanted = false;
+ else {
+ _cleanup_free_ char *value = NULL;
+
+ r = get_proc_cmdline_key("systemd.legacy_systemd_cgroup_controller=", &value);
+ if (r < 0)
+ return false;
+
+ if (r == 0)
+ wanted = false;
+ else
+ wanted = parse_boolean(value) <= 0;
+ }
+
+ return wanted;
+}
+
+bool cg_is_legacy_systemd_controller_wanted(void) {
+ return cg_is_legacy_wanted() && !cg_is_unified_systemd_controller_wanted();
+}
+
int cg_weight_parse(const char *s, uint64_t *ret) {
uint64_t u;
int r;
@@ -2352,6 +2514,20 @@ int cg_blkio_weight_parse(const char *s, uint64_t *ret) {
return 0;
}
+bool is_cgroup_fs(const struct statfs *s) {
+ return is_fs_type(s, CGROUP_SUPER_MAGIC) ||
+ is_fs_type(s, CGROUP2_SUPER_MAGIC);
+}
+
+bool fd_is_cgroup_fs(int fd) {
+ struct statfs s;
+
+ if (fstatfs(fd, &s) < 0)
+ return -errno;
+
+ return is_cgroup_fs(&s);
+}
+
static const char *cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = {
[CGROUP_CONTROLLER_CPU] = "cpu",
[CGROUP_CONTROLLER_CPUACCT] = "cpuacct",
diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h
index 14ebde5fc9..0aa27c4cd7 100644
--- a/src/basic/cgroup-util.h
+++ b/src/basic/cgroup-util.h
@@ -23,6 +23,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
+#include <sys/statfs.h>
#include <sys/types.h>
#include "def.h"
@@ -112,6 +113,17 @@ static inline bool CGROUP_BLKIO_WEIGHT_IS_OK(uint64_t x) {
(x >= CGROUP_BLKIO_WEIGHT_MIN && x <= CGROUP_BLKIO_WEIGHT_MAX);
}
+/* Default resource limits */
+#define DEFAULT_TASKS_MAX_PERCENTAGE 15U /* 15% of PIDs, 4915 on default settings */
+#define DEFAULT_USER_TASKS_MAX_PERCENTAGE 33U /* 33% of PIDs, 10813 on default settings */
+
+typedef enum CGroupUnified {
+ CGROUP_UNIFIED_UNKNOWN = -1,
+ CGROUP_UNIFIED_NONE = 0, /* Both systemd and controllers on legacy */
+ CGROUP_UNIFIED_SYSTEMD = 1, /* Only systemd on unified */
+ CGROUP_UNIFIED_ALL = 2, /* Both systemd and controllers on unified */
+} CGroupUnified;
+
/*
* General rules:
*
@@ -169,10 +181,14 @@ int cg_create_and_attach(const char *controller, const char *path, pid_t pid);
int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value);
int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret);
+int cg_get_keyed_attribute(const char *controller, const char *path, const char *attribute, const char **keys, char **values);
int cg_set_group_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid);
int cg_set_task_access(const char *controller, const char *path, mode_t mode, uid_t uid, gid_t gid);
+int cg_set_xattr(const char *controller, const char *path, const char *name, const void *value, size_t size, int flags);
+int cg_get_xattr(const char *controller, const char *path, const char *name, void *value, size_t size);
+
int cg_install_release_agent(const char *controller, const char *agent);
int cg_uninstall_release_agent(const char *controller);
@@ -222,11 +238,16 @@ int cg_mask_supported(CGroupMask *ret);
int cg_kernel_controllers(Set *controllers);
-int cg_unified(void);
+bool cg_ns_supported(void);
+
+int cg_all_unified(void);
+int cg_unified(const char *controller);
void cg_unified_flush(void);
bool cg_is_unified_wanted(void);
bool cg_is_legacy_wanted(void);
+bool cg_is_unified_systemd_controller_wanted(void);
+bool cg_is_legacy_systemd_controller_wanted(void);
const char* cgroup_controller_to_string(CGroupController c) _const_;
CGroupController cgroup_controller_from_string(const char *s) _pure_;
@@ -234,3 +255,6 @@ CGroupController cgroup_controller_from_string(const char *s) _pure_;
int cg_weight_parse(const char *s, uint64_t *ret);
int cg_cpu_shares_parse(const char *s, uint64_t *ret);
int cg_blkio_weight_parse(const char *s, uint64_t *ret);
+
+bool is_cgroup_fs(const struct statfs *s);
+bool fd_is_cgroup_fs(int fd);
diff --git a/src/basic/def.h b/src/basic/def.h
index 1a7a0f4928..2266eff650 100644
--- a/src/basic/def.h
+++ b/src/basic/def.h
@@ -79,7 +79,7 @@
#endif
/* Return a nulstr for a standard cascade of configuration paths,
- * suitable to pass to conf_files_list_nulstr() or config_parse_many()
+ * suitable to pass to conf_files_list_nulstr() or config_parse_many_nulstr()
* to implement drop-in directories for extending configuration
* files. */
#define CONF_PATHS_NULSTR(n) \
diff --git a/src/basic/env-util.c b/src/basic/env-util.c
index 7f5fddb700..b74290d6fd 100644
--- a/src/basic/env-util.c
+++ b/src/basic/env-util.c
@@ -544,8 +544,7 @@ char *replace_env(const char *format, char **env) {
return k;
fail:
- free(r);
- return NULL;
+ return mfree(r);
}
char **replace_env_argv(char **argv, char **env) {
diff --git a/src/basic/escape.c b/src/basic/escape.c
index 01daf11ce7..4a1ec4505e 100644
--- a/src/basic/escape.c
+++ b/src/basic/escape.c
@@ -333,7 +333,7 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi
assert(remaining > 0);
if (*f != '\\') {
- /* A literal literal, copy verbatim */
+ /* A literal, copy verbatim */
*(t++) = *f;
continue;
}
diff --git a/src/basic/exit-status.c b/src/basic/exit-status.c
index d488cfc59f..59557f8afe 100644
--- a/src/basic/exit-status.c
+++ b/src/basic/exit-status.c
@@ -24,12 +24,12 @@
#include "macro.h"
#include "set.h"
-const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
+const char* exit_status_to_string(int status, ExitStatusLevel level) {
/* We cast to int here, so that -Wenum doesn't complain that
* EXIT_SUCCESS/EXIT_FAILURE aren't in the enum */
- switch ((int) status) {
+ switch (status) {
case EXIT_SUCCESS:
return "SUCCESS";
@@ -39,7 +39,7 @@ const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
}
if (IN_SET(level, EXIT_STATUS_SYSTEMD, EXIT_STATUS_LSB)) {
- switch ((int) status) {
+ switch (status) {
case EXIT_CHDIR:
return "CHDIR";
@@ -140,19 +140,19 @@ const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
case EXIT_RUNTIME_DIRECTORY:
return "RUNTIME_DIRECTORY";
- case EXIT_CHOWN:
- return "CHOWN";
-
case EXIT_MAKE_STARTER:
return "MAKE_STARTER";
+ case EXIT_CHOWN:
+ return "CHOWN";
+
case EXIT_SMACK_PROCESS_LABEL:
return "SMACK_PROCESS_LABEL";
}
}
if (level == EXIT_STATUS_LSB) {
- switch ((int) status) {
+ switch (status) {
case EXIT_INVALIDARGUMENT:
return "INVALIDARGUMENT";
@@ -177,34 +177,23 @@ const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) {
return NULL;
}
-
-bool is_clean_exit(int code, int status, ExitStatusSet *success_status) {
+bool is_clean_exit(int code, int status, ExitClean clean, ExitStatusSet *success_status) {
if (code == CLD_EXITED)
return status == 0 ||
(success_status &&
set_contains(success_status->status, INT_TO_PTR(status)));
- /* If a daemon does not implement handlers for some of the
- * signals that's not considered an unclean shutdown */
+ /* If a daemon does not implement handlers for some of the signals that's not considered an unclean shutdown */
if (code == CLD_KILLED)
- return IN_SET(status, SIGHUP, SIGINT, SIGTERM, SIGPIPE) ||
+ return
+ (clean == EXIT_CLEAN_DAEMON && IN_SET(status, SIGHUP, SIGINT, SIGTERM, SIGPIPE)) ||
(success_status &&
set_contains(success_status->signal, INT_TO_PTR(status)));
return false;
}
-bool is_clean_exit_lsb(int code, int status, ExitStatusSet *success_status) {
-
- if (is_clean_exit(code, status, success_status))
- return true;
-
- return
- code == CLD_EXITED &&
- IN_SET(status, EXIT_NOTINSTALLED, EXIT_NOTCONFIGURED);
-}
-
void exit_status_set_free(ExitStatusSet *x) {
assert(x);
diff --git a/src/basic/exit-status.h b/src/basic/exit-status.h
index 2309f68815..0cfdfd7891 100644
--- a/src/basic/exit-status.h
+++ b/src/basic/exit-status.h
@@ -31,7 +31,7 @@
* https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html
*/
-typedef enum ExitStatus {
+enum {
/* EXIT_SUCCESS defined by libc */
/* EXIT_FAILURE defined by libc */
EXIT_INVALIDARGUMENT = 2,
@@ -82,7 +82,7 @@ typedef enum ExitStatus {
EXIT_MAKE_STARTER,
EXIT_CHOWN,
EXIT_SMACK_PROCESS_LABEL,
-} ExitStatus;
+};
typedef enum ExitStatusLevel {
EXIT_STATUS_MINIMAL, /* only cover libc EXIT_STATUS/EXIT_FAILURE */
@@ -96,10 +96,14 @@ typedef struct ExitStatusSet {
Set *signal;
} ExitStatusSet;
-const char* exit_status_to_string(ExitStatus status, ExitStatusLevel level) _const_;
+const char* exit_status_to_string(int status, ExitStatusLevel level) _const_;
-bool is_clean_exit(int code, int status, ExitStatusSet *success_status);
-bool is_clean_exit_lsb(int code, int status, ExitStatusSet *success_status);
+typedef enum ExitClean {
+ EXIT_CLEAN_DAEMON,
+ EXIT_CLEAN_COMMAND,
+} ExitClean;
+
+bool is_clean_exit(int code, int status, ExitClean clean, ExitStatusSet *success_status);
void exit_status_set_free(ExitStatusSet *x);
bool exit_status_set_is_empty(ExitStatusSet *x);
diff --git a/src/basic/fileio.c b/src/basic/fileio.c
index f183de4999..1cfb7a98f5 100644
--- a/src/basic/fileio.c
+++ b/src/basic/fileio.c
@@ -37,6 +37,7 @@
#include "hexdecoct.h"
#include "log.h"
#include "macro.h"
+#include "missing.h"
#include "parse-util.h"
#include "path-util.h"
#include "random-util.h"
@@ -47,6 +48,8 @@
#include "umask-util.h"
#include "utf8.h"
+#define READ_FULL_BYTES_MAX (4U*1024U*1024U)
+
int write_string_stream(FILE *f, const char *line, bool enforce_newline) {
assert(f);
@@ -230,7 +233,7 @@ int read_full_stream(FILE *f, char **contents, size_t *size) {
if (S_ISREG(st.st_mode)) {
/* Safety check */
- if (st.st_size > 4*1024*1024)
+ if (st.st_size > READ_FULL_BYTES_MAX)
return -E2BIG;
/* Start with the right file size, but be prepared for
@@ -245,26 +248,31 @@ int read_full_stream(FILE *f, char **contents, size_t *size) {
char *t;
size_t k;
- t = realloc(buf, n+1);
+ t = realloc(buf, n + 1);
if (!t)
return -ENOMEM;
buf = t;
k = fread(buf + l, 1, n - l, f);
+ if (k > 0)
+ l += k;
- if (k <= 0) {
- if (ferror(f))
- return -errno;
+ if (ferror(f))
+ return -errno;
+ if (feof(f))
break;
- }
- l += k;
- n *= 2;
+ /* We aren't expecting fread() to return a short read outside
+ * of (error && eof), assert buffer is full and enlarge buffer.
+ */
+ assert(l == n);
/* Safety check */
- if (n > 4*1024*1024)
+ if (n >= READ_FULL_BYTES_MAX)
return -E2BIG;
+
+ n = MIN(n * 2, READ_FULL_BYTES_MAX);
}
buf[l] = 0;
@@ -1035,7 +1043,7 @@ int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
if (r < 0)
return r;
- fd = mkostemp_safe(t, O_WRONLY|O_CLOEXEC);
+ fd = mkostemp_safe(t);
if (fd < 0) {
free(t);
return -errno;
@@ -1068,7 +1076,7 @@ int fflush_and_check(FILE *f) {
}
/* This is much like mkostemp() but is subject to umask(). */
-int mkostemp_safe(char *pattern, int flags) {
+int mkostemp_safe(char *pattern) {
_cleanup_umask_ mode_t u = 0;
int fd;
@@ -1076,7 +1084,7 @@ int mkostemp_safe(char *pattern, int flags) {
u = umask(077);
- fd = mkostemp(pattern, flags);
+ fd = mkostemp(pattern, O_CLOEXEC);
if (fd < 0)
return -errno;
@@ -1161,8 +1169,8 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) {
char *t, *x;
uint64_t u;
unsigned i;
+ int r;
- assert(p);
assert(ret);
/* Turns this:
@@ -1171,6 +1179,12 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) {
* /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0
*/
+ if (!p) {
+ r = tmp_dir(&p);
+ if (r < 0)
+ return r;
+ }
+
if (!extra)
extra = "";
@@ -1257,24 +1271,25 @@ int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space)
int open_tmpfile_unlinkable(const char *directory, int flags) {
char *p;
- int fd;
+ int fd, r;
- if (!directory)
- directory = "/tmp";
+ if (!directory) {
+ r = tmp_dir(&directory);
+ if (r < 0)
+ return r;
+ }
/* Returns an unlinked temporary file that cannot be linked into the file system anymore */
-#ifdef O_TMPFILE
/* Try O_TMPFILE first, if it is supported */
fd = open(directory, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
if (fd >= 0)
return fd;
-#endif
/* Fall back to unguessable name + unlinking */
p = strjoina(directory, "/systemd-tmp-XXXXXX");
- fd = mkostemp_safe(p, flags);
+ fd = mkostemp_safe(p);
if (fd < 0)
return fd;
@@ -1297,7 +1312,6 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
* which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in
* "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */
-#ifdef O_TMPFILE
{
_cleanup_free_ char *dn = NULL;
@@ -1313,7 +1327,6 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
log_debug_errno(errno, "Failed to use O_TMPFILE on %s: %m", dn);
}
-#endif
r = tempfn_random(target, NULL, &tmp);
if (r < 0)
diff --git a/src/basic/fileio.h b/src/basic/fileio.h
index 9ac497d9eb..b58c83e64a 100644
--- a/src/basic/fileio.h
+++ b/src/basic/fileio.h
@@ -71,7 +71,7 @@ int search_and_fopen_nulstr(const char *path, const char *mode, const char *root
int fflush_and_check(FILE *f);
int fopen_temporary(const char *path, FILE **_f, char **_temp_path);
-int mkostemp_safe(char *pattern, int flags);
+int mkostemp_safe(char *pattern);
int tempfn_xxxxxx(const char *p, const char *extra, char **ret);
int tempfn_random(const char *p, const char *extra, char **ret);
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index f0c6f3265e..48952a1c26 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -496,34 +496,94 @@ int get_files_in_directory(const char *path, char ***list) {
return n;
}
-int var_tmp(char **ret) {
- const char *tmp_dir = NULL;
- const char *env_tmp_dir = NULL;
- char *c = NULL;
- int r;
+static int getenv_tmp_dir(const char **ret_path) {
+ const char *n;
+ int r, ret = 0;
- assert(ret);
+ assert(ret_path);
- env_tmp_dir = getenv("TMPDIR");
- if (env_tmp_dir != NULL) {
- r = is_dir(env_tmp_dir, true);
- if (r < 0 && r != -ENOENT)
- return r;
- if (r > 0)
- tmp_dir = env_tmp_dir;
+ /* We use the same order of environment variables python uses in tempfile.gettempdir():
+ * https://docs.python.org/3/library/tempfile.html#tempfile.gettempdir */
+ FOREACH_STRING(n, "TMPDIR", "TEMP", "TMP") {
+ const char *e;
+
+ e = secure_getenv(n);
+ if (!e)
+ continue;
+ if (!path_is_absolute(e)) {
+ r = -ENOTDIR;
+ goto next;
+ }
+ if (!path_is_safe(e)) {
+ r = -EPERM;
+ goto next;
+ }
+
+ r = is_dir(e, true);
+ if (r < 0)
+ goto next;
+ if (r == 0) {
+ r = -ENOTDIR;
+ goto next;
+ }
+
+ *ret_path = e;
+ return 1;
+
+ next:
+ /* Remember first error, to make this more debuggable */
+ if (ret >= 0)
+ ret = r;
}
- if (!tmp_dir)
- tmp_dir = "/var/tmp";
+ if (ret < 0)
+ return ret;
- c = strdup(tmp_dir);
- if (!c)
- return -ENOMEM;
- *ret = c;
+ *ret_path = NULL;
+ return ret;
+}
+
+static int tmp_dir_internal(const char *def, const char **ret) {
+ const char *e;
+ int r, k;
+
+ assert(def);
+ assert(ret);
+ r = getenv_tmp_dir(&e);
+ if (r > 0) {
+ *ret = e;
+ return 0;
+ }
+
+ k = is_dir(def, true);
+ if (k == 0)
+ k = -ENOTDIR;
+ if (k < 0)
+ return r < 0 ? r : k;
+
+ *ret = def;
return 0;
}
+int var_tmp_dir(const char **ret) {
+
+ /* Returns the location for "larger" temporary files, that is backed by physical storage if available, and thus
+ * even might survive a boot: /var/tmp. If $TMPDIR (or related environment variables) are set, its value is
+ * returned preferably however. Note that both this function and tmp_dir() below are affected by $TMPDIR,
+ * making it a variable that overrides all temporary file storage locations. */
+
+ return tmp_dir_internal("/var/tmp", ret);
+}
+
+int tmp_dir(const char **ret) {
+
+ /* Similar to var_tmp_dir() above, but returns the location for "smaller" temporary files, which is usually
+ * backed by an in-memory file system: /tmp. */
+
+ return tmp_dir_internal("/tmp", ret);
+}
+
int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
char path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
int r;
@@ -537,3 +597,186 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
return r;
}
+
+int chase_symlinks(const char *path, const char *_root, 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 */
+ char *todo;
+ int r;
+
+ assert(path);
+
+ /* 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.
+ *
+ * Note that "root" matters only if we encounter an absolute symlink, it's unused otherwise. Most importantly
+ * this means the path parameter passed in is not prefixed by it.
+ *
+ * Algorithmically this operates on two path buffers: "done" are the components of the path we already
+ * processed and resolved symlinks, "." and ".." of. "todo" are the components of the path we still need to
+ * process. On each iteration, we move one component from "todo" to "done", processing it's special meaning
+ * each time. The "todo" path always starts with at least one slash, the "done" path always ends in no
+ * slash. We always keep an O_PATH fd to the component we are currently processing, thus keeping lookup races
+ * at a minimum. */
+
+ r = path_make_absolute_cwd(path, &buffer);
+ if (r < 0)
+ return r;
+
+ if (_root) {
+ r = path_make_absolute_cwd(_root, &root);
+ if (r < 0)
+ return r;
+ }
+
+ fd = open("/", O_CLOEXEC|O_NOFOLLOW|O_PATH);
+ if (fd < 0)
+ return -errno;
+
+ todo = buffer;
+ for (;;) {
+ _cleanup_free_ char *first = NULL;
+ _cleanup_close_ int child = -1;
+ struct stat st;
+ size_t n, m;
+
+ /* Determine length of first component in the path */
+ n = strspn(todo, "/"); /* The slashes */
+ m = n + strcspn(todo + n, "/"); /* The entire length of the component */
+
+ /* Extract the first component. */
+ first = strndup(todo, m);
+ if (!first)
+ return -ENOMEM;
+
+ todo += m;
+
+ /* Just a single slash? Then we reached the end. */
+ if (isempty(first) || path_equal(first, "/"))
+ break;
+
+ /* Just a dot? Then let's eat this up. */
+ if (path_equal(first, "/."))
+ continue;
+
+ /* 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;
+
+ if (isempty(done) || path_equal(done, "/"))
+ return -EINVAL;
+
+ parent = dirname_malloc(done);
+ if (!parent)
+ return -ENOMEM;
+
+ /* Don't allow this to leave the root dir */
+ if (root &&
+ path_startswith(done, root) &&
+ !path_startswith(parent, root))
+ return -EINVAL;
+
+ free_and_replace(done, parent);
+
+ fd_parent = openat(fd, "..", O_CLOEXEC|O_NOFOLLOW|O_PATH);
+ if (fd_parent < 0)
+ return -errno;
+
+ safe_close(fd);
+ fd = fd_parent;
+
+ continue;
+ }
+
+ /* Otherwise let's see what this is. */
+ child = openat(fd, first + n, O_CLOEXEC|O_NOFOLLOW|O_PATH);
+ if (child < 0)
+ return -errno;
+
+ if (fstat(child, &st) < 0)
+ return -errno;
+
+ if (S_ISLNK(st.st_mode)) {
+ _cleanup_free_ char *destination = NULL;
+
+ /* This is a symlink, in this case read the destination. But let's make sure we don't follow
+ * symlinks without bounds. */
+ if (--max_follow <= 0)
+ return -ELOOP;
+
+ r = readlinkat_malloc(fd, first + n, &destination);
+ if (r < 0)
+ return r;
+ if (isempty(destination))
+ return -EINVAL;
+
+ if (path_is_absolute(destination)) {
+
+ /* An absolute destination. Start the loop from the beginning, but use the root
+ * directory as base. */
+
+ safe_close(fd);
+ fd = open(root ?: "/", O_CLOEXEC|O_NOFOLLOW|O_PATH);
+ if (fd < 0)
+ return -errno;
+
+ free_and_replace(buffer, destination);
+
+ todo = buffer;
+ free(done);
+
+ /* Note that we do not revalidate the root, we take it as is. */
+ if (isempty(root))
+ done = NULL;
+ else {
+ done = strdup(root);
+ if (!done)
+ return -ENOMEM;
+ }
+
+ } else {
+ char *joined;
+
+ /* A relative destination. If so, this is what we'll prefix what's left to do with what
+ * we just read, and start the loop again, but remain in the current directory. */
+
+ joined = strjoin("/", destination, todo, NULL);
+ if (!joined)
+ return -ENOMEM;
+
+ free(buffer);
+ todo = buffer = joined;
+ }
+
+ continue;
+ }
+
+ /* If this is not a symlink, then let's just add the name we read to what we already verified. */
+ if (!done) {
+ done = first;
+ first = NULL;
+ } else {
+ if (!strextend(&done, first, NULL))
+ return -ENOMEM;
+ }
+
+ /* And iterate again, but go one directory further down. */
+ safe_close(fd);
+ fd = child;
+ child = -1;
+ }
+
+ if (!done) {
+ /* Special case, turn the empty string into "/", to indicate the root directory. */
+ done = strdup("/");
+ if (!done)
+ return -ENOMEM;
+ }
+
+ *ret = done;
+ done = NULL;
+
+ return 0;
+}
diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h
index 075e5942b1..31df47cf1e 100644
--- a/src/basic/fs-util.h
+++ b/src/basic/fs-util.h
@@ -61,7 +61,8 @@ int mkfifo_atomic(const char *path, mode_t mode);
int get_files_in_directory(const char *path, char ***list);
-int var_tmp(char **ret);
+int tmp_dir(const char **ret);
+int var_tmp_dir(const char **ret);
#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1)
@@ -76,3 +77,5 @@ union inotify_event_buffer {
};
int inotify_add_watch_fd(int fd, int what, uint32_t mask);
+
+int chase_symlinks(const char *path, const char *_root, char **ret);
diff --git a/src/basic/gunicode.c b/src/basic/gunicode.c
index 542110503f..e6ac0545a4 100644
--- a/src/basic/gunicode.c
+++ b/src/basic/gunicode.c
@@ -26,7 +26,7 @@
char *
utf8_prev_char (const char *p)
{
- while (1)
+ for (;;)
{
p--;
if ((*p & 0xc0) != 0x80)
diff --git a/src/basic/hostname-util.c b/src/basic/hostname-util.c
index 13c3bb6446..e44a357287 100644
--- a/src/basic/hostname-util.c
+++ b/src/basic/hostname-util.c
@@ -163,7 +163,6 @@ char* hostname_cleanup(char *s) {
*(d++) = *p;
dot = false;
}
-
}
if (dot && d > s)
diff --git a/src/basic/list.h b/src/basic/list.h
index 5962aa4211..c3771a177f 100644
--- a/src/basic/list.h
+++ b/src/basic/list.h
@@ -142,6 +142,8 @@
} else { \
if ((_b->name##_prev = _a->name##_prev)) \
_b->name##_prev->name##_next = _b; \
+ else \
+ *_head = _b; \
_b->name##_next = _a; \
_a->name##_prev = _b; \
} \
diff --git a/src/basic/log.c b/src/basic/log.c
index 49b4598b7c..4919d175da 100644
--- a/src/basic/log.c
+++ b/src/basic/log.c
@@ -133,7 +133,7 @@ static int create_log_socket(int type) {
if (fd < 0)
return -errno;
- fd_inc_sndbuf(fd, SNDBUF_SIZE);
+ (void) fd_inc_sndbuf(fd, SNDBUF_SIZE);
/* We need a blocking fd here since we'd otherwise lose
messages way too early. However, let's not hang forever in the
@@ -330,8 +330,6 @@ static int write_to_console(
const char *file,
int line,
const char *func,
- const char *object_field,
- const char *object,
const char *buffer) {
char location[256], prefix[1 + DECIMAL_STR_MAX(int) + 2];
@@ -343,7 +341,7 @@ static int write_to_console(
return 0;
if (log_target == LOG_TARGET_CONSOLE_PREFIXED) {
- sprintf(prefix, "<%i>", level);
+ xsprintf(prefix, "<%i>", level);
IOVEC_SET_STRING(iovec[n++], prefix);
}
@@ -390,8 +388,6 @@ static int write_to_syslog(
const char *file,
int line,
const char *func,
- const char *object_field,
- const char *object,
const char *buffer) {
char header_priority[2 + DECIMAL_STR_MAX(int) + 1],
@@ -453,8 +449,6 @@ static int write_to_kmsg(
const char *file,
int line,
const char *func,
- const char *object_field,
- const char *object,
const char *buffer) {
char header_priority[2 + DECIMAL_STR_MAX(int) + 1],
@@ -485,7 +479,8 @@ static int log_do_header(
int level,
int error,
const char *file, int line, const char *func,
- const char *object_field, const char *object) {
+ const char *object_field, const char *object,
+ const char *extra_field, const char *extra) {
snprintf(header, size,
"PRIORITY=%i\n"
@@ -495,6 +490,7 @@ static int log_do_header(
"%s%s%s"
"%s%.*i%s"
"%s%s%s"
+ "%s%s%s"
"SYSLOG_IDENTIFIER=%s\n",
LOG_PRI(level),
LOG_FAC(level),
@@ -513,6 +509,9 @@ static int log_do_header(
isempty(object) ? "" : object_field,
isempty(object) ? "" : object,
isempty(object) ? "" : "\n",
+ isempty(extra) ? "" : extra_field,
+ isempty(extra) ? "" : extra,
+ isempty(extra) ? "" : "\n",
program_invocation_short_name);
return 0;
@@ -526,6 +525,8 @@ static int write_to_journal(
const char *func,
const char *object_field,
const char *object,
+ const char *extra_field,
+ const char *extra,
const char *buffer) {
char header[LINE_MAX];
@@ -535,7 +536,7 @@ static int write_to_journal(
if (journal_fd < 0)
return 0;
- log_do_header(header, sizeof(header), level, error, file, line, func, object_field, object);
+ log_do_header(header, sizeof(header), level, error, file, line, func, object_field, object, extra_field, extra);
IOVEC_SET_STRING(iovec[0], header);
IOVEC_SET_STRING(iovec[1], "MESSAGE=");
@@ -559,10 +560,15 @@ static int log_dispatch(
const char *func,
const char *object_field,
const char *object,
+ const char *extra,
+ const char *extra_field,
char *buffer) {
assert(buffer);
+ if (error < 0)
+ error = -error;
+
if (log_target == LOG_TARGET_NULL)
return -error;
@@ -570,9 +576,6 @@ static int log_dispatch(
if ((level & LOG_FACMASK) == 0)
level = log_facility | LOG_PRI(level);
- if (error < 0)
- error = -error;
-
do {
char *e;
int k = 0;
@@ -589,7 +592,7 @@ static int log_dispatch(
log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
log_target == LOG_TARGET_JOURNAL) {
- k = write_to_journal(level, error, file, line, func, object_field, object, buffer);
+ k = write_to_journal(level, error, file, line, func, object_field, object, extra_field, extra, buffer);
if (k < 0) {
if (k != -EAGAIN)
log_close_journal();
@@ -600,7 +603,7 @@ static int log_dispatch(
if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
log_target == LOG_TARGET_SYSLOG) {
- k = write_to_syslog(level, error, file, line, func, object_field, object, buffer);
+ k = write_to_syslog(level, error, file, line, func, buffer);
if (k < 0) {
if (k != -EAGAIN)
log_close_syslog();
@@ -615,7 +618,7 @@ static int log_dispatch(
log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
log_target == LOG_TARGET_KMSG)) {
- k = write_to_kmsg(level, error, file, line, func, object_field, object, buffer);
+ k = write_to_kmsg(level, error, file, line, func, buffer);
if (k < 0) {
log_close_kmsg();
log_open_console();
@@ -623,7 +626,7 @@ static int log_dispatch(
}
if (k <= 0)
- (void) write_to_console(level, error, file, line, func, object_field, object, buffer);
+ (void) write_to_console(level, error, file, line, func, buffer);
buffer = e;
} while (buffer);
@@ -649,7 +652,7 @@ int log_dump_internal(
if (_likely_(LOG_PRI(level) > log_max_level))
return -error;
- return log_dispatch(level, error, file, line, func, NULL, NULL, buffer);
+ return log_dispatch(level, error, file, line, func, NULL, NULL, NULL, NULL, buffer);
}
int log_internalv(
@@ -676,7 +679,7 @@ int log_internalv(
vsnprintf(buffer, sizeof(buffer), format, ap);
- return log_dispatch(level, error, file, line, func, NULL, NULL, buffer);
+ return log_dispatch(level, error, file, line, func, NULL, NULL, NULL, NULL, buffer);
}
int log_internal(
@@ -705,6 +708,8 @@ int log_object_internalv(
const char *func,
const char *object_field,
const char *object,
+ const char *extra_field,
+ const char *extra,
const char *format,
va_list ap) {
@@ -738,7 +743,7 @@ int log_object_internalv(
vsnprintf(b, l, format, ap);
- return log_dispatch(level, error, file, line, func, object_field, object, buffer);
+ return log_dispatch(level, error, file, line, func, object_field, object, extra_field, extra, buffer);
}
int log_object_internal(
@@ -749,13 +754,15 @@ int log_object_internal(
const char *func,
const char *object_field,
const char *object,
+ const char *extra_field,
+ const char *extra,
const char *format, ...) {
va_list ap;
int r;
va_start(ap, format);
- r = log_object_internalv(level, error, file, line, func, object_field, object, format, ap);
+ r = log_object_internalv(level, error, file, line, func, object_field, object, extra_field, extra, format, ap);
va_end(ap);
return r;
@@ -775,12 +782,12 @@ static void log_assert(
return;
DISABLE_WARNING_FORMAT_NONLITERAL;
- xsprintf(buffer, format, text, file, line, func);
+ snprintf(buffer, sizeof buffer, format, text, file, line, func);
REENABLE_WARNING;
log_abort_msg = buffer;
- log_dispatch(level, 0, file, line, func, NULL, NULL, buffer);
+ log_dispatch(level, 0, file, line, func, NULL, NULL, NULL, NULL, buffer);
}
noreturn void log_assert_failed(const char *text, const char *file, int line, const char *func) {
@@ -888,7 +895,7 @@ int log_struct_internal(
bool fallback = false;
/* If the journal is available do structured logging */
- log_do_header(header, sizeof(header), level, error, file, line, func, NULL, NULL);
+ log_do_header(header, sizeof(header), level, error, file, line, func, NULL, NULL, NULL, NULL);
IOVEC_SET_STRING(iovec[n++], header);
va_start(ap, format);
@@ -935,7 +942,7 @@ int log_struct_internal(
if (!found)
return -error;
- return log_dispatch(level, error, file, line, func, NULL, NULL, buf + 8);
+ return log_dispatch(level, error, file, line, func, NULL, NULL, NULL, NULL, buf + 8);
}
int log_set_target_from_string(const char *e) {
@@ -960,7 +967,7 @@ int log_set_max_level_from_string(const char *e) {
return 0;
}
-static int parse_proc_cmdline_item(const char *key, const char *value) {
+static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
/*
* The systemd.log_xyz= settings are parsed by all tools, and
@@ -1005,7 +1012,7 @@ void log_parse_environment(void) {
/* Only try to read the command line in daemons.
We assume that anything that has a controlling
tty is user stuff. */
- (void) parse_proc_cmdline(parse_proc_cmdline_item);
+ (void) parse_proc_cmdline(parse_proc_cmdline_item, NULL, true);
e = secure_getenv("SYSTEMD_LOG_TARGET");
if (e && log_set_target_from_string(e) < 0)
diff --git a/src/basic/log.h b/src/basic/log.h
index b6356228d9..2afee20bb5 100644
--- a/src/basic/log.h
+++ b/src/basic/log.h
@@ -100,18 +100,22 @@ int log_object_internal(
const char *func,
const char *object_field,
const char *object,
- const char *format, ...) _printf_(8,9);
+ const char *extra_field,
+ const char *extra,
+ const char *format, ...) _printf_(10,11);
int log_object_internalv(
int level,
int error,
- const char*file,
+ const char *file,
int line,
const char *func,
const char *object_field,
const char *object,
+ const char *extra_field,
+ const char *extra,
const char *format,
- va_list ap) _printf_(8,0);
+ va_list ap) _printf_(9,0);
int log_struct_internal(
int level,
diff --git a/src/basic/missing.h b/src/basic/missing.h
index b1272f8799..4c013be608 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -445,6 +445,10 @@ struct btrfs_ioctl_quota_ctl_args {
#define CGROUP2_SUPER_MAGIC 0x63677270
#endif
+#ifndef CLONE_NEWCGROUP
+#define CLONE_NEWCGROUP 0x02000000
+#endif
+
#ifndef TMPFS_MAGIC
#define TMPFS_MAGIC 0x01021994
#endif
@@ -469,24 +473,44 @@ struct btrfs_ioctl_quota_ctl_args {
#define MS_MOVE 8192
#endif
+#ifndef MS_REC
+#define MS_REC 16384
+#endif
+
#ifndef MS_PRIVATE
-#define MS_PRIVATE (1 << 18)
+#define MS_PRIVATE (1<<18)
#endif
-#ifndef SCM_SECURITY
-#define SCM_SECURITY 0x03
+#ifndef MS_REC
+#define MS_REC (1<<19)
+#endif
+
+#ifndef MS_SHARED
+#define MS_SHARED (1<<20)
+#endif
+
+#ifndef MS_RELATIME
+#define MS_RELATIME (1<<21)
+#endif
+
+#ifndef MS_KERNMOUNT
+#define MS_KERNMOUNT (1<<22)
+#endif
+
+#ifndef MS_I_VERSION
+#define MS_I_VERSION (1<<23)
#endif
#ifndef MS_STRICTATIME
-#define MS_STRICTATIME (1<<24)
+#define MS_STRICTATIME (1<<24)
#endif
-#ifndef MS_REC
-#define MS_REC 16384
+#ifndef MS_LAZYTIME
+#define MS_LAZYTIME (1<<25)
#endif
-#ifndef MS_SHARED
-#define MS_SHARED (1<<20)
+#ifndef SCM_SECURITY
+#define SCM_SECURITY 0x03
#endif
#ifndef PR_SET_NO_NEW_PRIVS
@@ -533,12 +557,21 @@ struct btrfs_ioctl_quota_ctl_args {
# define DRM_IOCTL_DROP_MASTER _IO('d', 0x1f)
#endif
-#if defined(__i386__) || defined(__x86_64__)
-
-/* The precise definition of __O_TMPFILE is arch specific, so let's
- * just define this on x86 where we know the value. */
+/* The precise definition of __O_TMPFILE is arch specific; use the
+ * values defined by the kernel (note: some are hexa, some are octal,
+ * duplicated as-is from the kernel definitions):
+ * - alpha, parisc, sparc: each has a specific value;
+ * - others: they use the "generic" value.
+ */
#ifndef __O_TMPFILE
+#if defined(__alpha__)
+#define __O_TMPFILE 0100000000
+#elif defined(__parisc__) || defined(__hppa__)
+#define __O_TMPFILE 0400000000
+#elif defined(__sparc__) || defined(__sparc64__)
+#define __O_TMPFILE 0x2000000
+#else
#define __O_TMPFILE 020000000
#endif
@@ -1039,6 +1072,10 @@ typedef int32_t key_serial_t;
#define ETHERTYPE_LLDP 0x88cc
#endif
+#ifndef IFA_F_MCAUTOJOIN
+#define IFA_F_MCAUTOJOIN 0x400
+#endif
+
#endif
#include "missing_syscall.h"
diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c
index 28dc778969..c8f8022578 100644
--- a/src/basic/mount-util.c
+++ b/src/basic/mount-util.c
@@ -36,6 +36,7 @@
#include "set.h"
#include "stdio-util.h"
#include "string-util.h"
+#include "strv.h"
static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id) {
char path[strlen("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
@@ -75,7 +76,6 @@ static int fd_fdinfo_mnt_id(int fd, const char *filename, int flags, int *mnt_id
return safe_atoi(p, mnt_id);
}
-
int fd_is_mount_point(int fd, const char *filename, int flags) {
union file_handle_union h = FILE_HANDLE_INIT, h_parent = FILE_HANDLE_INIT;
int mount_id = -1, mount_id_parent = -1;
@@ -162,7 +162,7 @@ int fd_is_mount_point(int fd, const char *filename, int flags) {
fallback_fdinfo:
r = fd_fdinfo_mnt_id(fd, filename, flags, &mount_id);
- if (r == -EOPNOTSUPP)
+ if (IN_SET(r, -EOPNOTSUPP, -EACCES))
goto fallback_fstat;
if (r < 0)
return r;
@@ -288,10 +288,12 @@ int umount_recursive(const char *prefix, int flags) {
continue;
if (umount2(p, flags) < 0) {
- r = -errno;
+ r = log_debug_errno(errno, "Failed to umount %s: %m", p);
continue;
}
+ log_debug("Successfully unmounted %s", p);
+
again = true;
n++;
@@ -312,24 +314,21 @@ static int get_mount_flags(const char *path, unsigned long *flags) {
return 0;
}
-int bind_remount_recursive(const char *prefix, bool ro) {
+int bind_remount_recursive(const char *prefix, bool ro, char **blacklist) {
_cleanup_set_free_free_ Set *done = NULL;
_cleanup_free_ char *cleaned = NULL;
int r;
- /* Recursively remount a directory (and all its submounts)
- * read-only or read-write. If the directory is already
- * mounted, we reuse the mount and simply mark it
- * MS_BIND|MS_RDONLY (or remove the MS_RDONLY for read-write
- * operation). If it isn't we first make it one. Afterwards we
- * apply MS_BIND|MS_RDONLY (or remove MS_RDONLY) to all
- * submounts we can access, too. When mounts are stacked on
- * the same mount point we only care for each individual
- * "top-level" mount on each point, as we cannot
- * influence/access the underlying mounts anyway. We do not
- * have any effect on future submounts that might get
- * propagated, they migt be writable. This includes future
- * submounts that have been triggered via autofs. */
+ /* Recursively remount a directory (and all its submounts) read-only or read-write. If the directory is already
+ * mounted, we reuse the mount and simply mark it MS_BIND|MS_RDONLY (or remove the MS_RDONLY for read-write
+ * operation). If it isn't we first make it one. Afterwards we apply MS_BIND|MS_RDONLY (or remove MS_RDONLY) to
+ * all submounts we can access, too. When mounts are stacked on the same mount point we only care for each
+ * individual "top-level" mount on each point, as we cannot influence/access the underlying mounts anyway. We
+ * do not have any effect on future submounts that might get propagated, they migt be writable. This includes
+ * future submounts that have been triggered via autofs.
+ *
+ * If the "blacklist" parameter is specified it may contain a list of subtrees to exclude from the
+ * remount operation. Note that we'll ignore the blacklist for the top-level path. */
cleaned = strdup(prefix);
if (!cleaned)
@@ -386,6 +385,33 @@ int bind_remount_recursive(const char *prefix, bool ro) {
if (r < 0)
return r;
+ if (!path_startswith(p, cleaned))
+ continue;
+
+ /* Ignore this mount if it is blacklisted, but only if it isn't the top-level mount we shall
+ * operate on. */
+ if (!path_equal(cleaned, p)) {
+ bool blacklisted = false;
+ char **i;
+
+ STRV_FOREACH(i, blacklist) {
+
+ if (path_equal(*i, cleaned))
+ continue;
+
+ if (!path_startswith(*i, cleaned))
+ continue;
+
+ if (path_startswith(p, *i)) {
+ blacklisted = true;
+ log_debug("Not remounting %s, because blacklisted by %s, called for %s", p, *i, cleaned);
+ break;
+ }
+ }
+ if (blacklisted)
+ continue;
+ }
+
/* Let's ignore autofs mounts. If they aren't
* triggered yet, we want to avoid triggering
* them, as we don't make any guarantees for
@@ -397,12 +423,9 @@ int bind_remount_recursive(const char *prefix, bool ro) {
continue;
}
- if (path_startswith(p, cleaned) &&
- !set_contains(done, p)) {
-
+ if (!set_contains(done, p)) {
r = set_consume(todo, p);
p = NULL;
-
if (r == -EEXIST)
continue;
if (r < 0)
@@ -419,8 +442,7 @@ int bind_remount_recursive(const char *prefix, bool ro) {
if (!set_contains(done, cleaned) &&
!set_contains(todo, cleaned)) {
- /* The prefix directory itself is not yet a
- * mount, make it one. */
+ /* The prefix directory itself is not yet a mount, make it one. */
if (mount(cleaned, cleaned, NULL, MS_BIND|MS_REC, NULL) < 0)
return -errno;
@@ -431,6 +453,8 @@ int bind_remount_recursive(const char *prefix, bool ro) {
if (mount(NULL, prefix, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0)
return -errno;
+ log_debug("Made top-level directory %s a mount point.", prefix);
+
x = strdup(cleaned);
if (!x)
return -ENOMEM;
@@ -448,8 +472,7 @@ int bind_remount_recursive(const char *prefix, bool ro) {
if (r < 0)
return r;
- /* Deal with mount points that are obstructed by a
- * later mount */
+ /* Deal with mount points that are obstructed by a later mount */
r = path_is_mount_point(x, 0);
if (r == -ENOENT || r == 0)
continue;
@@ -464,6 +487,7 @@ int bind_remount_recursive(const char *prefix, bool ro) {
if (mount(NULL, x, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0)
return -errno;
+ log_debug("Remounted %s read-only.", x);
}
}
}
@@ -501,6 +525,7 @@ bool fstype_is_network(const char *fstype) {
"glusterfs\0"
"pvfs2\0" /* OrangeFS */
"ocfs2\0"
+ "lustre\0"
;
const char *x;
@@ -557,3 +582,108 @@ const char* mode_to_inaccessible_node(mode_t mode) {
}
return NULL;
}
+
+#define FLAG(name) (flags & name ? STRINGIFY(name) "|" : "")
+static char* mount_flags_to_string(long unsigned flags) {
+ char *x;
+ _cleanup_free_ char *y = NULL;
+ long unsigned overflow;
+
+ overflow = flags & ~(MS_RDONLY |
+ MS_NOSUID |
+ MS_NODEV |
+ MS_NOEXEC |
+ MS_SYNCHRONOUS |
+ MS_REMOUNT |
+ MS_MANDLOCK |
+ MS_DIRSYNC |
+ MS_NOATIME |
+ MS_NODIRATIME |
+ MS_BIND |
+ MS_MOVE |
+ MS_REC |
+ MS_SILENT |
+ MS_POSIXACL |
+ MS_UNBINDABLE |
+ MS_PRIVATE |
+ MS_SLAVE |
+ MS_SHARED |
+ MS_RELATIME |
+ MS_KERNMOUNT |
+ MS_I_VERSION |
+ MS_STRICTATIME |
+ MS_LAZYTIME);
+
+ if (flags == 0 || overflow != 0)
+ if (asprintf(&y, "%lx", overflow) < 0)
+ return NULL;
+
+ x = strjoin(FLAG(MS_RDONLY),
+ FLAG(MS_NOSUID),
+ FLAG(MS_NODEV),
+ FLAG(MS_NOEXEC),
+ FLAG(MS_SYNCHRONOUS),
+ FLAG(MS_REMOUNT),
+ FLAG(MS_MANDLOCK),
+ FLAG(MS_DIRSYNC),
+ FLAG(MS_NOATIME),
+ FLAG(MS_NODIRATIME),
+ FLAG(MS_BIND),
+ FLAG(MS_MOVE),
+ FLAG(MS_REC),
+ FLAG(MS_SILENT),
+ FLAG(MS_POSIXACL),
+ FLAG(MS_UNBINDABLE),
+ FLAG(MS_PRIVATE),
+ FLAG(MS_SLAVE),
+ FLAG(MS_SHARED),
+ FLAG(MS_RELATIME),
+ FLAG(MS_KERNMOUNT),
+ FLAG(MS_I_VERSION),
+ FLAG(MS_STRICTATIME),
+ FLAG(MS_LAZYTIME),
+ y, NULL);
+ if (!x)
+ return NULL;
+ if (!y)
+ x[strlen(x) - 1] = '\0'; /* truncate the last | */
+ return x;
+}
+
+int mount_verbose(
+ int error_log_level,
+ const char *what,
+ const char *where,
+ const char *type,
+ unsigned long flags,
+ const char *options) {
+
+ _cleanup_free_ char *fl = NULL;
+
+ fl = mount_flags_to_string(flags);
+
+ if ((flags & MS_REMOUNT) && !what && !type)
+ log_debug("Remounting %s (%s \"%s\")...",
+ where, strnull(fl), strempty(options));
+ else if (!what && !type)
+ log_debug("Mounting %s (%s \"%s\")...",
+ where, strnull(fl), strempty(options));
+ else if ((flags & MS_BIND) && !type)
+ log_debug("Bind-mounting %s on %s (%s \"%s\")...",
+ what, where, strnull(fl), strempty(options));
+ else
+ log_debug("Mounting %s on %s (%s \"%s\")...",
+ strna(type), where, strnull(fl), strempty(options));
+ if (mount(what, where, type, flags, options) < 0)
+ return log_full_errno(error_log_level, errno,
+ "Failed to mount %s on %s (%s \"%s\"): %m",
+ strna(type), where, strnull(fl), strempty(options));
+ return 0;
+}
+
+int umount_verbose(const char *what) {
+ log_debug("Umounting %s...", what);
+ if (umount(what) < 0)
+ return log_error_errno(errno, "Failed to unmount %s: %m", what);
+ return 0;
+}
diff --git a/src/basic/mount-util.h b/src/basic/mount-util.h
index f46989ebb3..4f305df19f 100644
--- a/src/basic/mount-util.h
+++ b/src/basic/mount-util.h
@@ -35,7 +35,7 @@ int path_is_mount_point(const char *path, int flags);
int repeat_unmount(const char *path, int flags);
int umount_recursive(const char *target, int flags);
-int bind_remount_recursive(const char *prefix, bool ro);
+int bind_remount_recursive(const char *prefix, bool ro, char **blacklist);
int mount_move_root(const char *path);
@@ -52,3 +52,12 @@ union file_handle_union {
const char* mode_to_inaccessible_node(mode_t mode);
#define FILE_HANDLE_INIT { .handle.handle_bytes = MAX_HANDLE_SZ }
+
+int mount_verbose(
+ int error_log_level,
+ const char *what,
+ const char *where,
+ const char *type,
+ unsigned long flags,
+ const char *options);
+int umount_verbose(const char *where);
diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c
index 503a895731..c98815b9bc 100644
--- a/src/basic/parse-util.c
+++ b/src/basic/parse-util.c
@@ -29,6 +29,7 @@
#include "extract-word.h"
#include "macro.h"
#include "parse-util.h"
+#include "process-util.h"
#include "string-util.h"
int parse_boolean(const char *v) {
@@ -533,7 +534,7 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
return 0;
}
-int parse_percent(const char *p) {
+int parse_percent_unbounded(const char *p) {
const char *pc, *n;
unsigned v;
int r;
@@ -546,8 +547,30 @@ int parse_percent(const char *p) {
r = safe_atou(n, &v);
if (r < 0)
return r;
+
+ return (int) v;
+}
+
+int parse_percent(const char *p) {
+ int v;
+
+ v = parse_percent_unbounded(p);
if (v > 100)
return -ERANGE;
- return (int) v;
+ return v;
+}
+
+int parse_nice(const char *p, int *ret) {
+ int n, r;
+
+ r = safe_atoi(p, &n);
+ if (r < 0)
+ return r;
+
+ if (!nice_is_valid(n))
+ return -ERANGE;
+
+ *ret = n;
+ return 0;
}
diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h
index 73441bb6fd..461e1cd4d8 100644
--- a/src/basic/parse-util.h
+++ b/src/basic/parse-util.h
@@ -106,4 +106,7 @@ int safe_atod(const char *s, double *ret_d);
int parse_fractional_part_u(const char **s, size_t digits, unsigned *res);
+int parse_percent_unbounded(const char *p);
int parse_percent(const char *p);
+
+int parse_nice(const char *p, int *ret);
diff --git a/src/basic/path-util.c b/src/basic/path-util.c
index b2fa81a294..fd38f51c4c 100644
--- a/src/basic/path-util.c
+++ b/src/basic/path-util.c
@@ -34,9 +34,11 @@
#include "alloc-util.h"
#include "extract-word.h"
#include "fs-util.h"
+#include "glob-util.h"
#include "log.h"
#include "macro.h"
#include "missing.h"
+#include "parse-util.h"
#include "path-util.h"
#include "stat-util.h"
#include "string-util.h"
@@ -286,9 +288,7 @@ char **path_strv_resolve(char **l, const char *prefix) {
} else {
/* canonicalized path goes outside of
* prefix, keep the original path instead */
- free(u);
- u = orig;
- orig = NULL;
+ free_and_replace(u, orig);
}
} else
free(t);
@@ -354,6 +354,16 @@ char* path_startswith(const char *path, const char *prefix) {
assert(path);
assert(prefix);
+ /* Returns a pointer to the start of the first component after the parts matched by
+ * the prefix, iff
+ * - both paths are absolute or both paths are relative,
+ * and
+ * - each component in prefix in turn matches a component in path at the same position.
+ * An empty string will be returned when the prefix and path are equivalent.
+ *
+ * Returns NULL otherwise.
+ */
+
if ((path[0] == '/') != (prefix[0] == '/'))
return NULL;
@@ -810,7 +820,78 @@ bool is_device_path(const char *path) {
/* Returns true on paths that refer to a device, either in
* sysfs or in /dev */
- return
- path_startswith(path, "/dev/") ||
- path_startswith(path, "/sys/");
+ return path_startswith(path, "/dev/") ||
+ path_startswith(path, "/sys/");
+}
+
+bool is_deviceallow_pattern(const char *path) {
+ return path_startswith(path, "/dev/") ||
+ startswith(path, "block-") ||
+ startswith(path, "char-");
+}
+
+int systemd_installation_has_version(const char *root, unsigned minimal_version) {
+ const char *pattern;
+ int r;
+
+ /* Try to guess if systemd installation is later than the specified version. This
+ * is hacky and likely to yield false negatives, particularly if the installation
+ * is non-standard. False positives should be relatively rare.
+ */
+
+ NULSTR_FOREACH(pattern,
+ /* /lib works for systems without usr-merge, and for systems with a sane
+ * usr-merge, where /lib is a symlink to /usr/lib. /usr/lib is necessary
+ * for Gentoo which does a merge without making /lib a symlink.
+ */
+ "lib/systemd/libsystemd-shared-*.so\0"
+ "usr/lib/systemd/libsystemd-shared-*.so\0") {
+
+ _cleanup_strv_free_ char **names = NULL;
+ _cleanup_free_ char *path = NULL;
+ char *c, **name;
+
+ path = prefix_root(root, pattern);
+ if (!path)
+ return -ENOMEM;
+
+ r = glob_extend(&names, path);
+ if (r == -ENOENT)
+ continue;
+ if (r < 0)
+ return r;
+
+ assert_se((c = endswith(path, "*.so")));
+ *c = '\0'; /* truncate the glob part */
+
+ STRV_FOREACH(name, names) {
+ /* This is most likely to run only once, hence let's not optimize anything. */
+ char *t, *t2;
+ unsigned version;
+
+ t = startswith(*name, path);
+ if (!t)
+ continue;
+
+ t2 = endswith(t, ".so");
+ if (!t2)
+ continue;
+
+ t2[0] = '\0'; /* truncate the suffix */
+
+ r = safe_atou(t, &version);
+ if (r < 0) {
+ log_debug_errno(r, "Found libsystemd shared at \"%s.so\", but failed to parse version: %m", *name);
+ continue;
+ }
+
+ log_debug("Found libsystemd shared at \"%s.so\", version %u (%s).",
+ *name, version,
+ version >= minimal_version ? "OK" : "too old");
+ if (version >= minimal_version)
+ return true;
+ }
+ }
+
+ return false;
}
diff --git a/src/basic/path-util.h b/src/basic/path-util.h
index a27c13fcc3..66545f52d9 100644
--- a/src/basic/path-util.h
+++ b/src/basic/path-util.h
@@ -125,3 +125,6 @@ char *file_in_same_dir(const char *path, const char *filename);
bool hidden_or_backup_file(const char *filename) _pure_;
bool is_device_path(const char *path);
+bool is_deviceallow_pattern(const char *path);
+
+int systemd_installation_has_version(const char *root, unsigned minimal_version);
diff --git a/src/basic/prioq.c b/src/basic/prioq.c
index d2ec516d29..4570b8e4ba 100644
--- a/src/basic/prioq.c
+++ b/src/basic/prioq.c
@@ -62,9 +62,7 @@ Prioq* prioq_free(Prioq *q) {
return NULL;
free(q->items);
- free(q);
-
- return NULL;
+ return mfree(q);
}
int prioq_ensure_allocated(Prioq **q, compare_func_t compare_func) {
diff --git a/src/basic/proc-cmdline.c b/src/basic/proc-cmdline.c
index 0430beadaa..8297a222b7 100644
--- a/src/basic/proc-cmdline.c
+++ b/src/basic/proc-cmdline.c
@@ -42,7 +42,9 @@ int proc_cmdline(char **ret) {
return read_one_line_file("/proc/cmdline", ret);
}
-int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value)) {
+int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value, void *data),
+ void *data,
+ bool strip_prefix) {
_cleanup_free_ char *line = NULL;
const char *p;
int r;
@@ -56,7 +58,7 @@ int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value)) {
p = line;
for (;;) {
_cleanup_free_ char *word = NULL;
- char *value = NULL;
+ char *value = NULL, *unprefixed;
r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
if (r < 0)
@@ -66,14 +68,15 @@ int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value)) {
/* Filter out arguments that are intended only for the
* initrd */
- if (!in_initrd() && startswith(word, "rd."))
+ unprefixed = startswith(word, "rd.");
+ if (unprefixed && !in_initrd())
continue;
value = strchr(word, '=');
if (value)
*(value++) = 0;
- r = parse_item(word, value);
+ r = parse_item(strip_prefix && unprefixed ? unprefixed : word, value, data);
if (r < 0)
return r;
}
diff --git a/src/basic/proc-cmdline.h b/src/basic/proc-cmdline.h
index 452642a2f5..6d6ee95c11 100644
--- a/src/basic/proc-cmdline.h
+++ b/src/basic/proc-cmdline.h
@@ -20,7 +20,9 @@
***/
int proc_cmdline(char **ret);
-int parse_proc_cmdline(int (*parse_word)(const char *key, const char *value));
+int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value, void *data),
+ void *data,
+ bool strip_prefix);
int get_proc_cmdline_key(const char *parameter, char **value);
int shall_restore_state(void);
diff --git a/src/basic/process-util.h b/src/basic/process-util.h
index 9f75088796..2568e3834f 100644
--- a/src/basic/process-util.h
+++ b/src/basic/process-util.h
@@ -26,6 +26,7 @@
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
+#include <sys/resource.h>
#include "formats-util.h"
#include "macro.h"
@@ -103,3 +104,7 @@ int sched_policy_from_string(const char *s);
void valgrind_summary_hack(void);
int pid_compare_func(const void *a, const void *b);
+
+static inline bool nice_is_valid(int n) {
+ return n >= PRIO_MIN && n < PRIO_MAX;
+}
diff --git a/src/basic/replace-var.c b/src/basic/replace-var.c
index 6a204b9ec3..0d21423a9c 100644
--- a/src/basic/replace-var.c
+++ b/src/basic/replace-var.c
@@ -107,6 +107,5 @@ char *replace_var(const char *text, char *(*lookup)(const char *variable, void*u
return r;
oom:
- free(r);
- return NULL;
+ return mfree(r);
}
diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c
index 43816fd1bb..baa70c2c8d 100644
--- a/src/basic/rm-rf.c
+++ b/src/basic/rm-rf.c
@@ -27,6 +27,7 @@
#include <unistd.h>
#include "btrfs-util.h"
+#include "cgroup-util.h"
#include "fd-util.h"
#include "log.h"
#include "macro.h"
@@ -36,9 +37,14 @@
#include "stat-util.h"
#include "string-util.h"
+static bool is_physical_fs(const struct statfs *sfs) {
+ return !is_temporary_fs(sfs) && !is_cgroup_fs(sfs);
+}
+
int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
_cleanup_closedir_ DIR *d = NULL;
int ret = 0, r;
+ struct statfs sfs;
assert(fd >= 0);
@@ -47,13 +53,13 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
if (!(flags & REMOVE_PHYSICAL)) {
- r = fd_is_temporary_fs(fd);
+ r = fstatfs(fd, &sfs);
if (r < 0) {
safe_close(fd);
- return r;
+ return -errno;
}
- if (!r) {
+ if (is_physical_fs(&sfs)) {
/* We refuse to clean physical file systems
* with this call, unless explicitly
* requested. This is extra paranoia just to
@@ -210,7 +216,7 @@ int rm_rf(const char *path, RemoveFlags flags) {
if (statfs(path, &s) < 0)
return -errno;
- if (!is_temporary_fs(&s)) {
+ if (is_physical_fs(&s)) {
log_error("Attempted to remove disk file system, and we can't allow that.");
return -EPERM;
}
diff --git a/src/basic/set.h b/src/basic/set.h
index 12f64a8c57..a5f8beb0c4 100644
--- a/src/basic/set.h
+++ b/src/basic/set.h
@@ -23,8 +23,8 @@
#include "hashmap.h"
#include "macro.h"
-Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
-#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS)
+Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS)
static inline Set *set_free(Set *s) {
internal_hashmap_free(HASHMAP_BASE(s));
@@ -42,8 +42,8 @@ static inline Set *set_copy(Set *s) {
return (Set*) internal_hashmap_copy(HASHMAP_BASE(s));
}
-int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
-#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
+int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS);
+#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS)
int set_put(Set *s, const void *key);
/* no set_update */
diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c
index 385c3e4df3..1662c04705 100644
--- a/src/basic/socket-util.c
+++ b/src/basic/socket-util.c
@@ -441,7 +441,7 @@ const char* socket_address_get_path(const SocketAddress *a) {
}
bool socket_ipv6_is_supported(void) {
- if (access("/proc/net/sockstat6", F_OK) != 0)
+ if (access("/proc/net/if_inet6", F_OK) != 0)
return false;
return true;
@@ -1046,3 +1046,34 @@ int flush_accept(int fd) {
close(cfd);
}
}
+
+struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length) {
+ struct cmsghdr *cmsg;
+
+ assert(mh);
+
+ CMSG_FOREACH(cmsg, mh)
+ if (cmsg->cmsg_level == level &&
+ cmsg->cmsg_type == type &&
+ (length == (socklen_t) -1 || length == cmsg->cmsg_len))
+ return cmsg;
+
+ return NULL;
+}
+
+int socket_ioctl_fd(void) {
+ int fd;
+
+ /* Create a socket to invoke the various network interface ioctl()s on. Traditionally only AF_INET was good for
+ * that. Since kernel 4.6 AF_NETLINK works for this too. We first try to use AF_INET hence, but if that's not
+ * available (for example, because it is made unavailable via SECCOMP or such), we'll fall back to the more
+ * generic AF_NETLINK. */
+
+ fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0);
+ if (fd < 0)
+ fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_GENERIC);
+ if (fd < 0)
+ return -errno;
+
+ return fd;
+}
diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h
index e9230e4a9f..2ef572badb 100644
--- a/src/basic/socket-util.h
+++ b/src/basic/socket-util.h
@@ -142,6 +142,8 @@ int flush_accept(int fd);
#define CMSG_FOREACH(cmsg, mh) \
for ((cmsg) = CMSG_FIRSTHDR(mh); (cmsg); (cmsg) = CMSG_NXTHDR((mh), (cmsg)))
+struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t length);
+
/* Covers only file system and abstract AF_UNIX socket addresses, but not unnamed socket addresses. */
#define SOCKADDR_UN_LEN(sa) \
({ \
@@ -152,3 +154,5 @@ int flush_accept(int fd);
1 + strnlen(_sa->sun_path+1, sizeof(_sa->sun_path)-1) : \
strnlen(_sa->sun_path, sizeof(_sa->sun_path))); \
})
+
+int socket_ioctl_fd(void);
diff --git a/src/basic/special.h b/src/basic/special.h
index 084d3dfa23..5276bcf598 100644
--- a/src/basic/special.h
+++ b/src/basic/special.h
@@ -117,3 +117,6 @@
/* The scope unit systemd itself lives in. */
#define SPECIAL_INIT_SCOPE "init.scope"
+
+/* The root directory. */
+#define SPECIAL_ROOT_MOUNT "-.mount"
diff --git a/src/basic/strbuf.c b/src/basic/strbuf.c
index 4bef87d3c2..00aaf9e621 100644
--- a/src/basic/strbuf.c
+++ b/src/basic/strbuf.c
@@ -62,8 +62,7 @@ struct strbuf *strbuf_new(void) {
err:
free(str->buf);
free(str->root);
- free(str);
- return NULL;
+ return mfree(str);
}
static void strbuf_node_cleanup(struct strbuf_node *node) {
diff --git a/src/basic/string-util.c b/src/basic/string-util.c
index 293a15f9c0..6b06e643c9 100644
--- a/src/basic/string-util.c
+++ b/src/basic/string-util.c
@@ -22,6 +22,7 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include "alloc-util.h"
#include "gunicode.h"
@@ -323,6 +324,14 @@ char ascii_tolower(char x) {
return x;
}
+char ascii_toupper(char x) {
+
+ if (x >= 'a' && x <= 'z')
+ return x - 'a' + 'A';
+
+ return x;
+}
+
char *ascii_strlower(char *t) {
char *p;
@@ -334,6 +343,17 @@ char *ascii_strlower(char *t) {
return t;
}
+char *ascii_strupper(char *t) {
+ char *p;
+
+ assert(t);
+
+ for (p = t; *p; p++)
+ *p = ascii_toupper(*p);
+
+ return t;
+}
+
char *ascii_strlower_n(char *t, size_t n) {
size_t i;
@@ -423,7 +443,7 @@ static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_le
if (old_length <= 3 || old_length <= new_length)
return strndup(s, old_length);
- r = new0(char, new_length+1);
+ r = new0(char, new_length+3);
if (!r)
return NULL;
@@ -433,12 +453,12 @@ static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_le
x = new_length - 3;
memcpy(r, s, x);
- r[x] = '.';
- r[x+1] = '.';
- r[x+2] = '.';
+ r[x] = 0xe2; /* tri-dot ellipsis: … */
+ r[x+1] = 0x80;
+ r[x+2] = 0xa6;
memcpy(r + x + 3,
- s + old_length - (new_length - x - 3),
- new_length - x - 3);
+ s + old_length - (new_length - x - 1),
+ new_length - x - 1);
return r;
}
@@ -590,8 +610,7 @@ char *strreplace(const char *text, const char *old_string, const char *new_strin
return r;
oom:
- free(r);
- return NULL;
+ return mfree(r);
}
char *strip_tab_ansi(char **ibuf, size_t *_isz) {
@@ -662,8 +681,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
if (ferror(f)) {
fclose(f);
- free(obuf);
- return NULL;
+ return mfree(obuf);
}
fclose(f);
@@ -803,25 +821,20 @@ int free_and_strdup(char **p, const char *s) {
return 1;
}
-#pragma GCC push_options
-#pragma GCC optimize("O0")
+/*
+ * Pointer to memset is volatile so that compiler must de-reference
+ * the pointer and can't assume that it points to any function in
+ * particular (such as memset, which it then might further "optimize")
+ * This approach is inspired by openssl's crypto/mem_clr.c.
+ */
+typedef void *(*memset_t)(void *,int,size_t);
-void* memory_erase(void *p, size_t l) {
- volatile uint8_t* x = (volatile uint8_t*) p;
-
- /* This basically does what memset() does, but hopefully isn't
- * optimized away by the compiler. One of those days, when
- * glibc learns memset_s() we should replace this call by
- * memset_s(), but until then this has to do. */
-
- for (; l > 0; l--)
- *(x++) = 'x';
+static volatile memset_t memset_func = memset;
- return p;
+void* memory_erase(void *p, size_t l) {
+ return memset_func(p, 'x', l);
}
-#pragma GCC pop_options
-
char* string_erase(char *x) {
if (!x)
diff --git a/src/basic/string-util.h b/src/basic/string-util.h
index 1209e1e2e1..d029d538bd 100644
--- a/src/basic/string-util.h
+++ b/src/basic/string-util.h
@@ -70,6 +70,10 @@ static inline const char *empty_to_null(const char *p) {
return isempty(p) ? NULL : p;
}
+static inline const char *strdash_if_empty(const char *str) {
+ return isempty(str) ? "-" : str;
+}
+
static inline char *startswith(const char *s, const char *prefix) {
size_t l;
@@ -137,6 +141,9 @@ char ascii_tolower(char x);
char *ascii_strlower(char *s);
char *ascii_strlower_n(char *s, size_t n);
+char ascii_toupper(char x);
+char *ascii_strupper(char *s);
+
int ascii_strcasecmp_n(const char *a, const char *b, size_t n);
int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m);
diff --git a/src/basic/strv.c b/src/basic/strv.c
index 34e464d253..0eec868eed 100644
--- a/src/basic/strv.c
+++ b/src/basic/strv.c
@@ -87,8 +87,7 @@ void strv_clear(char **l) {
char **strv_free(char **l) {
strv_clear(l);
- free(l);
- return NULL;
+ return mfree(l);
}
char **strv_free_erase(char **l) {
@@ -426,8 +425,7 @@ char *strv_join_quoted(char **l) {
return buf;
oom:
- free(buf);
- return NULL;
+ return mfree(buf);
}
int strv_push(char ***l, char *value) {
@@ -869,8 +867,7 @@ char ***strv_free_free(char ***l) {
for (i = l; *i; i++)
strv_free(*i);
- free(l);
- return NULL;
+ return mfree(l);
}
char **strv_skip(char **l, size_t n) {
diff --git a/src/basic/strv.h b/src/basic/strv.h
index 683ce83a2a..385ad17779 100644
--- a/src/basic/strv.h
+++ b/src/basic/strv.h
@@ -96,10 +96,13 @@ bool strv_overlap(char **a, char **b) _pure_;
#define STRV_FOREACH(s, l) \
for ((s) = (l); (s) && *(s); (s)++)
-#define STRV_FOREACH_BACKWARDS(s, l) \
- STRV_FOREACH(s, l) \
- ; \
- for ((s)--; (l) && ((s) >= (l)); (s)--)
+#define STRV_FOREACH_BACKWARDS(s, l) \
+ for (s = ({ \
+ char **_l = l; \
+ _l ? _l + strv_length(_l) - 1U : NULL; \
+ }); \
+ (l) && ((s) >= (l)); \
+ (s)--)
#define STRV_FOREACH_PAIR(x, y, l) \
for ((x) = (l), (y) = (x+1); (x) && *(x) && *(y); (x) += 2, (y) = (x + 1))
@@ -141,6 +144,11 @@ void strv_print(char **l);
})
#define STR_IN_SET(x, ...) strv_contains(STRV_MAKE(__VA_ARGS__), x)
+#define STRPTR_IN_SET(x, ...) \
+ ({ \
+ const char* _x = (x); \
+ _x && strv_contains(STRV_MAKE(__VA_ARGS__), _x); \
+ })
#define FOREACH_STRING(x, ...) \
for (char **_l = ({ \
diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c
index df56d85317..eafdea9eb3 100644
--- a/src/basic/terminal-util.c
+++ b/src/basic/terminal-util.c
@@ -39,6 +39,7 @@
#include <unistd.h>
#include "alloc-util.h"
+#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
@@ -345,12 +346,7 @@ int open_terminal(const char *name, int mode) {
}
r = isatty(fd);
- if (r < 0) {
- safe_close(fd);
- return -errno;
- }
-
- if (!r) {
+ if (r == 0) {
safe_close(fd);
return -ENOTTY;
}
@@ -785,7 +781,7 @@ bool tty_is_vc_resolve(const char *tty) {
}
const char *default_term_for_tty(const char *tty) {
- return tty && tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt220";
+ return tty && tty_is_vc_resolve(tty) ? "linux" : "vt220";
}
int fd_columns(int fd) {
@@ -1191,12 +1187,9 @@ int open_terminal_in_namespace(pid_t pid, const char *name, int mode) {
return receive_one_fd(pair[0], 0);
}
-bool terminal_is_dumb(void) {
+static bool getenv_terminal_is_dumb(void) {
const char *e;
- if (!on_tty())
- return true;
-
e = getenv("TERM");
if (!e)
return true;
@@ -1204,15 +1197,25 @@ bool terminal_is_dumb(void) {
return streq(e, "dumb");
}
+bool terminal_is_dumb(void) {
+ if (!on_tty())
+ return true;
+
+ return getenv_terminal_is_dumb();
+}
+
bool colors_enabled(void) {
static int enabled = -1;
if (_unlikely_(enabled < 0)) {
- const char *colors;
-
- colors = getenv("SYSTEMD_COLORS");
- if (colors)
- enabled = parse_boolean(colors) != 0;
+ int val;
+
+ val = getenv_bool("SYSTEMD_COLORS");
+ if (val >= 0)
+ enabled = val;
+ else if (getpid() == 1)
+ /* PID1 outputs to the console without holding it open all the time */
+ enabled = !getenv_terminal_is_dumb();
else
enabled = !terminal_is_dumb();
}
diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h
index 169ab772ff..b862bfaf05 100644
--- a/src/basic/terminal-util.h
+++ b/src/basic/terminal-util.h
@@ -36,6 +36,10 @@
#define ANSI_HIGHLIGHT_YELLOW "\x1B[0;1;33m"
#define ANSI_HIGHLIGHT_BLUE "\x1B[0;1;34m"
#define ANSI_HIGHLIGHT_UNDERLINE "\x1B[0;1;4m"
+#define ANSI_HIGHLIGHT_RED_UNDERLINE "\x1B[0;1;4;31m"
+#define ANSI_HIGHLIGHT_GREEN_UNDERLINE "\x1B[0;1;4;32m"
+#define ANSI_HIGHLIGHT_YELLOW_UNDERLINE "\x1B[0;1;4;33m"
+#define ANSI_HIGHLIGHT_BLUE_UNDERLINE "\x1B[0;1;4;34m"
#define ANSI_NORMAL "\x1B[0m"
#define ANSI_ERASE_TO_END_OF_LINE "\x1B[K"
@@ -83,37 +87,24 @@ bool on_tty(void);
bool terminal_is_dumb(void);
bool colors_enabled(void);
-static inline const char *ansi_underline(void) {
- return colors_enabled() ? ANSI_UNDERLINE : "";
-}
-
-static inline const char *ansi_highlight(void) {
- return colors_enabled() ? ANSI_HIGHLIGHT : "";
-}
-
-static inline const char *ansi_highlight_underline(void) {
- return colors_enabled() ? ANSI_HIGHLIGHT_UNDERLINE : "";
-}
-
-static inline const char *ansi_highlight_red(void) {
- return colors_enabled() ? ANSI_HIGHLIGHT_RED : "";
-}
-
-static inline const char *ansi_highlight_green(void) {
- return colors_enabled() ? ANSI_HIGHLIGHT_GREEN : "";
-}
-
-static inline const char *ansi_highlight_yellow(void) {
- return colors_enabled() ? ANSI_HIGHLIGHT_YELLOW : "";
-}
-
-static inline const char *ansi_highlight_blue(void) {
- return colors_enabled() ? ANSI_HIGHLIGHT_BLUE : "";
-}
-
-static inline const char *ansi_normal(void) {
- return colors_enabled() ? ANSI_NORMAL : "";
-}
+#define DEFINE_ANSI_FUNC(name, NAME) \
+ static inline const char *ansi_##name(void) { \
+ return colors_enabled() ? ANSI_##NAME : ""; \
+ } \
+ struct __useless_struct_to_allow_trailing_semicolon__
+
+DEFINE_ANSI_FUNC(underline, UNDERLINE);
+DEFINE_ANSI_FUNC(highlight, HIGHLIGHT);
+DEFINE_ANSI_FUNC(highlight_underline, HIGHLIGHT_UNDERLINE);
+DEFINE_ANSI_FUNC(highlight_red, HIGHLIGHT_RED);
+DEFINE_ANSI_FUNC(highlight_green, HIGHLIGHT_GREEN);
+DEFINE_ANSI_FUNC(highlight_yellow, HIGHLIGHT_YELLOW);
+DEFINE_ANSI_FUNC(highlight_blue, HIGHLIGHT_BLUE);
+DEFINE_ANSI_FUNC(highlight_red_underline, HIGHLIGHT_RED_UNDERLINE);
+DEFINE_ANSI_FUNC(highlight_green_underline, HIGHLIGHT_GREEN_UNDERLINE);
+DEFINE_ANSI_FUNC(highlight_yellow_underline, HIGHLIGHT_YELLOW_UNDERLINE);
+DEFINE_ANSI_FUNC(highlight_blue_underline, HIGHLIGHT_BLUE_UNDERLINE);
+DEFINE_ANSI_FUNC(normal, NORMAL);
int get_ctty_devnr(pid_t pid, dev_t *d);
int get_ctty(pid_t, dev_t *_devnr, char **r);
diff --git a/src/basic/time-util.c b/src/basic/time-util.c
index 24e681bf85..fedff1362c 100644
--- a/src/basic/time-util.c
+++ b/src/basic/time-util.c
@@ -40,8 +40,6 @@
#include "strv.h"
#include "time-util.h"
-static nsec_t timespec_load_nsec(const struct timespec *ts);
-
static clockid_t map_clock_id(clockid_t c) {
/* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus, clock_gettime() will
@@ -198,7 +196,7 @@ usec_t timespec_load(const struct timespec *ts) {
(usec_t) ts->tv_nsec / NSEC_PER_USEC;
}
-static nsec_t timespec_load_nsec(const struct timespec *ts) {
+nsec_t timespec_load_nsec(const struct timespec *ts) {
assert(ts);
if (ts->tv_sec == (time_t) -1 && ts->tv_nsec == (long) -1)
@@ -254,32 +252,95 @@ struct timeval *timeval_store(struct timeval *tv, usec_t u) {
return tv;
}
-static char *format_timestamp_internal(char *buf, size_t l, usec_t t,
- bool utc, bool us) {
+static char *format_timestamp_internal(
+ char *buf,
+ size_t l,
+ usec_t t,
+ bool utc,
+ bool us) {
+
+ /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that our
+ * generated timestamps may be parsed with parse_timestamp(), and always read the same. */
+ static const char * const weekdays[] = {
+ [0] = "Sun",
+ [1] = "Mon",
+ [2] = "Tue",
+ [3] = "Wed",
+ [4] = "Thu",
+ [5] = "Fri",
+ [6] = "Sat",
+ };
+
struct tm tm;
time_t sec;
- int k;
+ size_t n;
assert(buf);
- assert(l > 0);
+ if (l <
+ 3 + /* week day */
+ 1 + 10 + /* space and date */
+ 1 + 8 + /* space and time */
+ (us ? 1 + 6 : 0) + /* "." and microsecond part */
+ 1 + 1 + /* space and shortest possible zone */
+ 1)
+ return NULL; /* Not enough space even for the shortest form. */
if (t <= 0 || t == USEC_INFINITY)
+ return NULL; /* Timestamp is unset */
+
+ sec = (time_t) (t / USEC_PER_SEC); /* Round down */
+ if ((usec_t) sec != (t / USEC_PER_SEC))
+ return NULL; /* overflow? */
+
+ if (!localtime_or_gmtime_r(&sec, &tm, utc))
return NULL;
- sec = (time_t) (t / USEC_PER_SEC);
- localtime_or_gmtime_r(&sec, &tm, utc);
+ /* Start with the week day */
+ assert((size_t) tm.tm_wday < ELEMENTSOF(weekdays));
+ memcpy(buf, weekdays[tm.tm_wday], 4);
- if (us)
- k = strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm);
- else
- k = strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", &tm);
+ /* Add the main components */
+ if (strftime(buf + 3, l - 3, " %Y-%m-%d %H:%M:%S", &tm) <= 0)
+ return NULL; /* Doesn't fit */
- if (k <= 0)
- return NULL;
+ /* Append the microseconds part, if that's requested */
if (us) {
- snprintf(buf + strlen(buf), l - strlen(buf), ".%06llu", (unsigned long long) (t % USEC_PER_SEC));
- if (strftime(buf + strlen(buf), l - strlen(buf), " %Z", &tm) <= 0)
- return NULL;
+ n = strlen(buf);
+ if (n + 8 > l)
+ return NULL; /* Microseconds part doesn't fit. */
+
+ sprintf(buf + n, ".%06llu", (unsigned long long) (t % USEC_PER_SEC));
+ }
+
+ /* Append the timezone */
+ n = strlen(buf);
+ if (utc) {
+ /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r() normally uses the
+ * obsolete "GMT" instead. */
+ if (n + 5 > l)
+ return NULL; /* "UTC" doesn't fit. */
+
+ strcpy(buf + n, " UTC");
+
+ } else if (!isempty(tm.tm_zone)) {
+ size_t tn;
+
+ /* An explicit timezone is specified, let's use it, if it fits */
+ tn = strlen(tm.tm_zone);
+ if (n + 1 + tn + 1 > l) {
+ /* The full time zone does not fit in. Yuck. */
+
+ if (n + 1 + _POSIX_TZNAME_MAX + 1 > l)
+ return NULL; /* Not even enough space for the POSIX minimum (of 6)? In that case, complain that it doesn't fit */
+
+ /* So the time zone doesn't fit in fully, but the caller passed enough space for the POSIX
+ * minimum time zone length. In this case suppress the timezone entirely, in order not to dump
+ * an overly long, hard to read string on the user. This should be safe, because the user will
+ * assume the local timezone anyway if none is shown. And so does parse_timestamp(). */
+ } else {
+ buf[n++] = ' ';
+ strcpy(buf + n, tm.tm_zone);
+ }
}
return buf;
@@ -539,12 +600,11 @@ int parse_timestamp(const char *t, usec_t *usec) {
{ "Sat", 6 },
};
- const char *k;
- const char *utc;
+ const char *k, *utc, *tzn = NULL;
struct tm tm, copy;
time_t x;
usec_t x_usec, plus = 0, minus = 0, ret;
- int r, weekday = -1;
+ int r, weekday = -1, dst = -1;
unsigned i;
/*
@@ -609,15 +669,55 @@ int parse_timestamp(const char *t, usec_t *usec) {
goto finish;
}
+ /* See if the timestamp is suffixed with UTC */
utc = endswith_no_case(t, " UTC");
if (utc)
t = strndupa(t, utc - t);
+ else {
+ const char *e = NULL;
+ int j;
- x = ret / USEC_PER_SEC;
+ tzset();
+
+ /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only
+ * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because
+ * there are no nice APIs available to cover this. By accepting the local time zone strings, we make
+ * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't
+ * support arbitrary timezone specifications. */
+
+ for (j = 0; j <= 1; j++) {
+
+ if (isempty(tzname[j]))
+ continue;
+
+ e = endswith_no_case(t, tzname[j]);
+ if (!e)
+ continue;
+ if (e == t)
+ continue;
+ if (e[-1] != ' ')
+ continue;
+
+ break;
+ }
+
+ if (IN_SET(j, 0, 1)) {
+ /* Found one of the two timezones specified. */
+ t = strndupa(t, e - t - 1);
+ dst = j;
+ tzn = tzname[j];
+ }
+ }
+
+ x = (time_t) (ret / USEC_PER_SEC);
x_usec = 0;
- assert_se(localtime_or_gmtime_r(&x, &tm, utc));
- tm.tm_isdst = -1;
+ if (!localtime_or_gmtime_r(&x, &tm, utc))
+ return -EINVAL;
+
+ tm.tm_isdst = dst;
+ if (tzn)
+ tm.tm_zone = tzn;
if (streq(t, "today")) {
tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
@@ -634,7 +734,6 @@ int parse_timestamp(const char *t, usec_t *usec) {
goto from_tm;
}
-
for (i = 0; i < ELEMENTSOF(day_nr); i++) {
size_t skip;
@@ -727,7 +826,6 @@ parse_usec:
return -EINVAL;
x_usec = add;
-
}
from_tm:
diff --git a/src/basic/time-util.h b/src/basic/time-util.h
index 1b058f0e49..558b0b5b7f 100644
--- a/src/basic/time-util.h
+++ b/src/basic/time-util.h
@@ -68,7 +68,9 @@ typedef struct triple_timestamp {
#define USEC_PER_YEAR ((usec_t) (31557600ULL*USEC_PER_SEC))
#define NSEC_PER_YEAR ((nsec_t) (31557600ULL*NSEC_PER_SEC))
-#define FORMAT_TIMESTAMP_MAX ((4*4+1)+11+9+4+1) /* weekdays can be unicode */
+/* We assume a maximum timezone length of 6. TZNAME_MAX is not defined on Linux, but glibc internally initializes this
+ * to 6. Let's rely on that. */
+#define FORMAT_TIMESTAMP_MAX (3+1+10+1+8+1+6+1+6+1)
#define FORMAT_TIMESTAMP_WIDTH 28 /* when outputting, assume this width */
#define FORMAT_TIMESTAMP_RELATIVE_MAX 256
#define FORMAT_TIMESPAN_MAX 64
@@ -109,6 +111,7 @@ static inline bool triple_timestamp_is_set(triple_timestamp *ts) {
usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock);
usec_t timespec_load(const struct timespec *ts) _pure_;
+nsec_t timespec_load_nsec(const struct timespec *ts) _pure_;
struct timespec *timespec_store(struct timespec *ts, usec_t u);
usec_t timeval_load(const struct timeval *tv) _pure_;
diff --git a/src/basic/user-util.c b/src/basic/user-util.c
index e9d668ddfc..de6c93056e 100644
--- a/src/basic/user-util.c
+++ b/src/basic/user-util.c
@@ -29,16 +29,20 @@
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
+#include <utmp.h>
-#include "missing.h"
#include "alloc-util.h"
#include "fd-util.h"
+#include "fileio.h"
#include "formats-util.h"
#include "macro.h"
+#include "missing.h"
#include "parse-util.h"
#include "path-util.h"
#include "string-util.h"
+#include "strv.h"
#include "user-util.h"
+#include "utf8.h"
bool uid_is_valid(uid_t uid) {
@@ -173,6 +177,35 @@ int get_user_creds(
return 0;
}
+int get_user_creds_clean(
+ const char **username,
+ uid_t *uid, gid_t *gid,
+ const char **home,
+ const char **shell) {
+
+ int r;
+
+ /* Like get_user_creds(), but resets home/shell to NULL if they don't contain anything relevant. */
+
+ r = get_user_creds(username, uid, gid, home, shell);
+ if (r < 0)
+ return r;
+
+ if (shell &&
+ (isempty(*shell) || PATH_IN_SET(*shell,
+ "/bin/nologin",
+ "/sbin/nologin",
+ "/usr/bin/nologin",
+ "/usr/sbin/nologin")))
+ *shell = NULL;
+
+ if (home &&
+ (isempty(*home) || path_equal(*home, "/")))
+ *home = NULL;
+
+ return 0;
+}
+
int get_group_creds(const char **groupname, gid_t *gid) {
struct group *g;
gid_t id;
@@ -427,9 +460,11 @@ int get_shell(char **_s) {
}
int reset_uid_gid(void) {
+ int r;
- if (setgroups(0, NULL) < 0)
- return -errno;
+ r = maybe_setgroups(0, NULL);
+ if (r < 0)
+ return r;
if (setresgid(0, 0, 0) < 0)
return -errno;
@@ -479,3 +514,123 @@ int take_etc_passwd_lock(const char *root) {
return fd;
}
+
+bool valid_user_group_name(const char *u) {
+ const char *i;
+ long sz;
+
+ /* Checks if the specified name is a valid user/group name. */
+
+ if (isempty(u))
+ return false;
+
+ if (!(u[0] >= 'a' && u[0] <= 'z') &&
+ !(u[0] >= 'A' && u[0] <= 'Z') &&
+ u[0] != '_')
+ return false;
+
+ for (i = u+1; *i; i++) {
+ if (!(*i >= 'a' && *i <= 'z') &&
+ !(*i >= 'A' && *i <= 'Z') &&
+ !(*i >= '0' && *i <= '9') &&
+ *i != '_' &&
+ *i != '-')
+ return false;
+ }
+
+ sz = sysconf(_SC_LOGIN_NAME_MAX);
+ assert_se(sz > 0);
+
+ if ((size_t) (i-u) > (size_t) sz)
+ return false;
+
+ if ((size_t) (i-u) > UT_NAMESIZE - 1)
+ return false;
+
+ return true;
+}
+
+bool valid_user_group_name_or_id(const char *u) {
+
+ /* Similar as above, but is also fine with numeric UID/GID specifications, as long as they are in the right
+ * range, and not the invalid user ids. */
+
+ if (isempty(u))
+ return false;
+
+ if (valid_user_group_name(u))
+ return true;
+
+ return parse_uid(u, NULL) >= 0;
+}
+
+bool valid_gecos(const char *d) {
+
+ if (!d)
+ return false;
+
+ if (!utf8_is_valid(d))
+ return false;
+
+ if (string_has_cc(d, NULL))
+ return false;
+
+ /* Colons are used as field separators, and hence not OK */
+ if (strchr(d, ':'))
+ return false;
+
+ return true;
+}
+
+bool valid_home(const char *p) {
+
+ if (isempty(p))
+ return false;
+
+ if (!utf8_is_valid(p))
+ return false;
+
+ if (string_has_cc(p, NULL))
+ return false;
+
+ if (!path_is_absolute(p))
+ return false;
+
+ if (!path_is_safe(p))
+ return false;
+
+ /* Colons are used as field separators, and hence not OK */
+ if (strchr(p, ':'))
+ return false;
+
+ return true;
+}
+
+int maybe_setgroups(size_t size, const gid_t *list) {
+ int r;
+
+ /* Check if setgroups is allowed before we try to drop all the auxiliary groups */
+ if (size == 0) { /* Dropping all aux groups? */
+ _cleanup_free_ char *setgroups_content = NULL;
+ bool can_setgroups;
+
+ r = read_one_line_file("/proc/self/setgroups", &setgroups_content);
+ if (r == -ENOENT)
+ /* Old kernels don't have /proc/self/setgroups, so assume we can use setgroups */
+ can_setgroups = true;
+ else if (r < 0)
+ return r;
+ else
+ can_setgroups = streq(setgroups_content, "allow");
+
+ if (!can_setgroups) {
+ log_debug("Skipping setgroups(), /proc/self/setgroups is set to 'deny'");
+ return 0;
+ }
+ }
+
+ if (setgroups(size, list) < 0)
+ return -errno;
+
+ return 0;
+}
diff --git a/src/basic/user-util.h b/src/basic/user-util.h
index 8026eca3f4..dfea561bde 100644
--- a/src/basic/user-util.h
+++ b/src/basic/user-util.h
@@ -20,6 +20,7 @@
***/
#include <stdbool.h>
+#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
@@ -39,6 +40,7 @@ char* getlogname_malloc(void);
char* getusername_malloc(void);
int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell);
+int get_user_creds_clean(const char **username, uid_t *uid, gid_t *gid, const char **home, const char **shell);
int get_group_creds(const char **groupname, gid_t *gid);
char* uid_to_name(uid_t uid);
@@ -57,8 +59,19 @@ int take_etc_passwd_lock(const char *root);
#define UID_INVALID ((uid_t) -1)
#define GID_INVALID ((gid_t) -1)
-/* The following macros add 1 when converting things, since UID 0 is a
- * valid UID, while the pointer NULL is special */
+/* Let's pick a UIDs within the 16bit range, so that we are compatible with containers using 16bit
+ * user namespacing. At least on Fedora normal users are allocated until UID 60000, hence do not
+ * allocate from below this. Also stay away from the upper end of the range as that is often used
+ * for overflow/nobody users. */
+#define DYNAMIC_UID_MIN ((uid_t) UINT32_C(0x0000EF00))
+#define DYNAMIC_UID_MAX ((uid_t) UINT32_C(0x0000FFEF))
+
+static inline bool uid_is_dynamic(uid_t uid) {
+ return DYNAMIC_UID_MIN <= uid && uid <= DYNAMIC_UID_MAX;
+}
+
+/* The following macros add 1 when converting things, since UID 0 is a valid UID, while the pointer
+ * NULL is special */
#define PTR_TO_UID(p) ((uid_t) (((uintptr_t) (p))-1))
#define UID_TO_PTR(u) ((void*) (((uintptr_t) (u))+1))
@@ -68,3 +81,10 @@ int take_etc_passwd_lock(const char *root);
static inline bool userns_supported(void) {
return access("/proc/self/uid_map", F_OK) >= 0;
}
+
+bool valid_user_group_name(const char *u);
+bool valid_user_group_name_or_id(const char *u);
+bool valid_gecos(const char *d);
+bool valid_home(const char *p);
+
+int maybe_setgroups(size_t size, const gid_t *list);
diff --git a/src/basic/util.c b/src/basic/util.c
index 9d66d28eb7..ec7939dc83 100644
--- a/src/basic/util.c
+++ b/src/basic/util.c
@@ -467,7 +467,7 @@ bool in_initrd(void) {
* 2. the root file system must be a memory file system
*
* The second check is extra paranoia, since misdetecting an
- * initrd can have bad bad consequences due the initrd
+ * initrd can have bad consequences due the initrd
* emptying when transititioning to the main systemd.
*/
diff --git a/src/basic/util.h b/src/basic/util.h
index 44497dcd78..bb2fc318ef 100644
--- a/src/basic/util.h
+++ b/src/basic/util.h
@@ -61,6 +61,10 @@ static inline const char* one_zero(bool b) {
return b ? "1" : "0";
}
+static inline const char* enable_disable(bool b) {
+ return b ? "enable" : "disable";
+}
+
void execute_directories(const char* const* directories, usec_t timeout, char *argv[]);
bool plymouth_running(void);
diff --git a/src/basic/virt.c b/src/basic/virt.c
index dace1f4328..d8d57381ad 100644
--- a/src/basic/virt.c
+++ b/src/basic/virt.c
@@ -33,6 +33,7 @@
#include "string-table.h"
#include "string-util.h"
#include "virt.h"
+#include "env-util.h"
static int detect_vm_cpuid(void) {
@@ -49,6 +50,8 @@ static int detect_vm_cpuid(void) {
{ "VMwareVMware", VIRTUALIZATION_VMWARE },
/* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
{ "Microsoft Hv", VIRTUALIZATION_MICROSOFT },
+ /* https://wiki.freebsd.org/bhyve */
+ { "bhyve bhyve ", VIRTUALIZATION_BHYVE },
};
uint32_t eax, ecx;
@@ -178,6 +181,8 @@ static int detect_vm_dmi(void) {
{ "Xen", VIRTUALIZATION_XEN },
{ "Bochs", VIRTUALIZATION_BOCHS },
{ "Parallels", VIRTUALIZATION_PARALLELS },
+ /* https://wiki.freebsd.org/bhyve */
+ { "BHYVE", VIRTUALIZATION_BHYVE },
};
unsigned i;
int r;
@@ -480,9 +485,82 @@ int detect_virtualization(void) {
return r;
}
+static int userns_has_mapping(const char *name) {
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_free_ char *buf = NULL;
+ size_t n_allocated = 0;
+ ssize_t n;
+ uint32_t a, b, c;
+ int r;
+
+ f = fopen(name, "re");
+ if (!f) {
+ log_debug_errno(errno, "Failed to open %s: %m", name);
+ return errno == ENOENT ? false : -errno;
+ }
+
+ n = getline(&buf, &n_allocated, f);
+ if (n < 0) {
+ if (feof(f)) {
+ log_debug("%s is empty, we're in an uninitialized user namespace", name);
+ return true;
+ }
+
+ return log_debug_errno(errno, "Failed to read %s: %m", name);
+ }
+
+ r = sscanf(buf, "%"PRIu32" %"PRIu32" %"PRIu32, &a, &b, &c);
+ if (r < 3)
+ return log_debug_errno(errno, "Failed to parse %s: %m", name);
+
+ if (a == 0 && b == 0 && c == UINT32_MAX) {
+ /* The kernel calls mappings_overlap() and does not allow overlaps */
+ log_debug("%s has a full 1:1 mapping", name);
+ return false;
+ }
+
+ /* Anything else implies that we are in a user namespace */
+ log_debug("Mapping found in %s, we're in a user namespace", name);
+ return true;
+}
+
+int running_in_userns(void) {
+ _cleanup_free_ char *line = NULL;
+ int r;
+
+ r = userns_has_mapping("/proc/self/uid_map");
+ if (r != 0)
+ return r;
+
+ r = userns_has_mapping("/proc/self/gid_map");
+ if (r != 0)
+ return r;
+
+ /* "setgroups" file was added in kernel v3.18-rc6-15-g9cc46516dd. It is also
+ * possible to compile a kernel without CONFIG_USER_NS, in which case "setgroups"
+ * also does not exist. We cannot distinguish those two cases, so assume that
+ * we're running on a stripped-down recent kernel, rather than on an old one,
+ * and if the file is not found, return false.
+ */
+ r = read_one_line_file("/proc/self/setgroups", &line);
+ if (r < 0) {
+ log_debug_errno(r, "/proc/self/setgroups: %m");
+ return r == -ENOENT ? false : r;
+ }
+
+ truncate_nl(line);
+ r = streq(line, "deny");
+ /* See user_namespaces(7) for a description of this "setgroups" contents. */
+ log_debug("/proc/self/setgroups contains \"%s\", %s user namespace", line, r ? "in" : "not in");
+ return r;
+}
+
int running_in_chroot(void) {
int ret;
+ if (getenv_bool("SYSTEMD_IGNORE_CHROOT") > 0)
+ return 0;
+
ret = files_same("/proc/1/root", "/");
if (ret < 0)
return ret;
@@ -502,6 +580,7 @@ static const char *const virtualization_table[_VIRTUALIZATION_MAX] = {
[VIRTUALIZATION_MICROSOFT] = "microsoft",
[VIRTUALIZATION_ZVM] = "zvm",
[VIRTUALIZATION_PARALLELS] = "parallels",
+ [VIRTUALIZATION_BHYVE] = "bhyve",
[VIRTUALIZATION_VM_OTHER] = "vm-other",
[VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn",
diff --git a/src/basic/virt.h b/src/basic/virt.h
index a538f07f6b..7d15169112 100644
--- a/src/basic/virt.h
+++ b/src/basic/virt.h
@@ -37,6 +37,7 @@ enum {
VIRTUALIZATION_MICROSOFT,
VIRTUALIZATION_ZVM,
VIRTUALIZATION_PARALLELS,
+ VIRTUALIZATION_BHYVE,
VIRTUALIZATION_VM_OTHER,
VIRTUALIZATION_VM_LAST = VIRTUALIZATION_VM_OTHER,
@@ -66,6 +67,7 @@ int detect_vm(void);
int detect_container(void);
int detect_virtualization(void);
+int running_in_userns(void);
int running_in_chroot(void);
const char *virtualization_to_string(int v) _const_;