summaryrefslogtreecommitdiff
path: root/src/basic
diff options
context:
space:
mode:
authorMartin Pitt <mpitt@debian.org>2017-03-02 10:44:39 +0100
committerMartin Pitt <mpitt@debian.org>2017-03-02 10:44:39 +0100
commit2897b343851c95927e26f45bea8c40da605dbed1 (patch)
treec15ec2f4b562d39a818acc5d65ae58944791dba9 /src/basic
parent8a584da2774aca0b14c8aacef574e93d943d470e (diff)
downloadsystemd-2897b343851c95927e26f45bea8c40da605dbed1.tar.gz
New upstream version 233
Diffstat (limited to 'src/basic')
-rw-r--r--src/basic/MurmurHash2.c6
-rw-r--r--src/basic/af-list.c2
-rw-r--r--src/basic/architecture.c3
-rw-r--r--src/basic/architecture.h20
-rw-r--r--src/basic/arphrd-list.c2
-rw-r--r--src/basic/btrfs-util.c43
-rw-r--r--src/basic/btrfs-util.h4
-rw-r--r--src/basic/build.h5
-rw-r--r--src/basic/calendarspec.c336
-rw-r--r--src/basic/calendarspec.h4
-rw-r--r--src/basic/cap-list.c2
-rw-r--r--src/basic/cgroup-util.c424
-rw-r--r--src/basic/cgroup-util.h8
-rw-r--r--src/basic/conf-files.c7
-rw-r--r--src/basic/copy.c133
-rw-r--r--src/basic/copy.h22
-rw-r--r--src/basic/def.h18
-rw-r--r--src/basic/dirent-util.c3
-rw-r--r--src/basic/env-util.c227
-rw-r--r--src/basic/env-util.h19
-rw-r--r--src/basic/errno-list.c2
-rw-r--r--src/basic/exec-util.c360
-rw-r--r--src/basic/exec-util.h40
-rw-r--r--src/basic/exit-status.c3
-rw-r--r--src/basic/exit-status.h1
-rw-r--r--src/basic/extract-word.c6
-rw-r--r--src/basic/fd-util.c6
-rw-r--r--src/basic/fileio.c170
-rw-r--r--src/basic/fileio.h5
-rw-r--r--src/basic/format-util.h (renamed from src/basic/formats-util.h)0
-rw-r--r--src/basic/fs-util.c131
-rw-r--r--src/basic/fs-util.h22
-rw-r--r--src/basic/hexdecoct.c17
-rw-r--r--src/basic/hostname-util.c2
-rw-r--r--src/basic/in-addr-util.c55
-rw-r--r--src/basic/in-addr-util.h9
-rw-r--r--src/basic/journal-importer.c481
-rw-r--r--src/basic/journal-importer.h70
-rw-r--r--src/basic/khash.c275
-rw-r--r--src/basic/khash.h53
-rw-r--r--src/basic/log.c119
-rw-r--r--src/basic/log.h4
-rw-r--r--src/basic/missing.h107
-rw-r--r--src/basic/missing_syscall.h2
-rw-r--r--src/basic/mount-util.c60
-rw-r--r--src/basic/mount-util.h5
-rw-r--r--src/basic/nss-util.h6
-rw-r--r--src/basic/parse-util.c16
-rw-r--r--src/basic/parse-util.h2
-rw-r--r--src/basic/path-util.c82
-rw-r--r--src/basic/path-util.h19
-rw-r--r--src/basic/proc-cmdline.c147
-rw-r--r--src/basic/proc-cmdline.h33
-rw-r--r--src/basic/process-util.c164
-rw-r--r--src/basic/process-util.h4
-rw-r--r--src/basic/raw-clone.h4
-rw-r--r--src/basic/rlimit-util.c2
-rw-r--r--src/basic/rm-rf.c17
-rw-r--r--src/basic/rm-rf.h2
-rw-r--r--src/basic/siphash24.c7
-rw-r--r--src/basic/socket-util.c101
-rw-r--r--src/basic/socket-util.h5
-rw-r--r--src/basic/sparse-endian.h47
-rw-r--r--src/basic/special.h1
-rw-r--r--src/basic/stat-util.c40
-rw-r--r--src/basic/stat-util.h1
-rw-r--r--src/basic/string-util.c13
-rw-r--r--src/basic/string-util.h11
-rw-r--r--src/basic/terminal-util.c19
-rw-r--r--src/basic/time-util.c44
-rw-r--r--src/basic/time-util.h17
-rw-r--r--src/basic/unit-name.c4
-rw-r--r--src/basic/user-util.c14
-rw-r--r--src/basic/util.c163
-rw-r--r--src/basic/util.h4
-rw-r--r--src/basic/virt.c109
76 files changed, 3355 insertions, 1036 deletions
diff --git a/src/basic/MurmurHash2.c b/src/basic/MurmurHash2.c
index 9020793930..a282a21201 100644
--- a/src/basic/MurmurHash2.c
+++ b/src/basic/MurmurHash2.c
@@ -69,9 +69,9 @@ uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed )
switch(len)
{
- case 3: h ^= data[2] << 16;
- case 2: h ^= data[1] << 8;
- case 1: h ^= data[0];
+ case 3: h ^= data[2] << 16; /* fall through */
+ case 2: h ^= data[1] << 8; /* fall through */
+ case 1: h ^= data[0]; /* fall through */
h *= m;
};
diff --git a/src/basic/af-list.c b/src/basic/af-list.c
index 3fac9c508b..4b291d177b 100644
--- a/src/basic/af-list.c
+++ b/src/basic/af-list.c
@@ -23,7 +23,7 @@
#include "af-list.h"
#include "macro.h"
-static const struct af_name* lookup_af(register const char *str, register unsigned int len);
+static const struct af_name* lookup_af(register const char *str, register GPERF_LEN_TYPE len);
#include "af-from-name.h"
#include "af-to-name.h"
diff --git a/src/basic/architecture.c b/src/basic/architecture.c
index b74dc0db78..5a3dc08a4a 100644
--- a/src/basic/architecture.c
+++ b/src/basic/architecture.c
@@ -123,7 +123,8 @@ int uname_architecture(void) {
{ "crisv32", ARCHITECTURE_CRIS },
#elif defined(__nios2__)
{ "nios2", ARCHITECTURE_NIOS2 },
-#elif defined(__riscv__)
+#elif defined(__riscv__) || defined(__riscv)
+ /* __riscv__ is obsolete, remove in 2018 */
{ "riscv32", ARCHITECTURE_RISCV32 },
{ "riscv64", ARCHITECTURE_RISCV64 },
# if __SIZEOF_POINTER__ == 4
diff --git a/src/basic/architecture.h b/src/basic/architecture.h
index 5a77c31932..46883719d1 100644
--- a/src/basic/architecture.h
+++ b/src/basic/architecture.h
@@ -124,13 +124,21 @@ int uname_architecture(void);
#elif defined(__sparc__)
# define native_architecture() ARCHITECTURE_SPARC
# define LIB_ARCH_TUPLE "sparc-linux-gnu"
-#elif defined(__mips64__)
+#elif defined(__mips64) && defined(__LP64__)
# if __BYTE_ORDER == __BIG_ENDIAN
# define native_architecture() ARCHITECTURE_MIPS64
-# error "Missing LIB_ARCH_TUPLE for MIPS64"
+# define LIB_ARCH_TUPLE "mips64-linux-gnuabi64"
# else
# define native_architecture() ARCHITECTURE_MIPS64_LE
-# error "Missing LIB_ARCH_TUPLE for MIPS64_LE"
+# define LIB_ARCH_TUPLE "mips64el-linux-gnuabi64"
+# endif
+#elif defined(__mips64)
+# if __BYTE_ORDER == __BIG_ENDIAN
+# define native_architecture() ARCHITECTURE_MIPS64
+# define LIB_ARCH_TUPLE "mips64-linux-gnuabin32"
+# else
+# define native_architecture() ARCHITECTURE_MIPS64_LE
+# define LIB_ARCH_TUPLE "mips64el-linux-gnuabin32"
# endif
#elif defined(__mips__)
# if __BYTE_ORDER == __BIG_ENDIAN
@@ -150,6 +158,7 @@ int uname_architecture(void);
# else
# define native_architecture() ARCHITECTURE_ARM64
# define LIB_ARCH_TUPLE "aarch64-linux-gnu"
+# define SECONDARY_ARCHITECTURE ARCHITECTURE_ARM
# endif
#elif defined(__arm__)
# if __BYTE_ORDER == __BIG_ENDIAN
@@ -186,14 +195,15 @@ int uname_architecture(void);
# define LIB_ARCH_TUPLE "m68k-linux-gnu"
#elif defined(__tilegx__)
# define native_architecture() ARCHITECTURE_TILEGX
-# error "Missing LIB_ARCH_TUPLE for TILEGX"
+# define LIB_ARCH_TUPLE "tilegx-linux-gnu"
#elif defined(__cris__)
# define native_architecture() ARCHITECTURE_CRIS
# error "Missing LIB_ARCH_TUPLE for CRIS"
#elif defined(__nios2__)
# define native_architecture() ARCHITECTURE_NIOS2
# define LIB_ARCH_TUPLE "nios2-linux-gnu"
-#elif defined(__riscv__)
+#elif defined(__riscv__) || defined(__riscv)
+ /* __riscv__ is obsolete, remove in 2018 */
# if __SIZEOF_POINTER__ == 4
# define native_architecture() ARCHITECTURE_RISCV32
# define LIB_ARCH_TUPLE "riscv32-linux-gnu"
diff --git a/src/basic/arphrd-list.c b/src/basic/arphrd-list.c
index 6792d1ee3f..2d598dc66f 100644
--- a/src/basic/arphrd-list.c
+++ b/src/basic/arphrd-list.c
@@ -23,7 +23,7 @@
#include "arphrd-list.h"
#include "macro.h"
-static const struct arphrd_name* lookup_arphrd(register const char *str, register unsigned int len);
+static const struct arphrd_name* lookup_arphrd(register const char *str, register GPERF_LEN_TYPE len);
#include "arphrd-from-name.h"
#include "arphrd-to-name.h"
diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c
index 359d85f2e8..5505499312 100644
--- a/src/basic/btrfs-util.c
+++ b/src/basic/btrfs-util.c
@@ -20,6 +20,7 @@
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
+#include <linux/fs.h>
#include <linux/loop.h>
#include <stddef.h>
#include <stdio.h>
@@ -38,6 +39,7 @@
#include "alloc-util.h"
#include "btrfs-ctree.h"
#include "btrfs-util.h"
+#include "chattr-util.h"
#include "copy.h"
#include "fd-util.h"
#include "fileio.h"
@@ -45,6 +47,7 @@
#include "macro.h"
#include "missing.h"
#include "path-util.h"
+#include "rm-rf.h"
#include "selinux-util.h"
#include "smack-util.h"
#include "sparse-endian.h"
@@ -1642,7 +1645,7 @@ static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolum
if (old_child_fd < 0)
return -errno;
- np = strjoin(subvolume, "/", ino_args.name, NULL);
+ np = strjoin(subvolume, "/", ino_args.name);
if (!np)
return -ENOMEM;
@@ -1718,28 +1721,46 @@ int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlag
if (r < 0)
return r;
if (r == 0) {
+ bool plain_directory = false;
+
+ /* If the source isn't a proper subvolume, fail unless fallback is requested */
if (!(flags & BTRFS_SNAPSHOT_FALLBACK_COPY))
return -EISDIR;
r = btrfs_subvol_make(new_path);
- if (r < 0)
- return r;
+ if (r == -ENOTTY && (flags & BTRFS_SNAPSHOT_FALLBACK_DIRECTORY)) {
+ /* If the destination doesn't support subvolumes, then use a plain directory, if that's requested. */
+ if (mkdir(new_path, 0755) < 0)
+ return r;
- r = copy_directory_fd(old_fd, new_path, true);
- if (r < 0) {
- (void) btrfs_subvol_remove(new_path, BTRFS_REMOVE_QUOTA);
+ plain_directory = true;
+ } else if (r < 0)
return r;
- }
+
+ r = copy_directory_fd(old_fd, new_path, COPY_MERGE|COPY_REFLINK);
+ if (r < 0)
+ goto fallback_fail;
if (flags & BTRFS_SNAPSHOT_READ_ONLY) {
- r = btrfs_subvol_set_read_only(new_path, true);
- if (r < 0) {
- (void) btrfs_subvol_remove(new_path, BTRFS_REMOVE_QUOTA);
- return r;
+
+ if (plain_directory) {
+ /* Plain directories have no recursive read-only flag, but something pretty close to
+ * it: the IMMUTABLE bit. Let's use this here, if this is requested. */
+
+ if (flags & BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE)
+ (void) chattr_path(new_path, FS_IMMUTABLE_FL, FS_IMMUTABLE_FL);
+ } else {
+ r = btrfs_subvol_set_read_only(new_path, true);
+ if (r < 0)
+ goto fallback_fail;
}
}
return 0;
+
+ fallback_fail:
+ (void) rm_rf(new_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
+ return r;
}
r = extract_subvolume_name(new_path, &subvolume);
diff --git a/src/basic/btrfs-util.h b/src/basic/btrfs-util.h
index 1d852d502c..04a2e1274b 100644
--- a/src/basic/btrfs-util.h
+++ b/src/basic/btrfs-util.h
@@ -45,10 +45,12 @@ typedef struct BtrfsQuotaInfo {
} BtrfsQuotaInfo;
typedef enum BtrfsSnapshotFlags {
- BTRFS_SNAPSHOT_FALLBACK_COPY = 1,
+ BTRFS_SNAPSHOT_FALLBACK_COPY = 1, /* If the source isn't a subvolume, reflink everything */
BTRFS_SNAPSHOT_READ_ONLY = 2,
BTRFS_SNAPSHOT_RECURSIVE = 4,
BTRFS_SNAPSHOT_QUOTA = 8,
+ BTRFS_SNAPSHOT_FALLBACK_DIRECTORY = 16, /* If the destination doesn't support subvolumes, reflink/copy instead */
+ BTRFS_SNAPSHOT_FALLBACK_IMMUTABLE = 32, /* When we can't create a subvolume, use the FS_IMMUTABLE attribute for indicating read-only */
} BtrfsSnapshotFlags;
typedef enum BtrfsRemoveFlags {
diff --git a/src/basic/build.h b/src/basic/build.h
index 633c2aaccb..91312bd2a3 100644
--- a/src/basic/build.h
+++ b/src/basic/build.h
@@ -133,6 +133,8 @@
#define _IDN_FEATURE_ "-IDN"
#endif
+#define _CGROUP_HIEARCHY_ "default-hierarchy=" DEFAULT_HIERARCHY_NAME
+
#define SYSTEMD_FEATURES \
_PAM_FEATURE_ " " \
_AUDIT_FEATURE_ " " \
@@ -152,4 +154,5 @@
_BLKID_FEATURE_ " " \
_ELFUTILS_FEATURE_ " " \
_KMOD_FEATURE_ " " \
- _IDN_FEATURE_
+ _IDN_FEATURE_ " " \
+ _CGROUP_HIEARCHY_
diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c
index fda293fcb9..2323eb8555 100644
--- a/src/basic/calendarspec.c
+++ b/src/basic/calendarspec.c
@@ -18,7 +18,9 @@
***/
#include <alloca.h>
+#include <ctype.h>
#include <errno.h>
+#include <limits.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
@@ -32,9 +34,9 @@
#include "parse-util.h"
#include "string-util.h"
-/* Longest valid date/time range is 1970..2199 */
-#define MAX_RANGE_LEN 230
-#define BITS_WEEKDAYS 127
+#define BITS_WEEKDAYS 127
+#define MIN_YEAR 1970
+#define MAX_YEAR 2199
static void free_chain(CalendarComponent *c) {
CalendarComponent *n;
@@ -64,9 +66,14 @@ void calendar_spec_free(CalendarSpec *c) {
static int component_compare(const void *_a, const void *_b) {
CalendarComponent * const *a = _a, * const *b = _b;
- if ((*a)->value < (*b)->value)
+ if ((*a)->start < (*b)->start)
return -1;
- if ((*a)->value > (*b)->value)
+ if ((*a)->start > (*b)->start)
+ return 1;
+
+ if ((*a)->stop < (*b)->stop)
+ return -1;
+ if ((*a)->stop > (*b)->stop)
return 1;
if ((*a)->repeat < (*b)->repeat)
@@ -77,15 +84,24 @@ static int component_compare(const void *_a, const void *_b) {
return 0;
}
-static void sort_chain(CalendarComponent **c) {
+static void normalize_chain(CalendarComponent **c) {
unsigned n = 0, k;
CalendarComponent **b, *i, **j, *next;
assert(c);
- for (i = *c; i; i = i->next)
+ for (i = *c; i; i = i->next) {
n++;
+ /*
+ * While we're counting the chain, also normalize `stop`
+ * so the length of the range is a multiple of `repeat`
+ */
+ if (i->stop > i->start && i->repeat > 0)
+ i->stop -= (i->stop - i->start) % i->repeat;
+
+ }
+
if (n <= 1)
return;
@@ -100,8 +116,7 @@ static void sort_chain(CalendarComponent **c) {
/* Drop non-unique entries */
for (k = n-1; k > 0; k--) {
- if (b[k-1]->value == next->value &&
- b[k-1]->repeat == next->repeat) {
+ if (component_compare(&b[k-1], &next) == 0) {
free(b[k-1]);
continue;
}
@@ -117,15 +132,19 @@ static void fix_year(CalendarComponent *c) {
/* Turns 12 → 2012, 89 → 1989 */
while (c) {
- CalendarComponent *n = c->next;
+ if (c->start >= 0 && c->start < 70)
+ c->start += 2000;
- if (c->value >= 0 && c->value < 70)
- c->value += 2000;
+ if (c->stop >= 0 && c->stop < 70)
+ c->stop += 2000;
- if (c->value >= 70 && c->value < 100)
- c->value += 1900;
+ if (c->start >= 70 && c->start < 100)
+ c->start += 1900;
- c = n;
+ if (c->stop >= 70 && c->stop < 100)
+ c->stop += 1900;
+
+ c = c->next;
}
}
@@ -135,30 +154,54 @@ int calendar_spec_normalize(CalendarSpec *c) {
if (c->weekdays_bits <= 0 || c->weekdays_bits >= BITS_WEEKDAYS)
c->weekdays_bits = -1;
+ if (c->end_of_month && !c->day)
+ c->end_of_month = false;
+
fix_year(c->year);
- sort_chain(&c->year);
- sort_chain(&c->month);
- sort_chain(&c->day);
- sort_chain(&c->hour);
- sort_chain(&c->minute);
- sort_chain(&c->microsecond);
+ normalize_chain(&c->year);
+ normalize_chain(&c->month);
+ normalize_chain(&c->day);
+ normalize_chain(&c->hour);
+ normalize_chain(&c->minute);
+ normalize_chain(&c->microsecond);
return 0;
}
-_pure_ static bool chain_valid(CalendarComponent *c, int from, int to) {
+_pure_ static bool chain_valid(CalendarComponent *c, int from, int to, bool end_of_month) {
if (!c)
return true;
- if (c->value < from || c->value > to)
- return false;
+ /* Forbid dates more than 28 days from the end of the month */
+ if (end_of_month)
+ to -= 3;
- if (c->value + c->repeat > to)
+ if (c->start < from || c->start > to)
return false;
+ /*
+ * c->repeat must be short enough so at least one repetition may
+ * occur before the end of the interval. For dates scheduled
+ * relative to the end of the month, c->start and c->stop
+ * correspond to the Nth last day of the month.
+ */
+ if (c->stop >= 0) {
+ if (c->stop < from || c ->stop > to)
+ return false;
+
+ if (c->start + c->repeat > c->stop)
+ return false;
+ } else {
+ if (end_of_month && c->start - c->repeat < from)
+ return false;
+
+ if (!end_of_month && c->start + c->repeat > to)
+ return false;
+ }
+
if (c->next)
- return chain_valid(c->next, from, to);
+ return chain_valid(c->next, from, to, end_of_month);
return true;
}
@@ -169,22 +212,22 @@ _pure_ bool calendar_spec_valid(CalendarSpec *c) {
if (c->weekdays_bits > BITS_WEEKDAYS)
return false;
- if (!chain_valid(c->year, 1970, 2199))
+ if (!chain_valid(c->year, MIN_YEAR, MAX_YEAR, false))
return false;
- if (!chain_valid(c->month, 1, 12))
+ if (!chain_valid(c->month, 1, 12, false))
return false;
- if (!chain_valid(c->day, 1, 31))
+ if (!chain_valid(c->day, 1, 31, c->end_of_month))
return false;
- if (!chain_valid(c->hour, 0, 23))
+ if (!chain_valid(c->hour, 0, 23, false))
return false;
- if (!chain_valid(c->minute, 0, 59))
+ if (!chain_valid(c->minute, 0, 59, false))
return false;
- if (!chain_valid(c->microsecond, 0, 60*USEC_PER_SEC-1))
+ if (!chain_valid(c->microsecond, 0, 60*USEC_PER_SEC-1, false))
return false;
return true;
@@ -240,6 +283,8 @@ static void format_weekdays(FILE *f, const CalendarSpec *c) {
}
static void format_chain(FILE *f, int space, const CalendarComponent *c, bool usec) {
+ int d = usec ? (int) USEC_PER_SEC : 1;
+
assert(f);
if (!c) {
@@ -247,23 +292,27 @@ static void format_chain(FILE *f, int space, const CalendarComponent *c, bool us
return;
}
- assert(c->value >= 0);
- if (!usec)
- fprintf(f, "%0*i", space, c->value);
- else if (c->value % USEC_PER_SEC == 0)
- fprintf(f, "%0*i", space, (int) (c->value / USEC_PER_SEC));
- else
- fprintf(f, "%0*i.%06i", space, (int) (c->value / USEC_PER_SEC), (int) (c->value % USEC_PER_SEC));
-
- if (c->repeat > 0) {
- if (!usec)
- fprintf(f, "/%i", c->repeat);
- else if (c->repeat % USEC_PER_SEC == 0)
- fprintf(f, "/%i", (int) (c->repeat / USEC_PER_SEC));
- else
- fprintf(f, "/%i.%06i", (int) (c->repeat / USEC_PER_SEC), (int) (c->repeat % USEC_PER_SEC));
+ if (usec && c->start == 0 && c->repeat == USEC_PER_SEC && !c->next) {
+ fputc('*', f);
+ return;
}
+ assert(c->start >= 0);
+
+ fprintf(f, "%0*i", space, c->start / d);
+ if (c->start % d > 0)
+ fprintf(f, ".%06i", c->start % d);
+
+ if (c->stop > 0)
+ fprintf(f, "..%0*i", space, c->stop / d);
+ if (c->stop % d > 0)
+ fprintf(f, ".%06i", c->stop % d);
+
+ if (c->repeat > 0 && !(c->stop > 0 && c->repeat == d))
+ fprintf(f, "/%i", c->repeat / d);
+ if (c->repeat % d > 0)
+ fprintf(f, ".%06i", c->repeat % d);
+
if (c->next) {
fputc(',', f);
format_chain(f, space, c->next, usec);
@@ -291,7 +340,7 @@ int calendar_spec_to_string(const CalendarSpec *c, char **p) {
format_chain(f, 4, c->year, false);
fputc('-', f);
format_chain(f, 2, c->month, false);
- fputc('-', f);
+ fputc(c->end_of_month ? '~' : '-', f);
format_chain(f, 2, c->day, false);
fputc(' ', f);
format_chain(f, 2, c->hour, false);
@@ -358,9 +407,6 @@ static int parse_weekdays(const char **p, CalendarSpec *c) {
for (;;) {
unsigned i;
- if (!first && **p == ' ')
- return 0;
-
for (i = 0; i < ELEMENTSOF(day_nr); i++) {
size_t skip;
@@ -416,7 +462,7 @@ static int parse_weekdays(const char **p, CalendarSpec *c) {
return -EINVAL;
l = day_nr[i].nr;
- *p += 1;
+ *p += 2;
/* Support ranges with "-" for backwards compatibility */
} else if (**p == '-') {
@@ -424,28 +470,38 @@ static int parse_weekdays(const char **p, CalendarSpec *c) {
return -EINVAL;
l = day_nr[i].nr;
- } else
+ *p += 1;
+
+ } else if (**p == ',') {
l = -1;
+ *p += 1;
+ }
+
+ /* Allow a trailing comma but not an open range */
+ if (**p == 0 || **p == ' ') {
+ *p += strspn(*p, " ");
+ return l < 0 ? 0 : -EINVAL;
+ }
- *p += 1;
first = false;
}
}
-static int parse_component_decimal(const char **p, bool usec, unsigned long *res) {
+static int parse_component_decimal(const char **p, bool usec, int *res) {
unsigned long value;
const char *e = NULL;
char *ee = NULL;
int r;
+ if (!isdigit(**p))
+ return -EINVAL;
+
errno = 0;
value = strtoul(*p, &ee, 10);
if (errno > 0)
return -errno;
if (ee == *p)
return -EINVAL;
- if ((unsigned long) (int) value != value)
- return -ERANGE;
e = ee;
if (usec) {
@@ -453,7 +509,9 @@ static int parse_component_decimal(const char **p, bool usec, unsigned long *res
return -ERANGE;
value *= USEC_PER_SEC;
- if (*e == '.') {
+
+ /* One "." is a decimal point, but ".." is a range separator */
+ if (e[0] == '.' && e[1] != '.') {
unsigned add;
e++;
@@ -467,6 +525,9 @@ static int parse_component_decimal(const char **p, bool usec, unsigned long *res
}
}
+ if (value > INT_MAX)
+ return -ERANGE;
+
*p = e;
*res = value;
@@ -482,7 +543,8 @@ static int const_chain(int value, CalendarComponent **c) {
if (!cc)
return -ENOMEM;
- cc->value = value;
+ cc->start = value;
+ cc->stop = -1;
cc->repeat = 0;
cc->next = *c;
@@ -492,9 +554,8 @@ static int const_chain(int value, CalendarComponent **c) {
}
static int prepend_component(const char **p, bool usec, CalendarComponent **c) {
- unsigned long i, value, range_end, range_inc, repeat = 0;
+ int r, start, stop = -1, repeat = 0;
CalendarComponent *cc;
- int r;
const char *e;
assert(p);
@@ -502,10 +563,19 @@ static int prepend_component(const char **p, bool usec, CalendarComponent **c) {
e = *p;
- r = parse_component_decimal(&e, usec, &value);
+ r = parse_component_decimal(&e, usec, &start);
if (r < 0)
return r;
+ if (e[0] == '.' && e[1] == '.') {
+ e += 2;
+ r = parse_component_decimal(&e, usec, &stop);
+ if (r < 0)
+ return r;
+
+ repeat = usec ? USEC_PER_SEC : 1;
+ }
+
if (*e == '/') {
e++;
r = parse_component_decimal(&e, usec, &repeat);
@@ -514,40 +584,17 @@ static int prepend_component(const char **p, bool usec, CalendarComponent **c) {
if (repeat == 0)
return -ERANGE;
- } else if (e[0] == '.' && e[1] == '.') {
- e += 2;
- r = parse_component_decimal(&e, usec, &range_end);
- if (r < 0)
- return r;
-
- if (value >= range_end)
- return -EINVAL;
-
- range_inc = usec ? USEC_PER_SEC : 1;
-
- /* Don't allow impossibly large ranges... */
- if (range_end - value >= MAX_RANGE_LEN * range_inc)
- return -EINVAL;
-
- /* ...or ranges with only a single element */
- if (range_end - value < range_inc)
- return -EINVAL;
-
- for (i = value; i <= range_end; i += range_inc) {
- r = const_chain(i, c);
- if (r < 0)
- return r;
- }
}
- if (*e != 0 && *e != ' ' && *e != ',' && *e != '-' && *e != ':')
+ if (*e != 0 && *e != ' ' && *e != ',' && *e != '-' && *e != '~' && *e != ':')
return -EINVAL;
cc = new0(CalendarComponent, 1);
if (!cc)
return -ENOMEM;
- cc->value = value;
+ cc->start = start;
+ cc->stop = stop;
cc->repeat = repeat;
cc->next = *c;
@@ -620,7 +667,9 @@ static int parse_date(const char **p, CalendarSpec *c) {
return 0;
}
- if (*t != '-') {
+ if (*t == '~')
+ c->end_of_month = true;
+ else if (*t != '-') {
free_chain(first);
return -EINVAL;
}
@@ -638,9 +687,15 @@ static int parse_date(const char **p, CalendarSpec *c) {
c->month = first;
c->day = second;
return 0;
+ } else if (c->end_of_month) {
+ free_chain(first);
+ free_chain(second);
+ return -EINVAL;
}
- if (*t != '-') {
+ if (*t == '~')
+ c->end_of_month = true;
+ else if (*t != '-') {
free_chain(first);
free_chain(second);
return -EINVAL;
@@ -654,7 +709,7 @@ static int parse_date(const char **p, CalendarSpec *c) {
return r;
}
- /* Got tree parts, hence it is year, month and day */
+ /* Got three parts, hence it is year, month and day */
if (*t == ' ' || *t == 0) {
*p = t + strspn(t, " ");
c->year = first;
@@ -680,14 +735,9 @@ static int parse_calendar_time(const char **p, CalendarSpec *c) {
t = *p;
- if (*t == 0) {
- /* If no time is specified at all, but a date of some
- * kind, then this means 00:00:00 */
- if (c->day || c->weekdays_bits > 0)
- goto null_hour;
-
- goto finish;
- }
+ /* If no time is specified at all, then this means 00:00:00 */
+ if (*t == 0)
+ goto null_hour;
r = parse_chain(&t, false, &h);
if (r < 0)
@@ -704,12 +754,8 @@ static int parse_calendar_time(const char **p, CalendarSpec *c) {
goto fail;
/* Already at the end? Then it's hours and minutes, and seconds are 0 */
- if (*t == 0) {
- if (m != NULL)
- goto null_second;
-
- goto finish;
- }
+ if (*t == 0)
+ goto null_second;
if (*t != ':') {
r = -EINVAL;
@@ -765,9 +811,6 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
assert(p);
assert(spec);
- if (isempty(p))
- return -EINVAL;
-
c = new0(CalendarSpec, 1);
if (!c)
return -ENOMEM;
@@ -806,6 +849,11 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
}
}
+ if (isempty(p)) {
+ r = -EINVAL;
+ goto fail;
+ }
+
if (strcaseeq(p, "minutely")) {
r = const_chain(0, &c->microsecond);
if (r < 0)
@@ -965,9 +1013,23 @@ fail:
return r;
}
-static int find_matching_component(const CalendarComponent *c, int *val) {
- const CalendarComponent *n;
- int d = -1;
+static int find_end_of_month(struct tm *tm, bool utc, int day) {
+ struct tm t = *tm;
+
+ t.tm_mon++;
+ t.tm_mday = 1 - day;
+
+ if (mktime_or_timegm(&t, utc) < 0 ||
+ t.tm_mon != tm->tm_mon)
+ return -1;
+
+ return t.tm_mday;
+}
+
+static int find_matching_component(const CalendarSpec *spec, const CalendarComponent *c,
+ struct tm *tm, int *val) {
+ const CalendarComponent *p = c;
+ int start, stop, d = -1;
bool d_set = false;
int r;
@@ -977,27 +1039,36 @@ static int find_matching_component(const CalendarComponent *c, int *val) {
return 0;
while (c) {
- n = c->next;
+ start = c->start;
+ stop = c->stop;
+
+ if (spec->end_of_month && p == spec->day) {
+ start = find_end_of_month(tm, spec->utc, start);
+ stop = find_end_of_month(tm, spec->utc, stop);
+
+ if (stop > 0)
+ SWAP_TWO(start, stop);
+ }
- if (c->value >= *val) {
+ if (start >= *val) {
- if (!d_set || c->value < d) {
- d = c->value;
+ if (!d_set || start < d) {
+ d = start;
d_set = true;
}
} else if (c->repeat > 0) {
int k;
- k = c->value + c->repeat * ((*val - c->value + c->repeat -1) / c->repeat);
+ k = start + c->repeat * ((*val - start + c->repeat - 1) / c->repeat);
- if (!d_set || k < d) {
+ if ((!d_set || k < d) && (stop < 0 || k <= stop)) {
d = k;
d_set = true;
}
}
- c = n;
+ c = c->next;
}
if (!d_set)
@@ -1014,7 +1085,15 @@ static bool tm_out_of_bounds(const struct tm *tm, bool utc) {
t = *tm;
- if (mktime_or_timegm(&t, utc) == (time_t) -1)
+ if (mktime_or_timegm(&t, utc) < 0)
+ return true;
+
+ /*
+ * Set an upper bound on the year so impossible dates like "*-02-31"
+ * don't cause find_next() to loop forever. tm_year contains years
+ * since 1900, so adjust it accordingly.
+ */
+ if (tm->tm_year + 1900 > MAX_YEAR)
return true;
/* Did any normalization take place? If so, it was out of bounds before */
@@ -1035,7 +1114,7 @@ static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) {
return true;
t = *tm;
- if (mktime_or_timegm(&t, utc) == (time_t) -1)
+ if (mktime_or_timegm(&t, utc) < 0)
return false;
k = t.tm_wday == 0 ? 6 : t.tm_wday - 1;
@@ -1059,7 +1138,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) {
c.tm_isdst = spec->dst;
c.tm_year += 1900;
- r = find_matching_component(spec->year, &c.tm_year);
+ r = find_matching_component(spec, spec->year, &c, &c.tm_year);
c.tm_year -= 1900;
if (r > 0) {
@@ -1073,7 +1152,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) {
return -ENOENT;
c.tm_mon += 1;
- r = find_matching_component(spec->month, &c.tm_mon);
+ r = find_matching_component(spec, spec->month, &c, &c.tm_mon);
c.tm_mon -= 1;
if (r > 0) {
@@ -1088,7 +1167,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) {
continue;
}
- r = find_matching_component(spec->day, &c.tm_mday);
+ r = find_matching_component(spec, spec->day, &c, &c.tm_mday);
if (r > 0)
c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
@@ -1104,7 +1183,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) {
continue;
}
- r = find_matching_component(spec->hour, &c.tm_hour);
+ r = find_matching_component(spec, spec->hour, &c, &c.tm_hour);
if (r > 0)
c.tm_min = c.tm_sec = tm_usec = 0;
if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
@@ -1113,7 +1192,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) {
continue;
}
- r = find_matching_component(spec->minute, &c.tm_min);
+ r = find_matching_component(spec, spec->minute, &c, &c.tm_min);
if (r > 0)
c.tm_sec = tm_usec = 0;
if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
@@ -1123,7 +1202,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) {
}
c.tm_sec = c.tm_sec * USEC_PER_SEC + tm_usec;
- r = find_matching_component(spec->microsecond, &c.tm_sec);
+ r = find_matching_component(spec, spec->microsecond, &c, &c.tm_sec);
tm_usec = c.tm_sec % USEC_PER_SEC;
c.tm_sec /= USEC_PER_SEC;
@@ -1148,6 +1227,9 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next)
assert(spec);
assert(next);
+ if (usec > USEC_TIMESTAMP_FORMATTABLE_MAX)
+ return -EINVAL;
+
usec++;
t = (time_t) (usec / USEC_PER_SEC);
assert_se(localtime_or_gmtime_r(&t, &tm, spec->utc));
@@ -1158,7 +1240,7 @@ int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next)
return r;
t = mktime_or_timegm(&tm, spec->utc);
- if (t == (time_t) -1)
+ if (t < 0)
return -EINVAL;
*next = (usec_t) t * USEC_PER_SEC + tm_usec;
diff --git a/src/basic/calendarspec.h b/src/basic/calendarspec.h
index c6087228fd..3d8798de0b 100644
--- a/src/basic/calendarspec.h
+++ b/src/basic/calendarspec.h
@@ -28,7 +28,8 @@
#include "util.h"
typedef struct CalendarComponent {
- int value;
+ int start;
+ int stop;
int repeat;
struct CalendarComponent *next;
@@ -36,6 +37,7 @@ typedef struct CalendarComponent {
typedef struct CalendarSpec {
int weekdays_bits;
+ bool end_of_month;
bool utc;
int dst;
diff --git a/src/basic/cap-list.c b/src/basic/cap-list.c
index 3e773a06f5..d68cc78d05 100644
--- a/src/basic/cap-list.c
+++ b/src/basic/cap-list.c
@@ -26,7 +26,7 @@
#include "parse-util.h"
#include "util.h"
-static const struct capability_name* lookup_capability(register const char *str, register unsigned int len);
+static const struct capability_name* lookup_capability(register const char *str, register GPERF_LEN_TYPE len);
#include "cap-from-name.h"
#include "cap-to-name.h"
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c
index cede835920..bda5c555ad 100644
--- a/src/basic/cgroup-util.c
+++ b/src/basic/cgroup-util.c
@@ -38,7 +38,7 @@
#include "extract-word.h"
#include "fd-util.h"
#include "fileio.h"
-#include "formats-util.h"
+#include "format-util.h"
#include "fs-util.h"
#include "log.h"
#include "login-util.h"
@@ -182,8 +182,7 @@ int cg_read_subgroup(DIR *d, char **fn) {
if (de->d_type != DT_DIR)
continue;
- if (streq(de->d_name, ".") ||
- streq(de->d_name, ".."))
+ if (dot_or_dot_dot(de->d_name))
continue;
b = strdup(de->d_name);
@@ -209,6 +208,18 @@ int cg_rmdir(const char *controller, const char *path) {
if (r < 0 && errno != ENOENT)
return -errno;
+ r = cg_hybrid_unified();
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 0;
+
+ if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
+ r = cg_rmdir(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path);
+ if (r < 0)
+ log_warning_errno(r, "Failed to remove compat systemd cgroup %s: %m", path);
+ }
+
return 0;
}
@@ -345,7 +356,7 @@ int cg_kill_recursive(
while ((r = cg_read_subgroup(d, &fn)) > 0) {
_cleanup_free_ char *p = NULL;
- p = strjoin(path, "/", fn, NULL);
+ p = strjoin(path, "/", fn);
free(fn);
if (!p)
return -ENOMEM;
@@ -479,7 +490,7 @@ int cg_migrate_recursive(
while ((r = cg_read_subgroup(d, &fn)) > 0) {
_cleanup_free_ char *p = NULL;
- p = strjoin(pfrom, "/", fn, NULL);
+ p = strjoin(pfrom, "/", fn);
free(fn);
if (!p)
return -ENOMEM;
@@ -543,6 +554,13 @@ static const char *controller_to_dirname(const char *controller) {
* just cuts off the name= prefixed used for named
* hierarchies, if it is specified. */
+ if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
+ if (cg_hybrid_unified() > 0)
+ controller = SYSTEMD_CGROUP_CONTROLLER_HYBRID;
+ else
+ controller = SYSTEMD_CGROUP_CONTROLLER_LEGACY;
+ }
+
e = startswith(controller, "name=");
if (e)
return e;
@@ -562,11 +580,11 @@ static int join_path_legacy(const char *controller, const char *path, const char
if (isempty(path) && isempty(suffix))
t = strappend("/sys/fs/cgroup/", dn);
else if (isempty(path))
- t = strjoin("/sys/fs/cgroup/", dn, "/", suffix, NULL);
+ t = strjoin("/sys/fs/cgroup/", dn, "/", suffix);
else if (isempty(suffix))
- t = strjoin("/sys/fs/cgroup/", dn, "/", path, NULL);
+ t = strjoin("/sys/fs/cgroup/", dn, "/", path);
else
- t = strjoin("/sys/fs/cgroup/", dn, "/", path, "/", suffix, NULL);
+ t = strjoin("/sys/fs/cgroup/", dn, "/", path, "/", suffix);
if (!t)
return -ENOMEM;
@@ -586,7 +604,7 @@ static int join_path_unified(const char *path, const char *suffix, char **fs) {
else if (isempty(suffix))
t = strappend("/sys/fs/cgroup/", path);
else
- t = strjoin("/sys/fs/cgroup/", path, "/", suffix, NULL);
+ t = strjoin("/sys/fs/cgroup/", path, "/", suffix);
if (!t)
return -ENOMEM;
@@ -595,7 +613,7 @@ static int join_path_unified(const char *path, const char *suffix, char **fs) {
}
int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
- int unified, r;
+ int r;
assert(fs);
@@ -613,7 +631,7 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch
else if (!path)
t = strdup(suffix);
else
- t = strjoin(path, "/", suffix, NULL);
+ t = strjoin(path, "/", suffix);
if (!t)
return -ENOMEM;
@@ -624,11 +642,10 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch
if (!cg_controller_is_valid(controller))
return -EINVAL;
- unified = cg_all_unified();
- if (unified < 0)
- return unified;
-
- if (unified > 0)
+ r = cg_all_unified();
+ if (r < 0)
+ return r;
+ if (r > 0)
r = join_path_unified(path, suffix, fs);
else
r = join_path_legacy(controller, path, suffix, fs);
@@ -640,7 +657,7 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch
}
static int controller_is_accessible(const char *controller) {
- int unified;
+ int r;
assert(controller);
@@ -652,10 +669,10 @@ static int controller_is_accessible(const char *controller) {
if (!cg_controller_is_valid(controller))
return -EINVAL;
- unified = cg_all_unified();
- if (unified < 0)
- return unified;
- if (unified > 0) {
+ r = cg_all_unified();
+ if (r < 0)
+ return r;
+ if (r > 0) {
/* We don't support named hierarchies if we are using
* the unified hierarchy. */
@@ -709,7 +726,7 @@ static int trim_cb(const char *path, const struct stat *sb, int typeflag, struct
int cg_trim(const char *controller, const char *path, bool delete_root) {
_cleanup_free_ char *fs = NULL;
- int r = 0;
+ int r = 0, q;
assert(path);
@@ -732,6 +749,15 @@ int cg_trim(const char *controller, const char *path, bool delete_root) {
return -errno;
}
+ q = cg_hybrid_unified();
+ if (q < 0)
+ return q;
+ if (q > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
+ q = cg_trim(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, delete_root);
+ if (q < 0)
+ log_warning_errno(q, "Failed to trim compat systemd cgroup %s: %m", path);
+ }
+
return r;
}
@@ -755,6 +781,16 @@ int cg_create(const char *controller, const char *path) {
return -errno;
}
+ r = cg_hybrid_unified();
+ if (r < 0)
+ return r;
+
+ if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
+ r = cg_create(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path);
+ if (r < 0)
+ log_warning_errno(r, "Failed to create compat systemd cgroup %s: %m", path);
+ }
+
return 1;
}
@@ -792,7 +828,21 @@ int cg_attach(const char *controller, const char *path, pid_t pid) {
xsprintf(c, PID_FMT "\n", pid);
- return write_string_file(fs, c, 0);
+ r = write_string_file(fs, c, 0);
+ if (r < 0)
+ return r;
+
+ r = cg_hybrid_unified();
+ if (r < 0)
+ return r;
+
+ if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
+ r = cg_attach(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, pid);
+ if (r < 0)
+ log_warning_errno(r, "Failed to attach %d to compat systemd cgroup %s: %m", pid, path);
+ }
+
+ return 0;
}
int cg_attach_fallback(const char *controller, const char *path, pid_t pid) {
@@ -841,7 +891,20 @@ int cg_set_group_access(
if (r < 0)
return r;
- return chmod_and_chown(fs, mode, uid, gid);
+ r = chmod_and_chown(fs, mode, uid, gid);
+ if (r < 0)
+ return r;
+
+ r = cg_hybrid_unified();
+ if (r < 0)
+ return r;
+ if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
+ r = cg_set_group_access(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, mode, uid, gid);
+ if (r < 0)
+ log_warning_errno(r, "Failed to set group access on compat systemd cgroup %s: %m", path);
+ }
+
+ return 0;
}
int cg_set_task_access(
@@ -852,7 +915,7 @@ int cg_set_task_access(
gid_t gid) {
_cleanup_free_ char *fs = NULL, *procs = NULL;
- int r, unified;
+ int r;
assert(path);
@@ -870,16 +933,24 @@ int cg_set_task_access(
if (r < 0)
return r;
- unified = cg_unified(controller);
- if (unified < 0)
- return unified;
- if (unified)
- return 0;
+ r = cg_unified_controller(controller);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ /* Compatibility, Always keep values for "tasks" in sync with
+ * "cgroup.procs" */
+ if (cg_get_path(controller, path, "tasks", &procs) >= 0)
+ (void) chmod_and_chown(procs, mode, uid, gid);
+ }
- /* Compatibility, Always keep values for "tasks" in sync with
- * "cgroup.procs" */
- if (cg_get_path(controller, path, "tasks", &procs) >= 0)
- (void) chmod_and_chown(procs, mode, uid, gid);
+ r = cg_hybrid_unified();
+ if (r < 0)
+ return r;
+ if (r > 0 && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) {
+ r = cg_set_task_access(SYSTEMD_CGROUP_CONTROLLER_LEGACY, path, mode, uid, gid);
+ if (r < 0)
+ log_warning_errno(r, "Failed to set task access on compat systemd cgroup %s: %m", path);
+ }
return 0;
}
@@ -924,7 +995,7 @@ int cg_get_xattr(const char *controller, const char *path, const char *name, voi
int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
_cleanup_fclose_ FILE *f = NULL;
char line[LINE_MAX];
- const char *fs;
+ const char *fs, *controller_str;
size_t cs = 0;
int unified;
@@ -937,11 +1008,17 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
} else
controller = SYSTEMD_CGROUP_CONTROLLER;
- unified = cg_unified(controller);
+ unified = cg_unified_controller(controller);
if (unified < 0)
return unified;
- if (unified == 0)
- cs = strlen(controller);
+ if (unified == 0) {
+ if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
+ controller_str = SYSTEMD_CGROUP_CONTROLLER_LEGACY;
+ else
+ controller_str = controller;
+
+ cs = strlen(controller_str);
+ }
fs = procfs_file_alloca(pid, "cgroup");
f = fopen(fs, "re");
@@ -978,7 +1055,7 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
*e = 0;
FOREACH_WORD_SEPARATOR(word, k, l, ",", state) {
- if (k == cs && memcmp(word, controller, cs) == 0) {
+ if (k == cs && memcmp(word, controller_str, cs) == 0) {
found = true;
break;
}
@@ -1002,14 +1079,14 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path) {
int cg_install_release_agent(const char *controller, const char *agent) {
_cleanup_free_ char *fs = NULL, *contents = NULL;
const char *sc;
- int r, unified;
+ int r;
assert(agent);
- unified = cg_unified(controller);
- if (unified < 0)
- return unified;
- if (unified) /* doesn't apply to unified hierarchy */
+ r = cg_unified_controller(controller);
+ if (r < 0)
+ return r;
+ if (r > 0) /* doesn't apply to unified hierarchy */
return -EOPNOTSUPP;
r = cg_get_path(controller, NULL, "release_agent", &fs);
@@ -1055,12 +1132,12 @@ int cg_install_release_agent(const char *controller, const char *agent) {
int cg_uninstall_release_agent(const char *controller) {
_cleanup_free_ char *fs = NULL;
- int r, unified;
+ int r;
- unified = cg_unified(controller);
- if (unified < 0)
- return unified;
- if (unified) /* Doesn't apply to unified hierarchy */
+ r = cg_unified_controller(controller);
+ if (r < 0)
+ return r;
+ if (r > 0) /* Doesn't apply to unified hierarchy */
return -EOPNOTSUPP;
r = cg_get_path(controller, NULL, "notify_on_release", &fs);
@@ -1105,7 +1182,7 @@ int cg_is_empty(const char *controller, const char *path) {
}
int cg_is_empty_recursive(const char *controller, const char *path) {
- int unified, r;
+ int r;
assert(path);
@@ -1113,11 +1190,10 @@ int cg_is_empty_recursive(const char *controller, const char *path) {
if (controller && (isempty(path) || path_equal(path, "/")))
return false;
- unified = cg_unified(controller);
- if (unified < 0)
- return unified;
-
- if (unified > 0) {
+ r = cg_unified_controller(controller);
+ if (r < 0)
+ return r;
+ if (r > 0) {
_cleanup_free_ char *t = NULL;
/* On the unified hierarchy we can check empty state
@@ -1145,7 +1221,7 @@ int cg_is_empty_recursive(const char *controller, const char *path) {
while ((r = cg_read_subgroup(d, &fn)) > 0) {
_cleanup_free_ char *p = NULL;
- p = strjoin(path, "/", fn, NULL);
+ p = strjoin(path, "/", fn);
free(fn);
if (!p)
return -ENOMEM;
@@ -1834,6 +1910,9 @@ bool cg_controller_is_valid(const char *p) {
if (!p)
return false;
+ if (streq(p, SYSTEMD_CGROUP_CONTROLLER))
+ return true;
+
s = startswith(p, "name=");
if (s)
p = s;
@@ -1987,7 +2066,7 @@ int cg_get_keyed_attribute(const char *controller, const char *path, const char
int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path) {
CGroupController c;
- int r, unified;
+ int r;
/* This one will create a cgroup in our private tree, but also
* duplicate it in the trees specified in mask, and remove it
@@ -1999,10 +2078,10 @@ 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_all_unified();
- if (unified < 0)
- return unified;
- if (unified > 0)
+ r = cg_all_unified();
+ if (r < 0)
+ return r;
+ if (r > 0)
return 0;
/* Otherwise, do the same in the other hierarchies */
@@ -2023,16 +2102,16 @@ int cg_create_everywhere(CGroupMask supported, CGroupMask mask, const char *path
int cg_attach_everywhere(CGroupMask supported, const char *path, pid_t pid, cg_migrate_callback_t path_callback, void *userdata) {
CGroupController c;
- int r, unified;
+ int r;
r = cg_attach(SYSTEMD_CGROUP_CONTROLLER, path, pid);
if (r < 0)
return r;
- unified = cg_all_unified();
- if (unified < 0)
- return unified;
- if (unified > 0)
+ r = cg_all_unified();
+ if (r < 0)
+ return r;
+ if (r > 0)
return 0;
for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
@@ -2073,7 +2152,7 @@ int cg_attach_many_everywhere(CGroupMask supported, const char *path, Set* pids,
int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to, cg_migrate_callback_t to_callback, void *userdata) {
CGroupController c;
- int r = 0, unified;
+ int r = 0, q;
if (!path_equal(from, to)) {
r = cg_migrate_recursive(SYSTEMD_CGROUP_CONTROLLER, from, SYSTEMD_CGROUP_CONTROLLER, to, CGROUP_REMOVE);
@@ -2081,10 +2160,10 @@ int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to
return r;
}
- unified = cg_all_unified();
- if (unified < 0)
- return unified;
- if (unified > 0)
+ q = cg_all_unified();
+ if (q < 0)
+ return q;
+ if (q > 0)
return r;
for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
@@ -2108,16 +2187,16 @@ int cg_migrate_everywhere(CGroupMask supported, const char *from, const char *to
int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root) {
CGroupController c;
- int r, unified;
+ int r, q;
r = cg_trim(SYSTEMD_CGROUP_CONTROLLER, path, delete_root);
if (r < 0)
return r;
- unified = cg_all_unified();
- if (unified < 0)
- return unified;
- if (unified > 0)
+ q = cg_all_unified();
+ if (q < 0)
+ return q;
+ if (q > 0)
return r;
for (c = 0; c < _CGROUP_CONTROLLER_MAX; c++) {
@@ -2134,16 +2213,16 @@ int cg_trim_everywhere(CGroupMask supported, const char *path, bool delete_root)
int cg_mask_supported(CGroupMask *ret) {
CGroupMask mask = 0;
- int r, unified;
+ int r;
/* Determines the mask of supported cgroup controllers. Only
* includes controllers we can make sense of and that are
* actually accessible. */
- unified = cg_all_unified();
- if (unified < 0)
- return unified;
- if (unified > 0) {
+ r = cg_all_unified();
+ if (r < 0)
+ return r;
+ if (r > 0) {
_cleanup_free_ char *root = NULL, *controllers = NULL, *path = NULL;
const char *c;
@@ -2263,7 +2342,18 @@ int cg_kernel_controllers(Set *controllers) {
static thread_local CGroupUnified unified_cache = CGROUP_UNIFIED_UNKNOWN;
-static int cg_update_unified(void) {
+/* The hybrid mode was initially implemented in v232 and simply mounted cgroup v2 on /sys/fs/cgroup/systemd. This
+ * unfortunately broke other tools (such as docker) which expected the v1 "name=systemd" hierarchy on
+ * /sys/fs/cgroup/systemd. From v233 and on, the hybrid mode mountnbs v2 on /sys/fs/cgroup/unified and maintains
+ * "name=systemd" hierarchy on /sys/fs/cgroup/systemd for compatibility with other tools.
+ *
+ * To keep live upgrade working, we detect and support v232 layout. When v232 layout is detected, to keep cgroup v2
+ * process management but disable the compat dual layout, we return %true on
+ * cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) and %false on cg_hybrid_unified().
+ */
+static thread_local bool unified_systemd_v232;
+
+static int cg_unified_update(void) {
struct statfs fs;
@@ -2281,54 +2371,83 @@ static int cg_update_unified(void) {
if (F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC))
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;
+ if (statfs("/sys/fs/cgroup/unified/", &fs) == 0 &&
+ F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
+ unified_cache = CGROUP_UNIFIED_SYSTEMD;
+ unified_systemd_v232 = false;
+ } else if (statfs("/sys/fs/cgroup/systemd/", &fs) == 0 &&
+ F_TYPE_EQUAL(fs.f_type, CGROUP2_SUPER_MAGIC)) {
+ unified_cache = CGROUP_UNIFIED_SYSTEMD;
+ unified_systemd_v232 = true;
+ } else {
+ if (statfs("/sys/fs/cgroup/systemd/", &fs) < 0)
+ return -errno;
+ if (!F_TYPE_EQUAL(fs.f_type, CGROUP_SUPER_MAGIC))
+ return -ENOMEDIUM;
+ unified_cache = CGROUP_UNIFIED_NONE;
+ }
} else
return -ENOMEDIUM;
return 0;
}
-int cg_unified(const char *controller) {
-
+int cg_unified_controller(const char *controller) {
int r;
- r = cg_update_unified();
+ r = cg_unified_update();
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;
+ if (unified_cache == CGROUP_UNIFIED_NONE)
+ return false;
+
+ if (unified_cache >= CGROUP_UNIFIED_ALL)
+ return true;
+
+ return streq_ptr(controller, SYSTEMD_CGROUP_CONTROLLER);
}
int cg_all_unified(void) {
+ int r;
- return cg_unified(NULL);
+ r = cg_unified_update();
+ if (r < 0)
+ return r;
+
+ return unified_cache >= CGROUP_UNIFIED_ALL;
}
-void cg_unified_flush(void) {
+int cg_hybrid_unified(void) {
+ int r;
+
+ r = cg_unified_update();
+ if (r < 0)
+ return r;
+
+ return unified_cache == CGROUP_UNIFIED_SYSTEMD && !unified_systemd_v232;
+}
+
+int cg_unified_flush(void) {
unified_cache = CGROUP_UNIFIED_UNKNOWN;
+
+ return cg_unified_update();
}
int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) {
_cleanup_free_ char *fs = NULL;
CGroupController c;
- int r, unified;
+ int r;
assert(p);
if (supported == 0)
return 0;
- unified = cg_all_unified();
- if (unified < 0)
- return unified;
- if (!unified) /* on the legacy hiearchy there's no joining of controllers defined */
+ r = cg_all_unified();
+ if (r < 0)
+ return r;
+ if (r == 0) /* on the legacy hiearchy there's no joining of controllers defined */
return 0;
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, p, "cgroup.subtree_control", &fs);
@@ -2360,82 +2479,69 @@ int cg_enable_everywhere(CGroupMask supported, CGroupMask mask, const char *p) {
bool cg_is_unified_wanted(void) {
static thread_local int wanted = -1;
- int r, unified;
-
- /* If the hierarchy is already mounted, then follow whatever
- * was chosen for it. */
- unified = cg_all_unified();
- if (unified >= 0)
- return unified;
+ int r;
+ bool b;
+ const bool is_default = DEFAULT_HIERARCHY == CGROUP_UNIFIED_ALL;
- /* Otherwise, let's see what the kernel command line has to
- * say. Since checking that is expensive, let's cache the
- * result. */
+ /* If we have a cached value, return that. */
if (wanted >= 0)
return wanted;
- r = get_proc_cmdline_key("systemd.unified_cgroup_hierarchy", NULL);
- if (r > 0)
- return (wanted = true);
- else {
- _cleanup_free_ char *value = NULL;
+ /* If the hierarchy is already mounted, then follow whatever
+ * was chosen for it. */
+ if (cg_unified_flush() >= 0)
+ return (wanted = unified_cache >= CGROUP_UNIFIED_ALL);
- r = get_proc_cmdline_key("systemd.unified_cgroup_hierarchy=", &value);
- if (r < 0)
- return false;
- if (r == 0)
- return (wanted = false);
+ /* Otherwise, let's see what the kernel command line has to say.
+ * Since checking is expensive, cache a non-error result. */
+ r = proc_cmdline_get_bool("systemd.unified_cgroup_hierarchy", &b);
- return (wanted = parse_boolean(value) > 0);
- }
+ return (wanted = r > 0 ? b : is_default);
}
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 we have a cached value, return that. */
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;
+ /* Check if we have cgroups2 already mounted. */
+ if (cg_unified_flush() >= 0 &&
+ unified_cache == CGROUP_UNIFIED_ALL)
+ return (wanted = false);
- r = get_proc_cmdline_key("systemd.legacy_systemd_cgroup_controller=", &value);
- if (r < 0)
- return true;
+ /* Otherwise, assume that at least partial legacy is wanted,
+ * since cgroups2 should already be mounted at this point. */
+ return (wanted = true);
+}
- if (r == 0)
- wanted = true;
- else
- wanted = parse_boolean(value) <= 0;
- }
+bool cg_is_hybrid_wanted(void) {
+ static thread_local int wanted = -1;
+ int r;
+ bool b;
+ const bool is_default = DEFAULT_HIERARCHY >= CGROUP_UNIFIED_SYSTEMD;
+ /* We default to true if the default is "hybrid", obviously,
+ * but also when the default is "unified", because if we get
+ * called, it means that unified hierarchy was not mounted. */
- return wanted;
-}
+ /* If we have a cached value, return that. */
+ if (wanted >= 0)
+ return wanted;
+
+ /* If the hierarchy is already mounted, then follow whatever
+ * was chosen for it. */
+ if (cg_unified_flush() >= 0 &&
+ unified_cache == CGROUP_UNIFIED_ALL)
+ return (wanted = false);
+
+ /* Otherwise, let's see what the kernel command line has to say.
+ * Since checking is expensive, cache a non-error result. */
+ r = proc_cmdline_get_bool("systemd.legacy_systemd_cgroup_controller", &b);
-bool cg_is_legacy_systemd_controller_wanted(void) {
- return cg_is_legacy_wanted() && !cg_is_unified_systemd_controller_wanted();
+ /* The meaning of the kernel option is reversed wrt. to the return value
+ * of this function, hence the negation. */
+ return (wanted = r > 0 ? !b : is_default);
}
int cg_weight_parse(const char *s, uint64_t *ret) {
diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h
index 0aa27c4cd7..a522095d95 100644
--- a/src/basic/cgroup-util.h
+++ b/src/basic/cgroup-util.h
@@ -241,13 +241,13 @@ int cg_kernel_controllers(Set *controllers);
bool cg_ns_supported(void);
int cg_all_unified(void);
-int cg_unified(const char *controller);
-void cg_unified_flush(void);
+int cg_hybrid_unified(void);
+int cg_unified_controller(const char *controller);
+int 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);
+bool cg_is_hybrid_wanted(void);
const char* cgroup_controller_to_string(CGroupController c) _const_;
CGroupController cgroup_controller_from_string(const char *s) _pure_;
diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c
index c781610e14..b8f0f5d03d 100644
--- a/src/basic/conf-files.c
+++ b/src/basic/conf-files.c
@@ -43,7 +43,6 @@ static int files_add(Hashmap *h, const char *root, const char *path, const char
int r;
assert(path);
- assert(suffix);
dirpath = prefix_roota(root, path);
@@ -60,7 +59,7 @@ static int files_add(Hashmap *h, const char *root, const char *path, const char
if (!dirent_is_file_with_suffix(de, suffix))
continue;
- p = strjoin(dirpath, "/", de->d_name, NULL);
+ p = strjoin(dirpath, "/", de->d_name);
if (!p)
return -ENOMEM;
@@ -94,7 +93,6 @@ static int conf_files_list_strv_internal(char ***strv, const char *suffix, const
int r;
assert(strv);
- assert(suffix);
/* This alters the dirs string array */
if (!path_strv_resolve_uniq(dirs, root))
@@ -126,7 +124,6 @@ int conf_files_list_strv(char ***strv, const char *suffix, const char *root, con
_cleanup_strv_free_ char **copy = NULL;
assert(strv);
- assert(suffix);
copy = strv_copy((char**) dirs);
if (!copy)
@@ -140,7 +137,6 @@ int conf_files_list(char ***strv, const char *suffix, const char *root, const ch
va_list ap;
assert(strv);
- assert(suffix);
va_start(ap, dir);
dirs = strv_new_ap(dir, ap);
@@ -156,7 +152,6 @@ int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, c
_cleanup_strv_free_ char **dirs = NULL;
assert(strv);
- assert(suffix);
dirs = strv_split_nulstr(d);
if (!dirs)
diff --git a/src/basic/copy.c b/src/basic/copy.c
index 9883f5fa31..e120b9eb4e 100644
--- a/src/basic/copy.c
+++ b/src/basic/copy.c
@@ -45,6 +45,7 @@
#include "strv.h"
#include "time-util.h"
#include "umask-util.h"
+#include "user-util.h"
#include "xattr-util.h"
#define COPY_BUFFER_SIZE (16*1024u)
@@ -68,7 +69,7 @@ static ssize_t try_copy_file_range(int fd_in, loff_t *off_in,
return -errno;
}
-int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) {
+int copy_bytes(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags) {
bool try_cfr = true, try_sendfile = true, try_splice = true;
int r;
size_t m = SSIZE_MAX; /* that is the maximum that sendfile and c_f_r accept */
@@ -77,7 +78,7 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) {
assert(fdt >= 0);
/* Try btrfs reflinks first. */
- if (try_reflink &&
+ if ((copy_flags & COPY_REFLINK) &&
max_bytes == (uint64_t) -1 &&
lseek(fdf, 0, SEEK_CUR) == 0 &&
lseek(fdt, 0, SEEK_CUR) == 0) {
@@ -176,7 +177,16 @@ int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink) {
return 0; /* return 0 if we hit EOF earlier than the size limit */
}
-static int fd_copy_symlink(int df, const char *from, const struct stat *st, int dt, const char *to) {
+static int fd_copy_symlink(
+ int df,
+ const char *from,
+ const struct stat *st,
+ int dt,
+ const char *to,
+ uid_t override_uid,
+ gid_t override_gid,
+ CopyFlags copy_flags) {
+
_cleanup_free_ char *target = NULL;
int r;
@@ -191,13 +201,25 @@ static int fd_copy_symlink(int df, const char *from, const struct stat *st, int
if (symlinkat(target, dt, to) < 0)
return -errno;
- if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
+ if (fchownat(dt, to,
+ uid_is_valid(override_uid) ? override_uid : st->st_uid,
+ gid_is_valid(override_gid) ? override_gid : st->st_gid,
+ AT_SYMLINK_NOFOLLOW) < 0)
return -errno;
return 0;
}
-static int fd_copy_regular(int df, const char *from, const struct stat *st, int dt, const char *to) {
+static int fd_copy_regular(
+ int df,
+ const char *from,
+ const struct stat *st,
+ int dt,
+ const char *to,
+ uid_t override_uid,
+ gid_t override_gid,
+ CopyFlags copy_flags) {
+
_cleanup_close_ int fdf = -1, fdt = -1;
struct timespec ts[2];
int r, q;
@@ -214,13 +236,15 @@ static int fd_copy_regular(int df, const char *from, const struct stat *st, int
if (fdt < 0)
return -errno;
- r = copy_bytes(fdf, fdt, (uint64_t) -1, true);
+ r = copy_bytes(fdf, fdt, (uint64_t) -1, copy_flags);
if (r < 0) {
unlinkat(dt, to, 0);
return r;
}
- if (fchown(fdt, st->st_uid, st->st_gid) < 0)
+ if (fchown(fdt,
+ uid_is_valid(override_uid) ? override_uid : st->st_uid,
+ gid_is_valid(override_gid) ? override_gid : st->st_gid) < 0)
r = -errno;
if (fchmod(fdt, st->st_mode & 07777) < 0)
@@ -229,7 +253,6 @@ static int fd_copy_regular(int df, const char *from, const struct stat *st, int
ts[0] = st->st_atim;
ts[1] = st->st_mtim;
(void) futimens(fdt, ts);
-
(void) copy_xattr(fdf, fdt);
q = close(fdt);
@@ -243,7 +266,15 @@ static int fd_copy_regular(int df, const char *from, const struct stat *st, int
return r;
}
-static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt, const char *to) {
+static int fd_copy_fifo(
+ int df,
+ const char *from,
+ const struct stat *st,
+ int dt,
+ const char *to,
+ uid_t override_uid,
+ gid_t override_gid,
+ CopyFlags copy_flags) {
int r;
assert(from);
@@ -254,7 +285,10 @@ static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt,
if (r < 0)
return -errno;
- if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
+ if (fchownat(dt, to,
+ uid_is_valid(override_uid) ? override_uid : st->st_uid,
+ gid_is_valid(override_gid) ? override_gid : st->st_gid,
+ AT_SYMLINK_NOFOLLOW) < 0)
r = -errno;
if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
@@ -263,7 +297,15 @@ static int fd_copy_fifo(int df, const char *from, const struct stat *st, int dt,
return r;
}
-static int fd_copy_node(int df, const char *from, const struct stat *st, int dt, const char *to) {
+static int fd_copy_node(
+ int df,
+ const char *from,
+ const struct stat *st,
+ int dt,
+ const char *to,
+ uid_t override_uid,
+ gid_t override_gid,
+ CopyFlags copy_flags) {
int r;
assert(from);
@@ -274,7 +316,10 @@ static int fd_copy_node(int df, const char *from, const struct stat *st, int dt,
if (r < 0)
return -errno;
- if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0)
+ if (fchownat(dt, to,
+ uid_is_valid(override_uid) ? override_uid : st->st_uid,
+ gid_is_valid(override_gid) ? override_gid : st->st_gid,
+ AT_SYMLINK_NOFOLLOW) < 0)
r = -errno;
if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0)
@@ -290,7 +335,9 @@ static int fd_copy_directory(
int dt,
const char *to,
dev_t original_device,
- bool merge) {
+ uid_t override_uid,
+ gid_t override_gid,
+ CopyFlags copy_flags) {
_cleanup_close_ int fdf = -1, fdt = -1;
_cleanup_closedir_ DIR *d = NULL;
@@ -316,7 +363,7 @@ static int fd_copy_directory(
r = mkdirat(dt, to, st->st_mode & 07777);
if (r >= 0)
created = true;
- else if (errno == EEXIST && merge)
+ else if (errno == EEXIST && (copy_flags & COPY_MERGE))
created = false;
else
return -errno;
@@ -331,7 +378,7 @@ static int fd_copy_directory(
struct stat buf;
int q;
- if (STR_IN_SET(de->d_name, ".", ".."))
+ if (dot_or_dot_dot(de->d_name))
continue;
if (fstatat(dirfd(d), de->d_name, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
@@ -343,19 +390,19 @@ static int fd_copy_directory(
continue;
if (S_ISREG(buf.st_mode))
- q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name);
+ q = fd_copy_regular(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
else if (S_ISDIR(buf.st_mode))
- q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, merge);
+ q = fd_copy_directory(dirfd(d), de->d_name, &buf, fdt, de->d_name, original_device, override_uid, override_gid, copy_flags);
else if (S_ISLNK(buf.st_mode))
- q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name);
+ q = fd_copy_symlink(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
else if (S_ISFIFO(buf.st_mode))
- q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name);
+ q = fd_copy_fifo(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
else if (S_ISBLK(buf.st_mode) || S_ISCHR(buf.st_mode) || S_ISSOCK(buf.st_mode))
- q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name);
+ q = fd_copy_node(dirfd(d), de->d_name, &buf, fdt, de->d_name, override_uid, override_gid, copy_flags);
else
q = -EOPNOTSUPP;
- if (q == -EEXIST && merge)
+ if (q == -EEXIST && (copy_flags & COPY_MERGE))
q = 0;
if (q < 0)
@@ -368,7 +415,9 @@ static int fd_copy_directory(
st->st_mtim
};
- if (fchown(fdt, st->st_uid, st->st_gid) < 0)
+ if (fchown(fdt,
+ uid_is_valid(override_uid) ? override_uid : st->st_uid,
+ gid_is_valid(override_gid) ? override_gid : st->st_gid) < 0)
r = -errno;
if (fchmod(fdt, st->st_mode & 07777) < 0)
@@ -381,7 +430,7 @@ static int fd_copy_directory(
return r;
}
-int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge) {
+int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags) {
struct stat st;
assert(from);
@@ -391,24 +440,24 @@ int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge)
return -errno;
if (S_ISREG(st.st_mode))
- return fd_copy_regular(fdf, from, &st, fdt, to);
+ return fd_copy_regular(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
else if (S_ISDIR(st.st_mode))
- return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, merge);
+ return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, override_uid, override_gid, copy_flags);
else if (S_ISLNK(st.st_mode))
- return fd_copy_symlink(fdf, from, &st, fdt, to);
+ return fd_copy_symlink(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
else if (S_ISFIFO(st.st_mode))
- return fd_copy_fifo(fdf, from, &st, fdt, to);
+ return fd_copy_fifo(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) || S_ISSOCK(st.st_mode))
- return fd_copy_node(fdf, from, &st, fdt, to);
+ return fd_copy_node(fdf, from, &st, fdt, to, override_uid, override_gid, copy_flags);
else
return -EOPNOTSUPP;
}
-int copy_tree(const char *from, const char *to, bool merge) {
- return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, merge);
+int copy_tree(const char *from, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags) {
+ return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, override_uid, override_gid, copy_flags);
}
-int copy_directory_fd(int dirfd, const char *to, bool merge) {
+int copy_directory_fd(int dirfd, const char *to, CopyFlags copy_flags) {
struct stat st;
assert(dirfd >= 0);
@@ -420,10 +469,10 @@ int copy_directory_fd(int dirfd, const char *to, bool merge) {
if (!S_ISDIR(st.st_mode))
return -ENOTDIR;
- return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, merge);
+ return fd_copy_directory(dirfd, NULL, &st, AT_FDCWD, to, st.st_dev, UID_INVALID, GID_INVALID, copy_flags);
}
-int copy_directory(const char *from, const char *to, bool merge) {
+int copy_directory(const char *from, const char *to, CopyFlags copy_flags) {
struct stat st;
assert(from);
@@ -435,10 +484,10 @@ int copy_directory(const char *from, const char *to, bool merge) {
if (!S_ISDIR(st.st_mode))
return -ENOTDIR;
- return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, merge);
+ return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, UID_INVALID, GID_INVALID, copy_flags);
}
-int copy_file_fd(const char *from, int fdt, bool try_reflink) {
+int copy_file_fd(const char *from, int fdt, CopyFlags copy_flags) {
_cleanup_close_ int fdf = -1;
int r;
@@ -449,7 +498,7 @@ int copy_file_fd(const char *from, int fdt, bool try_reflink) {
if (fdf < 0)
return -errno;
- r = copy_bytes(fdf, fdt, (uint64_t) -1, try_reflink);
+ r = copy_bytes(fdf, fdt, (uint64_t) -1, copy_flags);
(void) copy_times(fdf, fdt);
(void) copy_xattr(fdf, fdt);
@@ -457,7 +506,7 @@ int copy_file_fd(const char *from, int fdt, bool try_reflink) {
return r;
}
-int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags) {
+int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags) {
int fdt = -1, r;
assert(from);
@@ -472,7 +521,7 @@ int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned
if (chattr_flags != 0)
(void) chattr_fd(fdt, chattr_flags, (unsigned) -1);
- r = copy_file_fd(from, fdt, true);
+ r = copy_file_fd(from, fdt, copy_flags);
if (r < 0) {
close(fdt);
unlink(to);
@@ -487,7 +536,7 @@ int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned
return 0;
}
-int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags) {
+int copy_file_atomic(const char *from, const char *to, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags) {
_cleanup_free_ char *t = NULL;
int r;
@@ -498,18 +547,18 @@ int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace
if (r < 0)
return r;
- r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags);
+ r = copy_file(from, t, O_NOFOLLOW|O_EXCL, mode, chattr_flags, copy_flags);
if (r < 0)
return r;
- if (replace) {
+ if (copy_flags & COPY_REPLACE) {
r = renameat(AT_FDCWD, t, AT_FDCWD, to);
if (r < 0)
r = -errno;
} else
r = rename_noreplace(AT_FDCWD, t, AT_FDCWD, to);
if (r < 0) {
- (void) unlink_noerrno(t);
+ (void) unlink(t);
return r;
}
diff --git a/src/basic/copy.h b/src/basic/copy.h
index b5d08ebafe..4f3e11423e 100644
--- a/src/basic/copy.h
+++ b/src/basic/copy.h
@@ -24,13 +24,19 @@
#include <stdint.h>
#include <sys/types.h>
-int copy_file_fd(const char *from, int to, bool try_reflink);
-int copy_file(const char *from, const char *to, int flags, mode_t mode, unsigned chattr_flags);
-int copy_file_atomic(const char *from, const char *to, mode_t mode, bool replace, unsigned chattr_flags);
-int copy_tree(const char *from, const char *to, bool merge);
-int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge);
-int copy_directory_fd(int dirfd, const char *to, bool merge);
-int copy_directory(const char *from, const char *to, bool merge);
-int copy_bytes(int fdf, int fdt, uint64_t max_bytes, bool try_reflink);
+typedef enum CopyFlags {
+ COPY_REFLINK = 0x1, /* try to reflink */
+ COPY_MERGE = 0x2, /* merge existing trees with our new one to copy */
+ COPY_REPLACE = 0x4, /* replace an existing file if there's one */
+} CopyFlags;
+
+int copy_file_fd(const char *from, int to, CopyFlags copy_flags);
+int copy_file(const char *from, const char *to, int open_flags, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags);
+int copy_file_atomic(const char *from, const char *to, mode_t mode, unsigned chattr_flags, CopyFlags copy_flags);
+int copy_tree(const char *from, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags);
+int copy_tree_at(int fdf, const char *from, int fdt, const char *to, uid_t override_uid, gid_t override_gid, CopyFlags copy_flags);
+int copy_directory_fd(int dirfd, const char *to, CopyFlags copy_flags);
+int copy_directory(const char *from, const char *to, CopyFlags copy_flags);
+int copy_bytes(int fdf, int fdt, uint64_t max_bytes, CopyFlags copy_flags);
int copy_times(int fdf, int fdt);
int copy_xattr(int fdf, int fdt);
diff --git a/src/basic/def.h b/src/basic/def.h
index 2266eff650..200ea973c1 100644
--- a/src/basic/def.h
+++ b/src/basic/def.h
@@ -36,7 +36,9 @@
/* The default value for the net.unix.max_dgram_qlen sysctl */
#define DEFAULT_UNIX_MAX_DGRAM_QLEN 512UL
-#define SYSTEMD_CGROUP_CONTROLLER "name=systemd"
+#define SYSTEMD_CGROUP_CONTROLLER_LEGACY "name=systemd"
+#define SYSTEMD_CGROUP_CONTROLLER_HYBRID "name=unified"
+#define SYSTEMD_CGROUP_CONTROLLER "_systemd"
#define SIGNALS_CRASH_HANDLER SIGSEGV,SIGILL,SIGFPE,SIGBUS,SIGQUIT,SIGABRT
#define SIGNALS_IGNORE SIGPIPE
@@ -73,18 +75,18 @@
#define NOTIFY_BUFFER_MAX PIPE_BUF
#ifdef HAVE_SPLIT_USR
-#define _CONF_PATHS_SPLIT_USR(n) "/lib/" n "\0"
+# define _CONF_PATHS_SPLIT_USR(n) "/lib/" n "\0"
#else
-#define _CONF_PATHS_SPLIT_USR(n)
+# define _CONF_PATHS_SPLIT_USR(n)
#endif
/* Return a nulstr for a standard cascade of configuration paths,
* 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) \
- "/etc/" n "\0" \
- "/run/" n "\0" \
- "/usr/local/lib/" n "\0" \
- "/usr/lib/" n "\0" \
+#define CONF_PATHS_NULSTR(n) \
+ "/etc/" n "\0" \
+ "/run/" n "\0" \
+ "/usr/local/lib/" n "\0" \
+ "/usr/lib/" n "\0" \
_CONF_PATHS_SPLIT_USR(n)
diff --git a/src/basic/dirent-util.c b/src/basic/dirent-util.c
index 59067121b7..6b9d26773e 100644
--- a/src/basic/dirent-util.c
+++ b/src/basic/dirent-util.c
@@ -70,5 +70,8 @@ bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) {
if (de->d_name[0] == '.')
return false;
+ if (!suffix)
+ return true;
+
return endswith(de->d_name, suffix);
}
diff --git a/src/basic/env-util.c b/src/basic/env-util.c
index b74290d6fd..1ec574e8a0 100644
--- a/src/basic/env-util.c
+++ b/src/basic/env-util.c
@@ -26,6 +26,7 @@
#include "alloc-util.h"
#include "env-util.h"
+#include "escape.h"
#include "extract-word.h"
#include "macro.h"
#include "parse-util.h"
@@ -247,7 +248,7 @@ fail:
return NULL;
}
-_pure_ static bool env_match(const char *t, const char *pattern) {
+static bool env_match(const char *t, const char *pattern) {
assert(t);
assert(pattern);
@@ -273,6 +274,19 @@ _pure_ static bool env_match(const char *t, const char *pattern) {
return false;
}
+static bool env_entry_has_name(const char *entry, const char *name) {
+ const char *t;
+
+ assert(entry);
+ assert(name);
+
+ t = startswith(entry, name);
+ if (!t)
+ return false;
+
+ return *t == '=';
+}
+
char **strv_env_delete(char **x, unsigned n_lists, ...) {
size_t n, i = 0;
char **k, **r;
@@ -384,6 +398,35 @@ char **strv_env_unset_many(char **l, ...) {
return l;
}
+int strv_env_replace(char ***l, char *p) {
+ char **f;
+ const char *t, *name;
+
+ assert(p);
+
+ /* Replace first occurrence of the env var or add a new one in the
+ * string list. Drop other occurences. Edits in-place. Does not copy p.
+ * p must be a valid key=value assignment.
+ */
+
+ t = strchr(p, '=');
+ assert(t);
+
+ name = strndupa(p, t - p);
+
+ for (f = *l; f && *f; f++)
+ if (env_entry_has_name(*f, name)) {
+ free_and_replace(*f, p);
+ strv_env_unset(f + 1, *f);
+ return 0;
+ }
+
+ /* We didn't find a match, we need to append p or create a new strv */
+ if (strv_push(l, p) < 0)
+ return -ENOMEM;
+ return 1;
+}
+
char **strv_env_set(char **x, const char *p) {
char **k, **r;
@@ -411,7 +454,7 @@ fail:
return NULL;
}
-char *strv_env_get_n(char **l, const char *name, size_t k) {
+char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) {
char **i;
assert(name);
@@ -419,18 +462,25 @@ char *strv_env_get_n(char **l, const char *name, size_t k) {
if (k <= 0)
return NULL;
- STRV_FOREACH(i, l)
+ STRV_FOREACH_BACKWARDS(i, l)
if (strneq(*i, name, k) &&
(*i)[k] == '=')
return *i + k + 1;
+ if (flags & REPLACE_ENV_USE_ENVIRONMENT) {
+ const char *t;
+
+ t = strndupa(name, k);
+ return getenv(t);
+ };
+
return NULL;
}
char *strv_env_get(char **l, const char *name) {
assert(name);
- return strv_env_get_n(l, name, strlen(name));
+ return strv_env_get_n(l, name, strlen(name), 0);
}
char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) {
@@ -469,19 +519,26 @@ char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const cha
return e;
}
-char *replace_env(const char *format, char **env) {
+char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) {
enum {
WORD,
CURLY,
- VARIABLE
+ VARIABLE,
+ VARIABLE_RAW,
+ TEST,
+ DEFAULT_VALUE,
+ ALTERNATE_VALUE,
} state = WORD;
- const char *e, *word = format;
- char *r = NULL, *k;
+ const char *e, *word = format, *test_value;
+ char *k;
+ _cleanup_free_ char *r = NULL;
+ size_t i, len;
+ int nest = 0;
assert(format);
- for (e = format; *e; e ++) {
+ for (e = format, i = 0; *e && i < n; e ++, i ++) {
switch (state) {
@@ -494,24 +551,36 @@ char *replace_env(const char *format, char **env) {
if (*e == '{') {
k = strnappend(r, word, e-word-1);
if (!k)
- goto fail;
+ return NULL;
free(r);
r = k;
word = e-1;
state = VARIABLE;
-
+ nest++;
} else if (*e == '$') {
k = strnappend(r, word, e-word);
if (!k)
- goto fail;
+ return NULL;
free(r);
r = k;
word = e+1;
state = WORD;
+
+ } else if (flags & REPLACE_ENV_ALLOW_BRACELESS && strchr(VALID_CHARS_ENV_NAME, *e)) {
+ k = strnappend(r, word, e-word-1);
+ if (!k)
+ return NULL;
+
+ free(r);
+ r = k;
+
+ word = e-1;
+ state = VARIABLE_RAW;
+
} else
state = WORD;
break;
@@ -520,31 +589,109 @@ char *replace_env(const char *format, char **env) {
if (*e == '}') {
const char *t;
- t = strempty(strv_env_get_n(env, word+2, e-word-2));
+ t = strv_env_get_n(env, word+2, e-word-2, flags);
k = strappend(r, t);
if (!k)
- goto fail;
+ return NULL;
free(r);
r = k;
word = e+1;
state = WORD;
+ } else if (*e == ':') {
+ if (!(flags & REPLACE_ENV_ALLOW_EXTENDED))
+ /* Treat this as unsupported syntax, i.e. do no replacement */
+ state = WORD;
+ else {
+ len = e-word-2;
+ state = TEST;
+ }
+ }
+ break;
+
+ case TEST:
+ if (*e == '-')
+ state = DEFAULT_VALUE;
+ else if (*e == '+')
+ state = ALTERNATE_VALUE;
+ else {
+ state = WORD;
+ break;
+ }
+
+ test_value = e+1;
+ break;
+
+ case DEFAULT_VALUE: /* fall through */
+ case ALTERNATE_VALUE:
+ assert(flags & REPLACE_ENV_ALLOW_EXTENDED);
+
+ if (*e == '{') {
+ nest++;
+ break;
+ }
+
+ if (*e != '}')
+ break;
+
+ nest--;
+ if (nest == 0) {
+ const char *t;
+ _cleanup_free_ char *v = NULL;
+
+ t = strv_env_get_n(env, word+2, len, flags);
+
+ if (t && state == ALTERNATE_VALUE)
+ t = v = replace_env_n(test_value, e-test_value, env, flags);
+ else if (!t && state == DEFAULT_VALUE)
+ t = v = replace_env_n(test_value, e-test_value, env, flags);
+
+ k = strappend(r, t);
+ if (!k)
+ return NULL;
+
+ free(r);
+ r = k;
+
+ word = e+1;
+ state = WORD;
+ }
+ break;
+
+ case VARIABLE_RAW:
+ assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
+
+ if (!strchr(VALID_CHARS_ENV_NAME, *e)) {
+ const char *t;
+
+ t = strv_env_get_n(env, word+1, e-word-1, flags);
+
+ k = strappend(r, t);
+ if (!k)
+ return NULL;
+
+ free(r);
+ r = k;
+
+ word = e--;
+ i--;
+ state = WORD;
}
break;
}
}
- k = strnappend(r, word, e-word);
- if (!k)
- goto fail;
+ if (state == VARIABLE_RAW) {
+ const char *t;
- free(r);
- return k;
+ assert(flags & REPLACE_ENV_ALLOW_BRACELESS);
-fail:
- return mfree(r);
+ t = strv_env_get_n(env, word+1, e-word-1, flags);
+ return strappend(r, t);
+ } else
+ return strnappend(r, word, e-word);
}
char **replace_env_argv(char **argv, char **env) {
@@ -600,7 +747,7 @@ char **replace_env_argv(char **argv, char **env) {
}
/* If ${FOO} appears as part of a word, replace it by the variable as-is */
- ret[k] = replace_env(*i, env);
+ ret[k] = replace_env(*i, env, 0);
if (!ret[k]) {
strv_free(ret);
return NULL;
@@ -621,3 +768,39 @@ int getenv_bool(const char *p) {
return parse_boolean(e);
}
+
+int serialize_environment(FILE *f, char **environment) {
+ char **e;
+
+ STRV_FOREACH(e, environment) {
+ _cleanup_free_ char *ce;
+
+ ce = cescape(*e);
+ if (!ce)
+ return -ENOMEM;
+
+ fprintf(f, "env=%s\n", *e);
+ }
+
+ /* caller should call ferror() */
+
+ return 0;
+}
+
+int deserialize_environment(char ***environment, const char *line) {
+ char *uce = NULL;
+ int r;
+
+ assert(line);
+ assert(environment);
+
+ assert(startswith(line, "env="));
+ r = cunescape(line + 4, UNESCAPE_RELAX, &uce);
+ if (r < 0)
+ return r;
+
+ if (!env_assignment_is_valid(uce))
+ return -EINVAL;
+
+ return strv_env_replace(environment, uce);
+}
diff --git a/src/basic/env-util.h b/src/basic/env-util.h
index b1fef704c2..e88fa6aac0 100644
--- a/src/basic/env-util.h
+++ b/src/basic/env-util.h
@@ -21,6 +21,7 @@
#include <stdbool.h>
#include <stddef.h>
+#include <stdio.h>
#include "macro.h"
@@ -28,9 +29,19 @@ bool env_name_is_valid(const char *e);
bool env_value_is_valid(const char *e);
bool env_assignment_is_valid(const char *e);
-char *replace_env(const char *format, char **env);
+enum {
+ REPLACE_ENV_USE_ENVIRONMENT = 1u,
+ REPLACE_ENV_ALLOW_BRACELESS = 2u,
+ REPLACE_ENV_ALLOW_EXTENDED = 4u,
+};
+
+char *replace_env_n(const char *format, size_t n, char **env, unsigned flags);
char **replace_env_argv(char **argv, char **env);
+static inline char *replace_env(const char *format, char **env, unsigned flags) {
+ return replace_env_n(format, strlen(format), env, flags);
+}
+
bool strv_env_is_valid(char **e);
#define strv_env_clean(l) strv_env_clean_with_callback(l, NULL, NULL)
char **strv_env_clean_with_callback(char **l, void (*invalid_callback)(const char *p, void *userdata), void *userdata);
@@ -44,8 +55,12 @@ char **strv_env_delete(char **x, unsigned n_lists, ...); /* New copy */
char **strv_env_set(char **x, const char *p); /* New copy ... */
char **strv_env_unset(char **l, const char *p); /* In place ... */
char **strv_env_unset_many(char **l, ...) _sentinel_;
+int strv_env_replace(char ***l, char *p); /* In place ... */
-char *strv_env_get_n(char **l, const char *name, size_t k) _pure_;
+char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) _pure_;
char *strv_env_get(char **x, const char *n) _pure_;
int getenv_bool(const char *p);
+
+int serialize_environment(FILE *f, char **environment);
+int deserialize_environment(char ***environment, const char *line);
diff --git a/src/basic/errno-list.c b/src/basic/errno-list.c
index 31b66bad5e..c6a01eec8b 100644
--- a/src/basic/errno-list.c
+++ b/src/basic/errno-list.c
@@ -23,7 +23,7 @@
#include "macro.h"
static const struct errno_name* lookup_errno(register const char *str,
- register unsigned int len);
+ register GPERF_LEN_TYPE len);
#include "errno-from-name.h"
#include "errno-to-name.h"
diff --git a/src/basic/exec-util.c b/src/basic/exec-util.c
new file mode 100644
index 0000000000..aced9e8e3d
--- /dev/null
+++ b/src/basic/exec-util.c
@@ -0,0 +1,360 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2010 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dirent.h>
+#include <errno.h>
+#include <sys/prctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include "alloc-util.h"
+#include "conf-files.h"
+#include "env-util.h"
+#include "exec-util.h"
+#include "fd-util.h"
+#include "fileio.h"
+#include "hashmap.h"
+#include "macro.h"
+#include "process-util.h"
+#include "set.h"
+#include "signal-util.h"
+#include "stat-util.h"
+#include "string-util.h"
+#include "strv.h"
+#include "terminal-util.h"
+#include "util.h"
+
+/* Put this test here for a lack of better place */
+assert_cc(EAGAIN == EWOULDBLOCK);
+
+static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) {
+
+ pid_t _pid;
+
+ if (null_or_empty_path(path)) {
+ log_debug("%s is empty (a mask).", path);
+ return 0;
+ }
+
+ _pid = fork();
+ if (_pid < 0)
+ return log_error_errno(errno, "Failed to fork: %m");
+ if (_pid == 0) {
+ char *_argv[2];
+
+ assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
+
+ if (stdout_fd >= 0) {
+ /* If the fd happens to be in the right place, go along with that */
+ if (stdout_fd != STDOUT_FILENO &&
+ dup2(stdout_fd, STDOUT_FILENO) < 0)
+ return -errno;
+
+ fd_cloexec(STDOUT_FILENO, false);
+ }
+
+ if (!argv) {
+ _argv[0] = (char*) path;
+ _argv[1] = NULL;
+ argv = _argv;
+ } else
+ argv[0] = (char*) path;
+
+ execv(path, argv);
+ log_error_errno(errno, "Failed to execute %s: %m", path);
+ _exit(EXIT_FAILURE);
+ }
+
+ log_debug("Spawned %s as " PID_FMT ".", path, _pid);
+ *pid = _pid;
+ return 1;
+}
+
+static int do_execute(
+ char **directories,
+ usec_t timeout,
+ gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
+ void* const callback_args[_STDOUT_CONSUME_MAX],
+ int output_fd,
+ char *argv[]) {
+
+ _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
+ _cleanup_strv_free_ char **paths = NULL;
+ char **path;
+ int r;
+
+ /* We fork this all off from a child process so that we can somewhat cleanly make
+ * use of SIGALRM to set a time limit.
+ *
+ * If callbacks is nonnull, execution is serial. Otherwise, we default to parallel.
+ */
+
+ (void) reset_all_signal_handlers();
+ (void) reset_signal_mask();
+
+ assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
+
+ r = conf_files_list_strv(&paths, NULL, NULL, (const char* const*) directories);
+ if (r < 0)
+ return r;
+
+ if (!callbacks) {
+ pids = hashmap_new(NULL);
+ if (!pids)
+ return log_oom();
+ }
+
+ /* Abort execution of this process after the timout. We simply rely on SIGALRM as
+ * default action terminating the process, and turn on alarm(). */
+
+ if (timeout != USEC_INFINITY)
+ alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
+
+ STRV_FOREACH(path, paths) {
+ _cleanup_free_ char *t = NULL;
+ _cleanup_close_ int fd = -1;
+ pid_t pid;
+
+ t = strdup(*path);
+ if (!t)
+ return log_oom();
+
+ if (callbacks) {
+ fd = open_serialization_fd(basename(*path));
+ if (fd < 0)
+ return log_error_errno(fd, "Failed to open serialization file: %m");
+ }
+
+ r = do_spawn(t, argv, fd, &pid);
+ if (r <= 0)
+ continue;
+
+ if (pids) {
+ r = hashmap_put(pids, PID_TO_PTR(pid), t);
+ if (r < 0)
+ return log_oom();
+ t = NULL;
+ } else {
+ r = wait_for_terminate_and_warn(t, pid, true);
+ if (r < 0)
+ continue;
+
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ return log_error_errno(errno, "Failed to seek on serialization fd: %m");
+
+ r = callbacks[STDOUT_GENERATE](fd, callback_args[STDOUT_GENERATE]);
+ fd = -1;
+ if (r < 0)
+ return log_error_errno(r, "Failed to process output from %s: %m", *path);
+ }
+ }
+
+ if (callbacks) {
+ r = callbacks[STDOUT_COLLECT](output_fd, callback_args[STDOUT_COLLECT]);
+ if (r < 0)
+ return log_error_errno(r, "Callback two failed: %m");
+ }
+
+ while (!hashmap_isempty(pids)) {
+ _cleanup_free_ char *t = NULL;
+ pid_t pid;
+
+ pid = PTR_TO_PID(hashmap_first_key(pids));
+ assert(pid > 0);
+
+ t = hashmap_remove(pids, PID_TO_PTR(pid));
+ assert(t);
+
+ wait_for_terminate_and_warn(t, pid, true);
+ }
+
+ return 0;
+}
+
+int execute_directories(
+ const char* const* directories,
+ usec_t timeout,
+ gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
+ void* const callback_args[_STDOUT_CONSUME_MAX],
+ char *argv[]) {
+
+ pid_t executor_pid;
+ char *name;
+ char **dirs = (char**) directories;
+ _cleanup_close_ int fd = -1;
+ int r;
+
+ assert(!strv_isempty(dirs));
+
+ name = basename(dirs[0]);
+ assert(!isempty(name));
+
+ if (callbacks) {
+ assert(callback_args);
+ assert(callbacks[STDOUT_GENERATE]);
+ assert(callbacks[STDOUT_COLLECT]);
+ assert(callbacks[STDOUT_CONSUME]);
+
+ fd = open_serialization_fd(name);
+ if (fd < 0)
+ return log_error_errno(fd, "Failed to open serialization file: %m");
+ }
+
+ /* Executes all binaries in the directories serially or in parallel and waits for
+ * them to finish. Optionally a timeout is applied. If a file with the same name
+ * exists in more than one directory, the earliest one wins. */
+
+ executor_pid = fork();
+ if (executor_pid < 0)
+ return log_error_errno(errno, "Failed to fork: %m");
+
+ if (executor_pid == 0) {
+ r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv);
+ _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
+ }
+
+ r = wait_for_terminate_and_warn(name, executor_pid, true);
+ if (r < 0)
+ return log_error_errno(r, "Execution failed: %m");
+ if (r > 0) {
+ /* non-zero return code from child */
+ log_error("Forker process failed.");
+ return -EREMOTEIO;
+ }
+
+ if (!callbacks)
+ return 0;
+
+ if (lseek(fd, 0, SEEK_SET) < 0)
+ return log_error_errno(errno, "Failed to rewind serialization fd: %m");
+
+ r = callbacks[STDOUT_CONSUME](fd, callback_args[STDOUT_CONSUME]);
+ fd = -1;
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse returned data: %m");
+ return 0;
+}
+
+static int gather_environment_generate(int fd, void *arg) {
+ char ***env = arg, **x, **y;
+ _cleanup_fclose_ FILE *f = NULL;
+ _cleanup_strv_free_ char **new;
+ int r;
+
+ /* Read a series of VAR=value assignments from fd, use them to update the list of
+ * variables in env. Also update the exported environment.
+ *
+ * fd is always consumed, even on error.
+ */
+
+ assert(env);
+
+ f = fdopen(fd, "r");
+ if (!f) {
+ safe_close(fd);
+ return -errno;
+ }
+
+ r = load_env_file_pairs(f, NULL, NULL, &new);
+ if (r < 0)
+ return r;
+
+ STRV_FOREACH_PAIR(x, y, new) {
+ char *p;
+
+ if (!env_name_is_valid(*x)) {
+ log_warning("Invalid variable assignment \"%s=...\", ignoring.", *x);
+ continue;
+ }
+
+ p = strjoin(*x, "=", *y);
+ if (!p)
+ return -ENOMEM;
+
+ r = strv_env_replace(env, p);
+ if (r < 0)
+ return r;
+
+ if (setenv(*x, *y, true) < 0)
+ return -errno;
+ }
+
+ return r;
+}
+
+static int gather_environment_collect(int fd, void *arg) {
+ char ***env = arg;
+ _cleanup_fclose_ FILE *f = NULL;
+ int r;
+
+ /* Write out a series of env=cescape(VAR=value) assignments to fd. */
+
+ assert(env);
+
+ f = fdopen(fd, "w");
+ if (!f) {
+ safe_close(fd);
+ return -errno;
+ }
+
+ r = serialize_environment(f, *env);
+ if (r < 0)
+ return r;
+
+ if (ferror(f))
+ return errno > 0 ? -errno : -EIO;
+
+ return 0;
+}
+
+static int gather_environment_consume(int fd, void *arg) {
+ char ***env = arg;
+ _cleanup_fclose_ FILE *f = NULL;
+ char line[LINE_MAX];
+ int r = 0, k;
+
+ /* Read a series of env=cescape(VAR=value) assignments from fd into env. */
+
+ assert(env);
+
+ f = fdopen(fd, "r");
+ if (!f) {
+ safe_close(fd);
+ return -errno;
+ }
+
+ FOREACH_LINE(line, f, return -EIO) {
+ truncate_nl(line);
+
+ k = deserialize_environment(env, line);
+ if (k < 0)
+ log_error_errno(k, "Invalid line \"%s\": %m", line);
+ if (k < 0 && r == 0)
+ r = k;
+ }
+
+ return r;
+}
+
+const gather_stdout_callback_t gather_environment[] = {
+ gather_environment_generate,
+ gather_environment_collect,
+ gather_environment_consume,
+};
diff --git a/src/basic/exec-util.h b/src/basic/exec-util.h
new file mode 100644
index 0000000000..72009799b2
--- /dev/null
+++ b/src/basic/exec-util.h
@@ -0,0 +1,40 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2017 Zbigniew Jędrzejewski-Szmek
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+
+#include "time-util.h"
+
+typedef int (*gather_stdout_callback_t) (int fd, void *arg);
+
+enum {
+ STDOUT_GENERATE, /* from generators to helper process */
+ STDOUT_COLLECT, /* from helper process to main process */
+ STDOUT_CONSUME, /* process data in main process */
+ _STDOUT_CONSUME_MAX,
+};
+
+int execute_directories(
+ const char* const* directories,
+ usec_t timeout,
+ gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
+ void* const callback_args[_STDOUT_CONSUME_MAX],
+ char *argv[]);
+
+extern const gather_stdout_callback_t gather_environment[_STDOUT_CONSUME_MAX];
diff --git a/src/basic/exit-status.c b/src/basic/exit-status.c
index 59557f8afe..1e23c32c3f 100644
--- a/src/basic/exit-status.c
+++ b/src/basic/exit-status.c
@@ -148,6 +148,9 @@ const char* exit_status_to_string(int status, ExitStatusLevel level) {
case EXIT_SMACK_PROCESS_LABEL:
return "SMACK_PROCESS_LABEL";
+
+ case EXIT_KEYRING:
+ return "KEYRING";
}
}
diff --git a/src/basic/exit-status.h b/src/basic/exit-status.h
index 0cfdfd7891..d22b2c00e4 100644
--- a/src/basic/exit-status.h
+++ b/src/basic/exit-status.h
@@ -82,6 +82,7 @@ enum {
EXIT_MAKE_STARTER,
EXIT_CHOWN,
EXIT_SMACK_PROCESS_LABEL,
+ EXIT_KEYRING,
};
typedef enum ExitStatusLevel {
diff --git a/src/basic/extract-word.c b/src/basic/extract-word.c
index d6c1228463..f8cac3e911 100644
--- a/src/basic/extract-word.c
+++ b/src/basic/extract-word.c
@@ -48,7 +48,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
/* Bail early if called after last value or with no input */
if (!*p)
- goto finish_force_terminate;
+ goto finish;
c = **p;
if (!separators)
@@ -227,8 +227,8 @@ int extract_first_word_and_warn(
*p = save;
r = extract_first_word(p, ret, separators, flags|EXTRACT_CUNESCAPE_RELAX);
if (r >= 0) {
- /* It worked this time, hence it must have been an invalid escape sequence we could correct. */
- log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Invalid escape sequences in line, correcting: \"%s\"", rvalue);
+ /* It worked this time, hence it must have been an invalid escape sequence. */
+ log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Ignoring unknown escape sequences: \"%s\"", *ret);
return r;
}
diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c
index 5c820332a5..19ad20789b 100644
--- a/src/basic/fd-util.c
+++ b/src/basic/fd-util.c
@@ -24,6 +24,7 @@
#include <sys/stat.h>
#include <unistd.h>
+#include "dirent-util.h"
#include "fd-util.h"
#include "fs-util.h"
#include "macro.h"
@@ -234,12 +235,9 @@ int close_all_fds(const int except[], unsigned n_except) {
return r;
}
- while ((de = readdir(d))) {
+ FOREACH_DIRENT(de, d, return -errno) {
int fd = -1;
- if (hidden_or_backup_file(de->d_name))
- continue;
-
if (safe_atoi(de->d_name, &fd) < 0)
/* Let's better ignore this, just in case */
continue;
diff --git a/src/basic/fileio.c b/src/basic/fileio.c
index 1cfb7a98f5..7c2c2b38f5 100644
--- a/src/basic/fileio.c
+++ b/src/basic/fileio.c
@@ -30,6 +30,7 @@
#include "alloc-util.h"
#include "ctype.h"
+#include "env-util.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
@@ -553,13 +554,14 @@ static int parse_env_file_internal(
}
}
- if (state == PRE_VALUE ||
- state == VALUE ||
- state == VALUE_ESCAPE ||
- state == SINGLE_QUOTE_VALUE ||
- state == SINGLE_QUOTE_VALUE_ESCAPE ||
- state == DOUBLE_QUOTE_VALUE ||
- state == DOUBLE_QUOTE_VALUE_ESCAPE) {
+ if (IN_SET(state,
+ PRE_VALUE,
+ VALUE,
+ VALUE_ESCAPE,
+ SINGLE_QUOTE_VALUE,
+ SINGLE_QUOTE_VALUE_ESCAPE,
+ DOUBLE_QUOTE_VALUE,
+ DOUBLE_QUOTE_VALUE_ESCAPE)) {
key[n_key] = 0;
@@ -586,14 +588,9 @@ fail:
return r;
}
-static int parse_env_file_push(
+static int check_utf8ness_and_warn(
const char *filename, unsigned line,
- const char *key, char *value,
- void *userdata,
- int *n_pushed) {
-
- const char *k;
- va_list aq, *ap = userdata;
+ const char *key, char *value) {
if (!utf8_is_valid(key)) {
_cleanup_free_ char *p = NULL;
@@ -611,6 +608,23 @@ static int parse_env_file_push(
return -EINVAL;
}
+ return 0;
+}
+
+static int parse_env_file_push(
+ const char *filename, unsigned line,
+ const char *key, char *value,
+ void *userdata,
+ int *n_pushed) {
+
+ const char *k;
+ va_list aq, *ap = userdata;
+ int r;
+
+ r = check_utf8ness_and_warn(filename, line, key, value);
+ if (r < 0)
+ return r;
+
va_copy(aq, *ap);
while ((k = va_arg(aq, const char *))) {
@@ -662,27 +676,19 @@ static int load_env_file_push(
char *p;
int r;
- if (!utf8_is_valid(key)) {
- _cleanup_free_ char *t = utf8_escape_invalid(key);
-
- log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
- return -EINVAL;
- }
-
- if (value && !utf8_is_valid(value)) {
- _cleanup_free_ char *t = utf8_escape_invalid(value);
-
- log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
- return -EINVAL;
- }
+ r = check_utf8ness_and_warn(filename, line, key, value);
+ if (r < 0)
+ return r;
- p = strjoin(key, "=", strempty(value), NULL);
+ p = strjoin(key, "=", value);
if (!p)
return -ENOMEM;
- r = strv_consume(m, p);
- if (r < 0)
+ r = strv_env_replace(m, p);
+ if (r < 0) {
+ free(p);
return r;
+ }
if (n_pushed)
(*n_pushed)++;
@@ -716,19 +722,9 @@ static int load_env_file_push_pairs(
char ***m = userdata;
int r;
- if (!utf8_is_valid(key)) {
- _cleanup_free_ char *t = utf8_escape_invalid(key);
-
- log_error("%s:%u: invalid UTF-8 for key '%s', ignoring.", strna(filename), line, t);
- return -EINVAL;
- }
-
- if (value && !utf8_is_valid(value)) {
- _cleanup_free_ char *t = utf8_escape_invalid(value);
-
- log_error("%s:%u: invalid UTF-8 value for key %s: '%s', ignoring.", strna(filename), line, key, t);
- return -EINVAL;
- }
+ r = check_utf8ness_and_warn(filename, line, key, value);
+ if (r < 0)
+ return r;
r = strv_extend(m, key);
if (r < 0)
@@ -767,6 +763,52 @@ int load_env_file_pairs(FILE *f, const char *fname, const char *newline, char **
return 0;
}
+static int merge_env_file_push(
+ const char *filename, unsigned line,
+ const char *key, char *value,
+ void *userdata,
+ int *n_pushed) {
+
+ char ***env = userdata;
+ char *expanded_value;
+
+ assert(env);
+
+ if (!value) {
+ log_error("%s:%u: invalid syntax (around \"%s\"), ignoring.", strna(filename), line, key);
+ return 0;
+ }
+
+ if (!env_name_is_valid(key)) {
+ log_error("%s:%u: invalid variable name \"%s\", ignoring.", strna(filename), line, key);
+ free(value);
+ return 0;
+ }
+
+ expanded_value = replace_env(value, *env,
+ REPLACE_ENV_USE_ENVIRONMENT|
+ REPLACE_ENV_ALLOW_BRACELESS|
+ REPLACE_ENV_ALLOW_EXTENDED);
+ if (!expanded_value)
+ return -ENOMEM;
+
+ free_and_replace(value, expanded_value);
+
+ return load_env_file_push(filename, line, key, value, env, n_pushed);
+}
+
+int merge_env_file(
+ char ***env,
+ FILE *f,
+ const char *fname) {
+
+ /* NOTE: this function supports braceful and braceless variable expansions,
+ * plus "extended" substitutions, unlike other exported parsing functions.
+ */
+
+ return parse_env_file_internal(f, fname, NEWLINE, merge_env_file_push, env, NULL);
+}
+
static void write_env_var(FILE *f, const char *v) {
const char *p;
@@ -963,9 +1005,9 @@ static int search_and_fopen_internal(const char *path, const char *mode, const c
FILE *f;
if (root)
- p = strjoin(root, *i, "/", path, NULL);
+ p = strjoin(root, *i, "/", path);
else
- p = strjoin(*i, "/", path, NULL);
+ p = strjoin(*i, "/", path);
if (!p)
return -ENOMEM;
@@ -1342,6 +1384,25 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) {
return fd;
}
+int open_serialization_fd(const char *ident) {
+ int fd = -1;
+
+ fd = memfd_create(ident, MFD_CLOEXEC);
+ if (fd < 0) {
+ const char *path;
+
+ path = getpid() == 1 ? "/run/systemd" : "/tmp";
+ fd = open_tmpfile_unlinkable(path, O_RDWR|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ log_debug("Serializing %s to %s.", ident, path);
+ } else
+ log_debug("Serializing %s to memfd.", ident);
+
+ return fd;
+}
+
int link_tmpfile(int fd, const char *path, const char *target) {
assert(fd >= 0);
@@ -1409,3 +1470,22 @@ int read_nul_string(FILE *f, char **ret) {
return 0;
}
+
+int mkdtemp_malloc(const char *template, char **ret) {
+ char *p;
+
+ assert(template);
+ assert(ret);
+
+ p = strdup(template);
+ if (!p)
+ return -ENOMEM;
+
+ if (!mkdtemp(p)) {
+ free(p);
+ return -errno;
+ }
+
+ *ret = p;
+ return 0;
+}
diff --git a/src/basic/fileio.h b/src/basic/fileio.h
index b58c83e64a..e547614cc4 100644
--- a/src/basic/fileio.h
+++ b/src/basic/fileio.h
@@ -48,6 +48,8 @@ int parse_env_file(const char *fname, const char *separator, ...) _sentinel_;
int load_env_file(FILE *f, const char *fname, const char *separator, char ***l);
int load_env_file_pairs(FILE *f, const char *fname, const char *separator, char ***l);
+int merge_env_file(char ***env, FILE *f, const char *fname);
+
int write_env_file(const char *fname, char **l);
int executable_is_script(const char *path, char **interpreter);
@@ -84,7 +86,10 @@ int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space)
int open_tmpfile_unlinkable(const char *directory, int flags);
int open_tmpfile_linkable(const char *target, int flags, char **ret_path);
+int open_serialization_fd(const char *ident);
int link_tmpfile(int fd, const char *path, const char *target);
int read_nul_string(FILE *f, char **ret);
+
+int mkdtemp_malloc(const char *template, char **ret);
diff --git a/src/basic/formats-util.h b/src/basic/format-util.h
index 39a185f59b..39a185f59b 100644
--- a/src/basic/formats-util.h
+++ b/src/basic/format-util.h
diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c
index 48952a1c26..8fe19ee4e4 100644
--- a/src/basic/fs-util.c
+++ b/src/basic/fs-util.c
@@ -17,7 +17,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <dirent.h>
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
@@ -224,25 +223,25 @@ int readlink_and_make_absolute(const char *p, char **r) {
return 0;
}
-int readlink_and_canonicalize(const char *p, char **r) {
+int readlink_and_canonicalize(const char *p, const char *root, char **ret) {
char *t, *s;
- int j;
+ int r;
assert(p);
- assert(r);
+ assert(ret);
- j = readlink_and_make_absolute(p, &t);
- if (j < 0)
- return j;
+ r = readlink_and_make_absolute(p, &t);
+ if (r < 0)
+ return r;
- s = canonicalize_file_name(t);
- if (s) {
+ r = chase_symlinks(t, root, 0, &s);
+ if (r < 0)
+ /* If we can't follow up, then let's return the original string, slightly cleaned up. */
+ *ret = path_kill_slashes(t);
+ else {
+ *ret = s;
free(t);
- *r = s;
- } else
- *r = t;
-
- path_kill_slashes(*r);
+ }
return 0;
}
@@ -446,6 +445,7 @@ int mkfifo_atomic(const char *path, mode_t mode) {
int get_files_in_directory(const char *path, char ***list) {
_cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
size_t bufsize = 0, n = 0;
_cleanup_strv_free_ char **l = NULL;
@@ -459,16 +459,7 @@ int get_files_in_directory(const char *path, char ***list) {
if (!d)
return -errno;
- for (;;) {
- struct dirent *de;
-
- errno = 0;
- de = readdir(d);
- if (!de && errno > 0)
- return -errno;
- if (!de)
- break;
-
+ FOREACH_DIRENT_ALL(de, d, return -errno) {
dirent_ensure_type(d, de);
if (!dirent_is_file(de))
@@ -598,10 +589,11 @@ 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) {
+int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret) {
_cleanup_free_ char *buffer = NULL, *done = NULL, *root = NULL;
_cleanup_close_ int fd = -1;
unsigned max_follow = 32; /* how many symlinks to follow before giving up and returning ELOOP */
+ bool exists = true;
char *todo;
int r;
@@ -610,26 +602,39 @@ int chase_symlinks(const char *path, const char *_root, char **ret) {
/* 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.
+ * Note that "root" primarily matters if we encounter an absolute symlink. It is also used when following
+ * relative symlinks to ensure they cannot be used to "escape" the root directory. The path parameter passed is
+ * assumed to be already prefixed by it, except if the CHASE_PREFIX_ROOT flag is set, in which case it is first
+ * prefixed accordingly.
*
* 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;
+ * at a minimum.
+ *
+ * Suggested usage: whenever you want to canonicalize a path, use this function. Pass the absolute path you got
+ * as-is: fully qualified and relative to your host's root. Optionally, specify the root parameter to tell this
+ * function what to do when encountering a symlink with an absolute path as directory: prefix it by the
+ * specified path.
+ *
+ * Note: there's also chase_symlinks_prefix() (see below), which as first step prefixes the passed path by the
+ * passed root. */
- if (_root) {
- r = path_make_absolute_cwd(_root, &root);
+ if (original_root) {
+ r = path_make_absolute_cwd(original_root, &root);
if (r < 0)
return r;
+
+ if (flags & CHASE_PREFIX_ROOT)
+ path = prefix_roota(root, path);
}
+ r = path_make_absolute_cwd(path, &buffer);
+ if (r < 0)
+ return r;
+
fd = open("/", O_CLOEXEC|O_NOFOLLOW|O_PATH);
if (fd < 0)
return -errno;
@@ -665,18 +670,20 @@ int chase_symlinks(const char *path, const char *_root, char **ret) {
_cleanup_free_ char *parent = NULL;
int fd_parent = -1;
+ /* If we already are at the top, then going up will not change anything. This is in-line with
+ * how the kernel handles this. */
if (isempty(done) || path_equal(done, "/"))
- return -EINVAL;
+ continue;
parent = dirname_malloc(done);
if (!parent)
return -ENOMEM;
- /* Don't allow this to leave the root dir */
+ /* Don't allow this to leave the root dir. */
if (root &&
path_startswith(done, root) &&
!path_startswith(parent, root))
- return -EINVAL;
+ continue;
free_and_replace(done, parent);
@@ -692,13 +699,32 @@ int chase_symlinks(const char *path, const char *_root, char **ret) {
/* Otherwise let's see what this is. */
child = openat(fd, first + n, O_CLOEXEC|O_NOFOLLOW|O_PATH);
- if (child < 0)
+ if (child < 0) {
+
+ if (errno == ENOENT &&
+ (flags & CHASE_NONEXISTENT) &&
+ (isempty(todo) || path_is_safe(todo))) {
+
+ /* If CHASE_NONEXISTENT is set, and the path does not exist, then that's OK, return
+ * what we got so far. But don't allow this if the remaining path contains "../ or "./"
+ * or something else weird. */
+
+ if (!strextend(&done, first, todo, NULL))
+ return -ENOMEM;
+
+ exists = false;
+ break;
+ }
+
return -errno;
+ }
if (fstat(child, &st) < 0)
return -errno;
if (S_ISLNK(st.st_mode)) {
+ char *joined;
+
_cleanup_free_ char *destination = NULL;
/* This is a symlink, in this case read the destination. But let's make sure we don't follow
@@ -722,9 +748,6 @@ int chase_symlinks(const char *path, const char *_root, char **ret) {
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. */
@@ -736,19 +759,17 @@ int chase_symlinks(const char *path, const char *_root, char **ret) {
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. */
+ /* 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;
+ joined = strjoin("/", destination, todo);
+ if (!joined)
+ return -ENOMEM;
- free(buffer);
- todo = buffer = joined;
- }
+ free(buffer);
+ todo = buffer = joined;
continue;
}
@@ -775,8 +796,10 @@ int chase_symlinks(const char *path, const char *_root, char **ret) {
return -ENOMEM;
}
- *ret = done;
- done = NULL;
+ if (ret) {
+ *ret = done;
+ done = NULL;
+ }
- return 0;
+ return exists;
}
diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h
index 31df47cf1e..094acf1799 100644
--- a/src/basic/fs-util.h
+++ b/src/basic/fs-util.h
@@ -39,7 +39,7 @@ int readlinkat_malloc(int fd, const char *p, char **ret);
int readlink_malloc(const char *p, char **r);
int readlink_value(const char *p, char **ret);
int readlink_and_make_absolute(const char *p, char **r);
-int readlink_and_canonicalize(const char *p, char **r);
+int readlink_and_canonicalize(const char *p, const char *root, char **r);
int readlink_and_make_absolute_root(const char *root, const char *path, char **ret);
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid);
@@ -78,4 +78,22 @@ 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);
+enum {
+ CHASE_PREFIX_ROOT = 1, /* If set, the specified path will be prefixed by the specified root before beginning the iteration */
+ CHASE_NONEXISTENT = 2, /* If set, it's OK if the path doesn't actually exist. */
+};
+
+int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret);
+
+/* Useful for usage with _cleanup_(), removes a directory and frees the pointer */
+static inline void rmdir_and_free(char *p) {
+ (void) rmdir(p);
+ free(p);
+}
+DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rmdir_and_free);
+
+static inline void unlink_and_free(char *p) {
+ (void) unlink(p);
+ free(p);
+}
+DEFINE_TRIVIAL_CLEANUP_FUNC(char*, unlink_and_free);
diff --git a/src/basic/hexdecoct.c b/src/basic/hexdecoct.c
index c5bda6c4d6..2d6e377f0a 100644
--- a/src/basic/hexdecoct.c
+++ b/src/basic/hexdecoct.c
@@ -72,10 +72,10 @@ int unhexchar(char c) {
}
char *hexmem(const void *p, size_t l) {
- char *r, *z;
const uint8_t *x;
+ char *r, *z;
- z = r = malloc(l * 2 + 1);
+ z = r = new(char, l * 2 + 1);
if (!r)
return NULL;
@@ -97,6 +97,9 @@ int unhexmem(const char *p, size_t l, void **mem, size_t *len) {
assert(len);
assert(p);
+ if (l % 2 != 0)
+ return -EINVAL;
+
z = r = malloc((l + 1) / 2 + 1);
if (!r)
return -ENOMEM;
@@ -107,12 +110,10 @@ int unhexmem(const char *p, size_t l, void **mem, size_t *len) {
a = unhexchar(x[0]);
if (a < 0)
return a;
- else if (x+1 < p + l) {
- b = unhexchar(x[1]);
- if (b < 0)
- return b;
- } else
- b = 0;
+
+ b = unhexchar(x[1]);
+ if (b < 0)
+ return b;
*(z++) = (uint8_t) a << 4 | (uint8_t) b;
}
diff --git a/src/basic/hostname-util.c b/src/basic/hostname-util.c
index e44a357287..a94037b303 100644
--- a/src/basic/hostname-util.c
+++ b/src/basic/hostname-util.c
@@ -55,7 +55,7 @@ char* gethostname_malloc(void) {
assert_se(uname(&u) >= 0);
if (isempty(u.nodename) || streq(u.nodename, "(none)"))
- return strdup(u.sysname);
+ return strdup(FALLBACK_HOSTNAME);
return strdup(u.nodename);
}
diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c
index aa7ccd1afd..3927df2955 100644
--- a/src/basic/in-addr-util.c
+++ b/src/basic/in-addr-util.c
@@ -31,15 +31,9 @@
#include "util.h"
bool in4_addr_is_null(const struct in_addr *a) {
- return a->s_addr == 0;
-}
+ assert(a);
-bool in6_addr_is_null(const struct in6_addr *a) {
- return
- a->s6_addr32[0] == 0 &&
- a->s6_addr32[1] == 0 &&
- a->s6_addr32[2] == 0 &&
- a->s6_addr32[3] == 0;
+ return a->s_addr == 0;
}
int in_addr_is_null(int family, const union in_addr_union *u) {
@@ -49,16 +43,22 @@ int in_addr_is_null(int family, const union in_addr_union *u) {
return in4_addr_is_null(&u->in);
if (family == AF_INET6)
- return in6_addr_is_null(&u->in6);
+ return IN6_IS_ADDR_UNSPECIFIED(&u->in6);
return -EAFNOSUPPORT;
}
+bool in4_addr_is_link_local(const struct in_addr *a) {
+ assert(a);
+
+ return (be32toh(a->s_addr) & UINT32_C(0xFFFF0000)) == (UINT32_C(169) << 24 | UINT32_C(254) << 16);
+}
+
int in_addr_is_link_local(int family, const union in_addr_union *u) {
assert(u);
if (family == AF_INET)
- return (be32toh(u->in.s_addr) & UINT32_C(0xFFFF0000)) == (UINT32_C(169) << 24 | UINT32_C(254) << 16);
+ return in4_addr_is_link_local(&u->in);
if (family == AF_INET6)
return IN6_IS_ADDR_LINKLOCAL(&u->in6);
@@ -66,12 +66,30 @@ int in_addr_is_link_local(int family, const union in_addr_union *u) {
return -EAFNOSUPPORT;
}
+int in_addr_is_multicast(int family, const union in_addr_union *u) {
+ assert(u);
+
+ if (family == AF_INET)
+ return IN_MULTICAST(be32toh(u->in.s_addr));
+
+ if (family == AF_INET6)
+ return IN6_IS_ADDR_MULTICAST(&u->in6);
+
+ return -EAFNOSUPPORT;
+}
+
+bool in4_addr_is_localhost(const struct in_addr *a) {
+ assert(a);
+
+ /* All of 127.x.x.x is localhost. */
+ return (be32toh(a->s_addr) & UINT32_C(0xFF000000)) == UINT32_C(127) << 24;
+}
+
int in_addr_is_localhost(int family, const union in_addr_union *u) {
assert(u);
if (family == AF_INET)
- /* All of 127.x.x.x is localhost. */
- return (be32toh(u->in.s_addr) & UINT32_C(0xFF000000)) == UINT32_C(127) << 24;
+ return in4_addr_is_localhost(&u->in);
if (family == AF_INET6)
return IN6_IS_ADDR_LOOPBACK(&u->in6);
@@ -277,15 +295,14 @@ fallback:
}
int in_addr_from_string(int family, const char *s, union in_addr_union *ret) {
-
+ union in_addr_union buffer;
assert(s);
- assert(ret);
if (!IN_SET(family, AF_INET, AF_INET6))
return -EAFNOSUPPORT;
errno = 0;
- if (inet_pton(family, s, ret) <= 0)
+ if (inet_pton(family, s, ret ?: &buffer) <= 0)
return errno > 0 ? -errno : -EINVAL;
return 0;
@@ -295,18 +312,18 @@ int in_addr_from_string_auto(const char *s, int *family, union in_addr_union *re
int r;
assert(s);
- assert(family);
- assert(ret);
r = in_addr_from_string(AF_INET, s, ret);
if (r >= 0) {
- *family = AF_INET;
+ if (family)
+ *family = AF_INET;
return 0;
}
r = in_addr_from_string(AF_INET6, s, ret);
if (r >= 0) {
- *family = AF_INET6;
+ if (family)
+ *family = AF_INET6;
return 0;
}
diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h
index d60064aef8..51a5aa67e4 100644
--- a/src/basic/in-addr-util.h
+++ b/src/basic/in-addr-util.h
@@ -37,11 +37,16 @@ struct in_addr_data {
};
bool in4_addr_is_null(const struct in_addr *a);
-bool in6_addr_is_null(const struct in6_addr *a);
-
int in_addr_is_null(int family, const union in_addr_union *u);
+
+int in_addr_is_multicast(int family, const union in_addr_union *u);
+
+bool in4_addr_is_link_local(const struct in_addr *a);
int in_addr_is_link_local(int family, const union in_addr_union *u);
+
+bool in4_addr_is_localhost(const struct in_addr *a);
int in_addr_is_localhost(int family, const union in_addr_union *u);
+
int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b);
int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen);
int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen);
diff --git a/src/basic/journal-importer.c b/src/basic/journal-importer.c
new file mode 100644
index 0000000000..4c13e46a49
--- /dev/null
+++ b/src/basic/journal-importer.c
@@ -0,0 +1,481 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2014 Zbigniew Jędrzejewski-Szmek
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <unistd.h>
+
+#include "alloc-util.h"
+#include "journal-importer.h"
+#include "fd-util.h"
+#include "parse-util.h"
+#include "string-util.h"
+
+enum {
+ IMPORTER_STATE_LINE = 0, /* waiting to read, or reading line */
+ IMPORTER_STATE_DATA_START, /* reading binary data header */
+ IMPORTER_STATE_DATA, /* reading binary data */
+ IMPORTER_STATE_DATA_FINISH, /* expecting newline */
+ IMPORTER_STATE_EOF, /* done */
+};
+
+static int iovw_put(struct iovec_wrapper *iovw, void* data, size_t len) {
+ if (!GREEDY_REALLOC(iovw->iovec, iovw->size_bytes, iovw->count + 1))
+ return log_oom();
+
+ iovw->iovec[iovw->count++] = (struct iovec) {data, len};
+ return 0;
+}
+
+static void iovw_free_contents(struct iovec_wrapper *iovw) {
+ iovw->iovec = mfree(iovw->iovec);
+ iovw->size_bytes = iovw->count = 0;
+}
+
+static void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new) {
+ size_t i;
+
+ for (i = 0; i < iovw->count; i++)
+ iovw->iovec[i].iov_base = (char*) iovw->iovec[i].iov_base - old + new;
+}
+
+size_t iovw_size(struct iovec_wrapper *iovw) {
+ size_t n = 0, i;
+
+ for (i = 0; i < iovw->count; i++)
+ n += iovw->iovec[i].iov_len;
+
+ return n;
+}
+
+void journal_importer_cleanup(JournalImporter *imp) {
+ if (imp->fd >= 0 && !imp->passive_fd) {
+ log_debug("Closing %s (fd=%d)", imp->name ?: "importer", imp->fd);
+ safe_close(imp->fd);
+ }
+
+ free(imp->buf);
+ iovw_free_contents(&imp->iovw);
+}
+
+static char* realloc_buffer(JournalImporter *imp, size_t size) {
+ char *b, *old = imp->buf;
+
+ b = GREEDY_REALLOC(imp->buf, imp->size, size);
+ if (!b)
+ return NULL;
+
+ iovw_rebase(&imp->iovw, old, imp->buf);
+
+ return b;
+}
+
+static int get_line(JournalImporter *imp, char **line, size_t *size) {
+ ssize_t n;
+ char *c = NULL;
+
+ assert(imp);
+ assert(imp->state == IMPORTER_STATE_LINE);
+ assert(imp->offset <= imp->filled);
+ assert(imp->filled <= imp->size);
+ assert(imp->buf == NULL || imp->size > 0);
+ assert(imp->fd >= 0);
+
+ for (;;) {
+ if (imp->buf) {
+ size_t start = MAX(imp->scanned, imp->offset);
+
+ c = memchr(imp->buf + start, '\n',
+ imp->filled - start);
+ if (c != NULL)
+ break;
+ }
+
+ imp->scanned = imp->filled;
+ if (imp->scanned >= DATA_SIZE_MAX) {
+ log_error("Entry is bigger than %u bytes.", DATA_SIZE_MAX);
+ return -E2BIG;
+ }
+
+ if (imp->passive_fd)
+ /* we have to wait for some data to come to us */
+ return -EAGAIN;
+
+ /* We know that imp->filled is at most DATA_SIZE_MAX, so if
+ we reallocate it, we'll increase the size at least a bit. */
+ assert_cc(DATA_SIZE_MAX < ENTRY_SIZE_MAX);
+ if (imp->size - imp->filled < LINE_CHUNK &&
+ !realloc_buffer(imp, MIN(imp->filled + LINE_CHUNK, ENTRY_SIZE_MAX)))
+ return log_oom();
+
+ assert(imp->buf);
+ assert(imp->size - imp->filled >= LINE_CHUNK ||
+ imp->size == ENTRY_SIZE_MAX);
+
+ n = read(imp->fd,
+ imp->buf + imp->filled,
+ imp->size - imp->filled);
+ if (n < 0) {
+ if (errno != EAGAIN)
+ log_error_errno(errno, "read(%d, ..., %zu): %m",
+ imp->fd,
+ imp->size - imp->filled);
+ return -errno;
+ } else if (n == 0)
+ return 0;
+
+ imp->filled += n;
+ }
+
+ *line = imp->buf + imp->offset;
+ *size = c + 1 - imp->buf - imp->offset;
+ imp->offset += *size;
+
+ return 1;
+}
+
+static int fill_fixed_size(JournalImporter *imp, void **data, size_t size) {
+
+ assert(imp);
+ assert(imp->state == IMPORTER_STATE_DATA_START ||
+ imp->state == IMPORTER_STATE_DATA ||
+ imp->state == IMPORTER_STATE_DATA_FINISH);
+ assert(size <= DATA_SIZE_MAX);
+ assert(imp->offset <= imp->filled);
+ assert(imp->filled <= imp->size);
+ assert(imp->buf != NULL || imp->size == 0);
+ assert(imp->buf == NULL || imp->size > 0);
+ assert(imp->fd >= 0);
+ assert(data);
+
+ while (imp->filled - imp->offset < size) {
+ int n;
+
+ if (imp->passive_fd)
+ /* we have to wait for some data to come to us */
+ return -EAGAIN;
+
+ if (!realloc_buffer(imp, imp->offset + size))
+ return log_oom();
+
+ n = read(imp->fd, imp->buf + imp->filled,
+ imp->size - imp->filled);
+ if (n < 0) {
+ if (errno != EAGAIN)
+ log_error_errno(errno, "read(%d, ..., %zu): %m", imp->fd,
+ imp->size - imp->filled);
+ return -errno;
+ } else if (n == 0)
+ return 0;
+
+ imp->filled += n;
+ }
+
+ *data = imp->buf + imp->offset;
+ imp->offset += size;
+
+ return 1;
+}
+
+static int get_data_size(JournalImporter *imp) {
+ int r;
+ void *data;
+
+ assert(imp);
+ assert(imp->state == IMPORTER_STATE_DATA_START);
+ assert(imp->data_size == 0);
+
+ r = fill_fixed_size(imp, &data, sizeof(uint64_t));
+ if (r <= 0)
+ return r;
+
+ imp->data_size = le64toh( *(uint64_t *) data );
+ if (imp->data_size > DATA_SIZE_MAX) {
+ log_error("Stream declares field with size %zu > DATA_SIZE_MAX = %u",
+ imp->data_size, DATA_SIZE_MAX);
+ return -EINVAL;
+ }
+ if (imp->data_size == 0)
+ log_warning("Binary field with zero length");
+
+ return 1;
+}
+
+static int get_data_data(JournalImporter *imp, void **data) {
+ int r;
+
+ assert(imp);
+ assert(data);
+ assert(imp->state == IMPORTER_STATE_DATA);
+
+ r = fill_fixed_size(imp, data, imp->data_size);
+ if (r <= 0)
+ return r;
+
+ return 1;
+}
+
+static int get_data_newline(JournalImporter *imp) {
+ int r;
+ char *data;
+
+ assert(imp);
+ assert(imp->state == IMPORTER_STATE_DATA_FINISH);
+
+ r = fill_fixed_size(imp, (void**) &data, 1);
+ if (r <= 0)
+ return r;
+
+ assert(data);
+ if (*data != '\n') {
+ log_error("expected newline, got '%c'", *data);
+ return -EINVAL;
+ }
+
+ return 1;
+}
+
+static int process_dunder(JournalImporter *imp, char *line, size_t n) {
+ const char *timestamp;
+ int r;
+
+ assert(line);
+ assert(n > 0);
+ assert(line[n-1] == '\n');
+
+ /* XXX: is it worth to support timestamps in extended format?
+ * We don't produce them, but who knows... */
+
+ timestamp = startswith(line, "__CURSOR=");
+ if (timestamp)
+ /* ignore __CURSOR */
+ return 1;
+
+ timestamp = startswith(line, "__REALTIME_TIMESTAMP=");
+ if (timestamp) {
+ long long unsigned x;
+ line[n-1] = '\0';
+ r = safe_atollu(timestamp, &x);
+ if (r < 0)
+ log_warning("Failed to parse __REALTIME_TIMESTAMP: '%s'", timestamp);
+ else
+ imp->ts.realtime = x;
+ return r < 0 ? r : 1;
+ }
+
+ timestamp = startswith(line, "__MONOTONIC_TIMESTAMP=");
+ if (timestamp) {
+ long long unsigned x;
+ line[n-1] = '\0';
+ r = safe_atollu(timestamp, &x);
+ if (r < 0)
+ log_warning("Failed to parse __MONOTONIC_TIMESTAMP: '%s'", timestamp);
+ else
+ imp->ts.monotonic = x;
+ return r < 0 ? r : 1;
+ }
+
+ timestamp = startswith(line, "__");
+ if (timestamp) {
+ log_notice("Unknown dunder line %s", line);
+ return 1;
+ }
+
+ /* no dunder */
+ return 0;
+}
+
+int journal_importer_process_data(JournalImporter *imp) {
+ int r;
+
+ switch(imp->state) {
+ case IMPORTER_STATE_LINE: {
+ char *line, *sep;
+ size_t n = 0;
+
+ assert(imp->data_size == 0);
+
+ r = get_line(imp, &line, &n);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ imp->state = IMPORTER_STATE_EOF;
+ return r;
+ }
+ assert(n > 0);
+ assert(line[n-1] == '\n');
+
+ if (n == 1) {
+ log_trace("Received empty line, event is ready");
+ return 1;
+ }
+
+ r = process_dunder(imp, line, n);
+ if (r != 0)
+ return r < 0 ? r : 0;
+
+ /* MESSAGE=xxx\n
+ or
+ COREDUMP\n
+ LLLLLLLL0011223344...\n
+ */
+ sep = memchr(line, '=', n);
+ if (sep) {
+ /* chomp newline */
+ n--;
+
+ r = iovw_put(&imp->iovw, line, n);
+ if (r < 0)
+ return r;
+ } else {
+ /* replace \n with = */
+ line[n-1] = '=';
+
+ imp->field_len = n;
+ imp->state = IMPORTER_STATE_DATA_START;
+
+ /* we cannot put the field in iovec until we have all data */
+ }
+
+ log_trace("Received: %.*s (%s)", (int) n, line, sep ? "text" : "binary");
+
+ return 0; /* continue */
+ }
+
+ case IMPORTER_STATE_DATA_START:
+ assert(imp->data_size == 0);
+
+ r = get_data_size(imp);
+ // log_debug("get_data_size() -> %d", r);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ imp->state = IMPORTER_STATE_EOF;
+ return 0;
+ }
+
+ imp->state = imp->data_size > 0 ?
+ IMPORTER_STATE_DATA : IMPORTER_STATE_DATA_FINISH;
+
+ return 0; /* continue */
+
+ case IMPORTER_STATE_DATA: {
+ void *data;
+ char *field;
+
+ assert(imp->data_size > 0);
+
+ r = get_data_data(imp, &data);
+ // log_debug("get_data_data() -> %d", r);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ imp->state = IMPORTER_STATE_EOF;
+ return 0;
+ }
+
+ assert(data);
+
+ field = (char*) data - sizeof(uint64_t) - imp->field_len;
+ memmove(field + sizeof(uint64_t), field, imp->field_len);
+
+ r = iovw_put(&imp->iovw, field + sizeof(uint64_t), imp->field_len + imp->data_size);
+ if (r < 0)
+ return r;
+
+ imp->state = IMPORTER_STATE_DATA_FINISH;
+
+ return 0; /* continue */
+ }
+
+ case IMPORTER_STATE_DATA_FINISH:
+ r = get_data_newline(imp);
+ // log_debug("get_data_newline() -> %d", r);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ imp->state = IMPORTER_STATE_EOF;
+ return 0;
+ }
+
+ imp->data_size = 0;
+ imp->state = IMPORTER_STATE_LINE;
+
+ return 0; /* continue */
+ default:
+ assert_not_reached("wtf?");
+ }
+}
+
+int journal_importer_push_data(JournalImporter *imp, const char *data, size_t size) {
+ assert(imp);
+ assert(imp->state != IMPORTER_STATE_EOF);
+
+ if (!realloc_buffer(imp, imp->filled + size)) {
+ log_error("Failed to store received data of size %zu "
+ "(in addition to existing %zu bytes with %zu filled): %s",
+ size, imp->size, imp->filled, strerror(ENOMEM));
+ return -ENOMEM;
+ }
+
+ memcpy(imp->buf + imp->filled, data, size);
+ imp->filled += size;
+
+ return 0;
+}
+
+void journal_importer_drop_iovw(JournalImporter *imp) {
+ size_t remain, target;
+
+ /* This function drops processed data that along with the iovw that points at it */
+
+ iovw_free_contents(&imp->iovw);
+
+ /* possibly reset buffer position */
+ remain = imp->filled - imp->offset;
+
+ if (remain == 0) /* no brainer */
+ imp->offset = imp->scanned = imp->filled = 0;
+ else if (imp->offset > imp->size - imp->filled &&
+ imp->offset > remain) {
+ memcpy(imp->buf, imp->buf + imp->offset, remain);
+ imp->offset = imp->scanned = 0;
+ imp->filled = remain;
+ }
+
+ target = imp->size;
+ while (target > 16 * LINE_CHUNK && imp->filled < target / 2)
+ target /= 2;
+ if (target < imp->size) {
+ char *tmp;
+
+ tmp = realloc(imp->buf, target);
+ if (!tmp)
+ log_warning("Failed to reallocate buffer to (smaller) size %zu",
+ target);
+ else {
+ log_debug("Reallocated buffer from %zu to %zu bytes",
+ imp->size, target);
+ imp->buf = tmp;
+ imp->size = target;
+ }
+ }
+}
+
+bool journal_importer_eof(const JournalImporter *imp) {
+ return imp->state == IMPORTER_STATE_EOF;
+}
diff --git a/src/basic/journal-importer.h b/src/basic/journal-importer.h
new file mode 100644
index 0000000000..b3e308dd6d
--- /dev/null
+++ b/src/basic/journal-importer.h
@@ -0,0 +1,70 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Zbigniew Jędrzejewski-Szmek
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#pragma once
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <sys/uio.h>
+
+#include "time-util.h"
+
+/* Make sure not to make this smaller than the maximum coredump size.
+ * See COREDUMP_MAX in coredump.c */
+#define ENTRY_SIZE_MAX (1024*1024*770u)
+#define DATA_SIZE_MAX (1024*1024*768u)
+#define LINE_CHUNK 8*1024u
+
+struct iovec_wrapper {
+ struct iovec *iovec;
+ size_t size_bytes;
+ size_t count;
+};
+
+size_t iovw_size(struct iovec_wrapper *iovw);
+
+typedef struct JournalImporter {
+ int fd;
+ bool passive_fd;
+ char *name;
+
+ char *buf;
+ size_t size; /* total size of the buffer */
+ size_t offset; /* offset to the beginning of live data in the buffer */
+ size_t scanned; /* number of bytes since the beginning of data without a newline */
+ size_t filled; /* total number of bytes in the buffer */
+
+ size_t field_len; /* used for binary fields: the field name length */
+ size_t data_size; /* and the size of the binary data chunk being processed */
+
+ struct iovec_wrapper iovw;
+
+ int state;
+ dual_timestamp ts;
+} JournalImporter;
+
+void journal_importer_cleanup(JournalImporter *);
+int journal_importer_process_data(JournalImporter *);
+int journal_importer_push_data(JournalImporter *, const char *data, size_t size);
+void journal_importer_drop_iovw(JournalImporter *);
+bool journal_importer_eof(const JournalImporter *);
+
+static inline size_t journal_importer_bytes_remaining(const JournalImporter *imp) {
+ return imp->filled;
+}
diff --git a/src/basic/khash.c b/src/basic/khash.c
new file mode 100644
index 0000000000..84648dc1c9
--- /dev/null
+++ b/src/basic/khash.c
@@ -0,0 +1,275 @@
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <linux/if_alg.h>
+#include <stdbool.h>
+#include <sys/socket.h>
+
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "hexdecoct.h"
+#include "khash.h"
+#include "macro.h"
+#include "missing.h"
+#include "string-util.h"
+#include "util.h"
+
+/* On current kernels the maximum digest (according to "grep digestsize /proc/crypto | sort -u") is actually 32, but
+ * let's add some extra room, the few wasted bytes don't really matter... */
+#define LONGEST_DIGEST 128
+
+struct khash {
+ int fd;
+ char *algorithm;
+ uint8_t digest[LONGEST_DIGEST+1];
+ size_t digest_size;
+ bool digest_valid;
+};
+
+int khash_new_with_key(khash **ret, const char *algorithm, const void *key, size_t key_size) {
+ union {
+ struct sockaddr sa;
+ struct sockaddr_alg alg;
+ } sa = {
+ .alg.salg_family = AF_ALG,
+ .alg.salg_type = "hash",
+ };
+
+ _cleanup_(khash_unrefp) khash *h = NULL;
+ _cleanup_close_ int fd = -1;
+ ssize_t n;
+
+ assert(ret);
+ assert(key || key_size == 0);
+
+ /* Filter out an empty algorithm early, as we do not support an algorithm by that name. */
+ if (isempty(algorithm))
+ return -EINVAL;
+
+ /* Overly long hash algorithm names we definitely do not support */
+ if (strlen(algorithm) >= sizeof(sa.alg.salg_name))
+ return -EOPNOTSUPP;
+
+ fd = socket(AF_ALG, SOCK_SEQPACKET|SOCK_CLOEXEC, 0);
+ if (fd < 0)
+ return -errno;
+
+ strcpy((char*) sa.alg.salg_name, algorithm);
+ if (bind(fd, &sa.sa, sizeof(sa)) < 0) {
+ if (errno == ENOENT)
+ return -EOPNOTSUPP;
+ return -errno;
+ }
+
+ if (key) {
+ if (setsockopt(fd, SOL_ALG, ALG_SET_KEY, key, key_size) < 0)
+ return -errno;
+ }
+
+ h = new0(khash, 1);
+ if (!h)
+ return -ENOMEM;
+
+ h->fd = accept4(fd, NULL, 0, SOCK_CLOEXEC);
+ if (h->fd < 0)
+ return -errno;
+
+ h->algorithm = strdup(algorithm);
+ if (!h->algorithm)
+ return -ENOMEM;
+
+ /* Temporary fix for rc kernel bug: https://bugzilla.redhat.com/show_bug.cgi?id=1395896 */
+ (void) send(h->fd, NULL, 0, 0);
+
+ /* Figure out the digest size */
+ n = recv(h->fd, h->digest, sizeof(h->digest), 0);
+ if (n < 0)
+ return -errno;
+ if (n >= LONGEST_DIGEST) /* longer than what we expected? If so, we don't support this */
+ return -EOPNOTSUPP;
+
+ h->digest_size = (size_t) n;
+ h->digest_valid = true;
+
+ /* Temporary fix for rc kernel bug: https://bugzilla.redhat.com/show_bug.cgi?id=1395896 */
+ (void) send(h->fd, NULL, 0, 0);
+
+ *ret = h;
+ h = NULL;
+
+ return 0;
+}
+
+int khash_new(khash **ret, const char *algorithm) {
+ return khash_new_with_key(ret, algorithm, NULL, 0);
+}
+
+khash* khash_unref(khash *h) {
+ if (!h)
+ return NULL;
+
+ safe_close(h->fd);
+ free(h->algorithm);
+ free(h);
+
+ return NULL;
+}
+
+int khash_dup(khash *h, khash **ret) {
+ _cleanup_(khash_unrefp) khash *copy = NULL;
+
+ assert(h);
+ assert(ret);
+
+ copy = newdup(khash, h, 1);
+ if (!copy)
+ return -ENOMEM;
+
+ copy->fd = -1;
+ copy->algorithm = strdup(h->algorithm);
+ if (!copy->algorithm)
+ return -ENOMEM;
+
+ copy->fd = accept4(h->fd, NULL, 0, SOCK_CLOEXEC);
+ if (copy->fd < 0)
+ return -errno;
+
+ *ret = copy;
+ copy = NULL;
+
+ return 0;
+}
+
+const char *khash_get_algorithm(khash *h) {
+ assert(h);
+
+ return h->algorithm;
+}
+
+size_t khash_get_size(khash *h) {
+ assert(h);
+
+ return h->digest_size;
+}
+
+int khash_reset(khash *h) {
+ ssize_t n;
+
+ assert(h);
+
+ n = send(h->fd, NULL, 0, 0);
+ if (n < 0)
+ return -errno;
+
+ h->digest_valid = false;
+
+ return 0;
+}
+
+int khash_put(khash *h, const void *buffer, size_t size) {
+ ssize_t n;
+
+ assert(h);
+ assert(buffer || size == 0);
+
+ if (size <= 0)
+ return 0;
+
+ n = send(h->fd, buffer, size, MSG_MORE);
+ if (n < 0)
+ return -errno;
+
+ h->digest_valid = false;
+
+ return 0;
+}
+
+int khash_put_iovec(khash *h, const struct iovec *iovec, size_t n) {
+ struct msghdr mh = {
+ mh.msg_iov = (struct iovec*) iovec,
+ mh.msg_iovlen = n,
+ };
+ ssize_t k;
+
+ assert(h);
+ assert(iovec || n == 0);
+
+ if (n <= 0)
+ return 0;
+
+ k = sendmsg(h->fd, &mh, MSG_MORE);
+ if (k < 0)
+ return -errno;
+
+ h->digest_valid = false;
+
+ return 0;
+}
+
+static int retrieve_digest(khash *h) {
+ ssize_t n;
+
+ assert(h);
+
+ if (h->digest_valid)
+ return 0;
+
+ n = recv(h->fd, h->digest, h->digest_size, 0);
+ if (n < 0)
+ return n;
+ if ((size_t) n != h->digest_size) /* digest size changed? */
+ return -EIO;
+
+ h->digest_valid = true;
+
+ return 0;
+}
+
+int khash_digest_data(khash *h, const void **ret) {
+ int r;
+
+ assert(h);
+ assert(ret);
+
+ r = retrieve_digest(h);
+ if (r < 0)
+ return r;
+
+ *ret = h->digest;
+ return 0;
+}
+
+int khash_digest_string(khash *h, char **ret) {
+ int r;
+ char *p;
+
+ assert(h);
+ assert(ret);
+
+ r = retrieve_digest(h);
+ if (r < 0)
+ return r;
+
+ p = hexmem(h->digest, h->digest_size);
+ if (!p)
+ return -ENOMEM;
+
+ *ret = p;
+ return 0;
+}
diff --git a/src/basic/khash.h b/src/basic/khash.h
new file mode 100644
index 0000000000..410f3020e0
--- /dev/null
+++ b/src/basic/khash.h
@@ -0,0 +1,53 @@
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2016 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#include "macro.h"
+
+typedef struct khash khash;
+
+/* For plain hash functions. Hash functions commonly supported on today's kernels are: crc32c, crct10dif, crc32,
+ * sha224, sha256, sha512, sha384, sha1, md5, md4, sha3-224, sha3-256, sha3-384, sha3-512, and more. */
+int khash_new(khash **ret, const char *algorithm);
+
+/* For keyed hash functions. Hash functions commonly supported on today's kernels are: hmac(sha256), cmac(aes),
+ * cmac(des3_ede), hmac(sha3-512), hmac(sha3-384), hmac(sha3-256), hmac(sha3-224), hmac(rmd160), hmac(rmd128),
+ * hmac(sha224), hmac(sha512), hmac(sha384), hmac(sha1), hmac(md5), and more. */
+int khash_new_with_key(khash **ret, const char *algorithm, const void *key, size_t key_size);
+
+int khash_dup(khash *h, khash **ret);
+khash* khash_unref(khash *h);
+
+const char *khash_get_algorithm(khash *h);
+size_t khash_get_size(khash *h);
+
+int khash_reset(khash *h);
+
+int khash_put(khash *h, const void *buffer, size_t size);
+int khash_put_iovec(khash *h, const struct iovec *iovec, size_t n);
+
+int khash_digest_data(khash *h, const void **ret);
+int khash_digest_string(khash *h, char **ret);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(khash*, khash_unref);
diff --git a/src/basic/log.c b/src/basic/log.c
index 4919d175da..36efc9ac7d 100644
--- a/src/basic/log.c
+++ b/src/basic/log.c
@@ -37,7 +37,7 @@
#include "alloc-util.h"
#include "fd-util.h"
-#include "formats-util.h"
+#include "format-util.h"
#include "io-util.h"
#include "log.h"
#include "macro.h"
@@ -72,6 +72,7 @@ static bool show_color = false;
static bool show_location = false;
static bool upgrade_syslog_to_journal = false;
+static bool always_reopen_console = false;
/* Akin to glibc's __abort_msg; which is private and we hence cannot
* use here. */
@@ -95,7 +96,7 @@ static int log_open_console(void) {
if (console_fd >= 0)
return 0;
- if (getpid() == 1) {
+ if (always_reopen_console) {
console_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
if (console_fd < 0)
return console_fd;
@@ -243,13 +244,13 @@ int log_open(void) {
return 0;
}
- if ((log_target != LOG_TARGET_AUTO && log_target != LOG_TARGET_SAFE) ||
+ if (!IN_SET(log_target, LOG_TARGET_AUTO, LOG_TARGET_SAFE) ||
getpid() == 1 ||
isatty(STDERR_FILENO) <= 0) {
- if (log_target == LOG_TARGET_AUTO ||
- log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
- log_target == LOG_TARGET_JOURNAL) {
+ if (IN_SET(log_target, LOG_TARGET_AUTO,
+ LOG_TARGET_JOURNAL_OR_KMSG,
+ LOG_TARGET_JOURNAL)) {
r = log_open_journal();
if (r >= 0) {
log_close_syslog();
@@ -258,8 +259,8 @@ int log_open(void) {
}
}
- if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
- log_target == LOG_TARGET_SYSLOG) {
+ if (IN_SET(log_target, LOG_TARGET_SYSLOG_OR_KMSG,
+ LOG_TARGET_SYSLOG)) {
r = log_open_syslog();
if (r >= 0) {
log_close_journal();
@@ -268,11 +269,11 @@ int log_open(void) {
}
}
- if (log_target == LOG_TARGET_AUTO ||
- log_target == LOG_TARGET_SAFE ||
- log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
- log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
- log_target == LOG_TARGET_KMSG) {
+ if (IN_SET(log_target, LOG_TARGET_AUTO,
+ LOG_TARGET_SAFE,
+ LOG_TARGET_JOURNAL_OR_KMSG,
+ LOG_TARGET_SYSLOG_OR_KMSG,
+ LOG_TARGET_KMSG)) {
r = log_open_kmsg();
if (r >= 0) {
log_close_journal();
@@ -500,7 +501,7 @@ static int log_do_header(
line ? "CODE_LINE=" : "",
line ? 1 : 0, line, /* %.0d means no output too, special case for 0 */
line ? "\n" : "",
- isempty(func) ? "" : "CODE_FUNCTION=",
+ isempty(func) ? "" : "CODE_FUNC=",
isempty(func) ? "" : func,
isempty(func) ? "" : "\n",
error ? "ERRNO=" : "",
@@ -588,9 +589,9 @@ static int log_dispatch(
if ((e = strpbrk(buffer, NEWLINE)))
*(e++) = 0;
- if (log_target == LOG_TARGET_AUTO ||
- log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
- log_target == LOG_TARGET_JOURNAL) {
+ if (IN_SET(log_target, LOG_TARGET_AUTO,
+ LOG_TARGET_JOURNAL_OR_KMSG,
+ LOG_TARGET_JOURNAL)) {
k = write_to_journal(level, error, file, line, func, object_field, object, extra_field, extra, buffer);
if (k < 0) {
@@ -600,8 +601,8 @@ static int log_dispatch(
}
}
- if (log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
- log_target == LOG_TARGET_SYSLOG) {
+ if (IN_SET(log_target, LOG_TARGET_SYSLOG_OR_KMSG,
+ LOG_TARGET_SYSLOG)) {
k = write_to_syslog(level, error, file, line, func, buffer);
if (k < 0) {
@@ -612,11 +613,11 @@ static int log_dispatch(
}
if (k <= 0 &&
- (log_target == LOG_TARGET_AUTO ||
- log_target == LOG_TARGET_SAFE ||
- log_target == LOG_TARGET_SYSLOG_OR_KMSG ||
- log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
- log_target == LOG_TARGET_KMSG)) {
+ IN_SET(log_target, LOG_TARGET_AUTO,
+ LOG_TARGET_SAFE,
+ LOG_TARGET_SYSLOG_OR_KMSG,
+ LOG_TARGET_JOURNAL_OR_KMSG,
+ LOG_TARGET_KMSG)) {
k = write_to_kmsg(level, error, file, line, func, buffer);
if (k < 0) {
@@ -881,9 +882,9 @@ int log_struct_internal(
if ((level & LOG_FACMASK) == 0)
level = log_facility | LOG_PRI(level);
- if ((log_target == LOG_TARGET_AUTO ||
- log_target == LOG_TARGET_JOURNAL_OR_KMSG ||
- log_target == LOG_TARGET_JOURNAL) &&
+ if (IN_SET(log_target, LOG_TARGET_AUTO,
+ LOG_TARGET_JOURNAL_OR_KMSG,
+ LOG_TARGET_JOURNAL) &&
journal_fd >= 0) {
char header[LINE_MAX];
struct iovec iovec[17] = {};
@@ -981,24 +982,30 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (streq(key, "debug") && !value)
log_set_max_level(LOG_DEBUG);
- else if (streq(key, "systemd.log_target") && value) {
+ else if (proc_cmdline_key_streq(key, "systemd.log_target")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
if (log_set_target_from_string(value) < 0)
log_warning("Failed to parse log target '%s'. Ignoring.", value);
- } else if (streq(key, "systemd.log_level") && value) {
+ } else if (proc_cmdline_key_streq(key, "systemd.log_level")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
if (log_set_max_level_from_string(value) < 0)
log_warning("Failed to parse log level '%s'. Ignoring.", value);
- } else if (streq(key, "systemd.log_color") && value) {
+ } else if (proc_cmdline_key_streq(key, "systemd.log_color")) {
- if (log_show_color_from_string(value) < 0)
+ if (log_show_color_from_string(value ?: "1") < 0)
log_warning("Failed to parse log color setting '%s'. Ignoring.", value);
- } else if (streq(key, "systemd.log_location") && value) {
+ } else if (proc_cmdline_key_streq(key, "systemd.log_location")) {
- if (log_show_location_from_string(value) < 0)
+ if (log_show_location_from_string(value ?: "1") < 0)
log_warning("Failed to parse log location setting '%s'. Ignoring.", value);
}
@@ -1009,10 +1016,9 @@ void log_parse_environment(void) {
const char *e;
if (get_ctty_devnr(0, NULL) < 0)
- /* 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, NULL, true);
+ /* Only try to read the command line in daemons. We assume that anything that has a controlling tty is
+ user stuff. */
+ (void) proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
e = secure_getenv("SYSTEMD_LOG_TARGET");
if (e && log_set_target_from_string(e) < 0)
@@ -1078,8 +1084,8 @@ int log_show_location_from_string(const char *e) {
}
bool log_on_console(void) {
- if (log_target == LOG_TARGET_CONSOLE ||
- log_target == LOG_TARGET_CONSOLE_PREFIXED)
+ if (IN_SET(log_target, LOG_TARGET_CONSOLE,
+ LOG_TARGET_CONSOLE_PREFIXED))
return true;
return syslog_fd < 0 && kmsg_fd < 0 && journal_fd < 0;
@@ -1134,8 +1140,8 @@ int log_syntax_internal(
PROTECT_ERRNO;
char buffer[LINE_MAX];
- int r;
va_list ap;
+ const char *unit_fmt = NULL;
if (error < 0)
error = -error;
@@ -1154,24 +1160,19 @@ int log_syntax_internal(
va_end(ap);
if (unit)
- r = log_struct_internal(
- level, error,
- file, line, func,
- getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s", unit,
- LOG_MESSAGE_ID(SD_MESSAGE_INVALID_CONFIGURATION),
- "CONFIG_FILE=%s", config_file,
- "CONFIG_LINE=%u", config_line,
- LOG_MESSAGE("[%s:%u] %s", config_file, config_line, buffer),
- NULL);
- else
- r = log_struct_internal(
- level, error,
- file, line, func,
- LOG_MESSAGE_ID(SD_MESSAGE_INVALID_CONFIGURATION),
- "CONFIG_FILE=%s", config_file,
- "CONFIG_LINE=%u", config_line,
- LOG_MESSAGE("[%s:%u] %s", config_file, config_line, buffer),
- NULL);
+ unit_fmt = getpid() == 1 ? "UNIT=%s" : "USER_UNIT=%s";
+
+ return log_struct_internal(
+ level, error,
+ file, line, func,
+ "MESSAGE_ID=" SD_MESSAGE_INVALID_CONFIGURATION_STR,
+ "CONFIG_FILE=%s", config_file,
+ "CONFIG_LINE=%u", config_line,
+ LOG_MESSAGE("%s:%u: %s", config_file, config_line, buffer),
+ unit_fmt, unit,
+ NULL);
+}
- return r;
+void log_set_always_reopen_console(bool b) {
+ always_reopen_console = b;
}
diff --git a/src/basic/log.h b/src/basic/log.h
index 2afee20bb5..72714e02e5 100644
--- a/src/basic/log.h
+++ b/src/basic/log.h
@@ -214,13 +214,13 @@ bool log_on_console(void) _pure_;
const char *log_target_to_string(LogTarget target) _const_;
LogTarget log_target_from_string(const char *s) _pure_;
-/* Helpers to prepare various fields for structured logging */
+/* Helper to prepare various field for structured logging */
#define LOG_MESSAGE(fmt, ...) "MESSAGE=" fmt, ##__VA_ARGS__
-#define LOG_MESSAGE_ID(x) "MESSAGE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(x)
void log_received_signal(int level, const struct signalfd_siginfo *si);
void log_set_upgrade_syslog_to_journal(bool b);
+void log_set_always_reopen_console(bool b);
int log_syntax_internal(
const char *unit,
diff --git a/src/basic/missing.h b/src/basic/missing.h
index 4c013be608..480462357d 100644
--- a/src/basic/missing.h
+++ b/src/basic/missing.h
@@ -34,6 +34,7 @@
#include <net/ethernet.h>
#include <stdlib.h>
#include <sys/resource.h>
+#include <sys/socket.h>
#include <sys/syscall.h>
#include <uchar.h>
#include <unistd.h>
@@ -50,6 +51,23 @@
#include <linux/btrfs.h>
#endif
+#ifdef HAVE_LINUX_VM_SOCKETS_H
+#include <linux/vm_sockets.h>
+#else
+#define VMADDR_CID_ANY -1U
+struct sockaddr_vm {
+ unsigned short svm_family;
+ unsigned short svm_reserved1;
+ unsigned int svm_port;
+ unsigned int svm_cid;
+ unsigned char svm_zero[sizeof(struct sockaddr) -
+ sizeof(unsigned short) -
+ sizeof(unsigned short) -
+ sizeof(unsigned int) -
+ sizeof(unsigned int)];
+};
+#endif /* !HAVE_LINUX_VM_SOCKETS_H */
+
#include "macro.h"
#ifndef RLIMIT_RTTIME
@@ -143,6 +161,10 @@
#define GRND_RANDOM 0x0002
#endif
+#ifndef FS_NOCOW_FL
+#define FS_NOCOW_FL 0x00800000
+#endif
+
#ifndef BTRFS_IOCTL_MAGIC
#define BTRFS_IOCTL_MAGIC 0x94
#endif
@@ -1022,6 +1044,22 @@ struct btrfs_ioctl_quota_ctl_args {
typedef int32_t key_serial_t;
#endif
+#ifndef KEYCTL_JOIN_SESSION_KEYRING
+#define KEYCTL_JOIN_SESSION_KEYRING 1
+#endif
+
+#ifndef KEYCTL_CHOWN
+#define KEYCTL_CHOWN 4
+#endif
+
+#ifndef KEYCTL_SETPERM
+#define KEYCTL_SETPERM 5
+#endif
+
+#ifndef KEYCTL_DESCRIBE
+#define KEYCTL_DESCRIBE 6
+#endif
+
#ifndef KEYCTL_READ
#define KEYCTL_READ 11
#endif
@@ -1030,10 +1068,44 @@ typedef int32_t key_serial_t;
#define KEYCTL_SET_TIMEOUT 15
#endif
+#ifndef KEY_POS_VIEW
+#define KEY_POS_VIEW 0x01000000
+#define KEY_POS_READ 0x02000000
+#define KEY_POS_WRITE 0x04000000
+#define KEY_POS_SEARCH 0x08000000
+#define KEY_POS_LINK 0x10000000
+#define KEY_POS_SETATTR 0x20000000
+
+#define KEY_USR_VIEW 0x00010000
+#define KEY_USR_READ 0x00020000
+#define KEY_USR_WRITE 0x00040000
+#define KEY_USR_SEARCH 0x00080000
+#define KEY_USR_LINK 0x00100000
+#define KEY_USR_SETATTR 0x00200000
+
+#define KEY_GRP_VIEW 0x00000100
+#define KEY_GRP_READ 0x00000200
+#define KEY_GRP_WRITE 0x00000400
+#define KEY_GRP_SEARCH 0x00000800
+#define KEY_GRP_LINK 0x00001000
+#define KEY_GRP_SETATTR 0x00002000
+
+#define KEY_OTH_VIEW 0x00000001
+#define KEY_OTH_READ 0x00000002
+#define KEY_OTH_WRITE 0x00000004
+#define KEY_OTH_SEARCH 0x00000008
+#define KEY_OTH_LINK 0x00000010
+#define KEY_OTH_SETATTR 0x00000020
+#endif
+
#ifndef KEY_SPEC_USER_KEYRING
#define KEY_SPEC_USER_KEYRING -4
#endif
+#ifndef KEY_SPEC_SESSION_KEYRING
+#define KEY_SPEC_SESSION_KEYRING -3
+#endif
+
#ifndef PR_CAP_AMBIENT
#define PR_CAP_AMBIENT 47
#endif
@@ -1076,6 +1148,41 @@ typedef int32_t key_serial_t;
#define IFA_F_MCAUTOJOIN 0x400
#endif
+#ifndef HAVE_STRUCT_ETHTOOL_LINK_SETTINGS
+
+#define ETHTOOL_GLINKSETTINGS 0x0000004c /* Get ethtool_link_settings */
+#define ETHTOOL_SLINKSETTINGS 0x0000004d /* Set ethtool_link_settings */
+
+struct ethtool_link_settings {
+ __u32 cmd;
+ __u32 speed;
+ __u8 duplex;
+ __u8 port;
+ __u8 phy_address;
+ __u8 autoneg;
+ __u8 mdio_support;
+ __u8 eth_tp_mdix;
+ __u8 eth_tp_mdix_ctrl;
+ __s8 link_mode_masks_nwords;
+ __u32 reserved[8];
+ __u32 link_mode_masks[0];
+ /* layout of link_mode_masks fields:
+ * __u32 map_supported[link_mode_masks_nwords];
+ * __u32 map_advertising[link_mode_masks_nwords];
+ * __u32 map_lp_advertising[link_mode_masks_nwords];
+ */
+};
+
+#endif
+
+#endif
+
+#ifndef SOL_ALG
+#define SOL_ALG 279
+#endif
+
+#ifndef AF_VSOCK
+#define AF_VSOCK 40
#endif
#include "missing_syscall.h"
diff --git a/src/basic/missing_syscall.h b/src/basic/missing_syscall.h
index e6fd67cb9d..9fc4156564 100644
--- a/src/basic/missing_syscall.h
+++ b/src/basic/missing_syscall.h
@@ -194,6 +194,8 @@ static inline pid_t raw_getpid(void) {
# define __NR_renameat2 316
# elif defined __arm__
# define __NR_renameat2 382
+# elif defined __aarch64__
+# define __NR_renameat2 276
# elif defined _MIPS_SIM
# if _MIPS_SIM == _MIPS_SIM_ABI32
# define __NR_renameat2 4351
diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c
index c8f8022578..a8fd63fb45 100644
--- a/src/basic/mount-util.c
+++ b/src/basic/mount-util.c
@@ -29,6 +29,7 @@
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
+#include "fs-util.h"
#include "hashmap.h"
#include "mount-util.h"
#include "parse-util.h"
@@ -111,9 +112,10 @@ int fd_is_mount_point(int fd, const char *filename, int flags) {
r = name_to_handle_at(fd, filename, &h.handle, &mount_id, flags);
if (r < 0) {
- if (errno == ENOSYS)
- /* This kernel does not support name_to_handle_at()
- * fall back to simpler logic. */
+ if (IN_SET(errno, ENOSYS, EACCES, EPERM))
+ /* This kernel does not support name_to_handle_at() at all, or the syscall was blocked (maybe
+ * through seccomp, because we are running inside of a container?): fall back to simpler
+ * logic. */
goto fallback_fdinfo;
else if (errno == EOPNOTSUPP)
/* This kernel or file system does not support
@@ -162,7 +164,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 (IN_SET(r, -EOPNOTSUPP, -EACCES))
+ if (IN_SET(r, -EOPNOTSUPP, -EACCES, -EPERM))
goto fallback_fstat;
if (r < 0)
return r;
@@ -205,9 +207,10 @@ fallback_fstat:
}
/* flags can be AT_SYMLINK_FOLLOW or 0 */
-int path_is_mount_point(const char *t, int flags) {
- _cleanup_close_ int fd = -1;
+int path_is_mount_point(const char *t, const char *root, int flags) {
_cleanup_free_ char *canonical = NULL, *parent = NULL;
+ _cleanup_close_ int fd = -1;
+ int r;
assert(t);
@@ -219,9 +222,9 @@ int path_is_mount_point(const char *t, int flags) {
* /bin -> /usr/bin/ and /usr is a mount point, then the parent that we
* look at needs to be /usr, not /. */
if (flags & AT_SYMLINK_FOLLOW) {
- canonical = canonicalize_file_name(t);
- if (!canonical)
- return -errno;
+ r = chase_symlinks(t, root, 0, &canonical);
+ if (r < 0)
+ return r;
t = canonical;
}
@@ -473,7 +476,7 @@ int bind_remount_recursive(const char *prefix, bool ro, char **blacklist) {
return r;
/* Deal with mount points that are obstructed by a later mount */
- r = path_is_mount_point(x, 0);
+ r = path_is_mount_point(x, NULL, 0);
if (r == -ENOENT || r == 0)
continue;
if (r < 0)
@@ -642,7 +645,7 @@ static char* mount_flags_to_string(long unsigned flags) {
FLAG(MS_I_VERSION),
FLAG(MS_STRICTATIME),
FLAG(MS_LAZYTIME),
- y, NULL);
+ y);
if (!x)
return NULL;
if (!y)
@@ -671,6 +674,9 @@ int mount_verbose(
else if ((flags & MS_BIND) && !type)
log_debug("Bind-mounting %s on %s (%s \"%s\")...",
what, where, strnull(fl), strempty(options));
+ else if (flags & MS_MOVE)
+ log_debug("Moving mount %s → %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));
@@ -687,3 +693,35 @@ int umount_verbose(const char *what) {
return log_error_errno(errno, "Failed to unmount %s: %m", what);
return 0;
}
+
+const char *mount_propagation_flags_to_string(unsigned long flags) {
+
+ switch (flags & (MS_SHARED|MS_SLAVE|MS_PRIVATE)) {
+ case 0:
+ return "";
+ case MS_SHARED:
+ return "shared";
+ case MS_SLAVE:
+ return "slave";
+ case MS_PRIVATE:
+ return "private";
+ }
+
+ return NULL;
+}
+
+
+int mount_propagation_flags_from_string(const char *name, unsigned long *ret) {
+
+ if (isempty(name))
+ *ret = 0;
+ else if (streq(name, "shared"))
+ *ret = MS_SHARED;
+ else if (streq(name, "slave"))
+ *ret = MS_SLAVE;
+ else if (streq(name, "private"))
+ *ret = MS_PRIVATE;
+ else
+ return -EINVAL;
+ return 0;
+}
diff --git a/src/basic/mount-util.h b/src/basic/mount-util.h
index 4f305df19f..1615c94e9a 100644
--- a/src/basic/mount-util.h
+++ b/src/basic/mount-util.h
@@ -30,7 +30,7 @@
#include "missing.h"
int fd_is_mount_point(int fd, const char *filename, int flags);
-int path_is_mount_point(const char *path, int flags);
+int path_is_mount_point(const char *path, const char *root, int flags);
int repeat_unmount(const char *path, int flags);
@@ -61,3 +61,6 @@ int mount_verbose(
unsigned long flags,
const char *options);
int umount_verbose(const char *where);
+
+const char *mount_propagation_flags_to_string(unsigned long flags);
+int mount_propagation_flags_from_string(const char *name, unsigned long *ret);
diff --git a/src/basic/nss-util.h b/src/basic/nss-util.h
index e7844fff96..9d927a8227 100644
--- a/src/basic/nss-util.h
+++ b/src/basic/nss-util.h
@@ -27,6 +27,10 @@
#define NSS_SIGNALS_BLOCK SIGALRM,SIGVTALRM,SIGPIPE,SIGCHLD,SIGTSTP,SIGIO,SIGHUP,SIGUSR1,SIGUSR2,SIGPROF,SIGURG,SIGWINCH
+#ifndef DEPRECATED_RES_USE_INET6
+# define DEPRECATED_RES_USE_INET6 0x00002000
+#endif
+
#define NSS_GETHOSTBYNAME_PROTOTYPES(module) \
enum nss_status _nss_##module##_gethostbyname4_r( \
const char *name, \
@@ -92,7 +96,7 @@ enum nss_status _nss_##module##_gethostbyname_r( \
int *errnop, int *h_errnop) { \
enum nss_status ret = NSS_STATUS_NOTFOUND; \
\
- if (_res.options & RES_USE_INET6) \
+ if (_res.options & DEPRECATED_RES_USE_INET6) \
ret = _nss_##module##_gethostbyname3_r( \
name, \
AF_INET6, \
diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c
index c98815b9bc..6e58ced6f5 100644
--- a/src/basic/parse-util.c
+++ b/src/basic/parse-util.c
@@ -574,3 +574,19 @@ int parse_nice(const char *p, int *ret) {
*ret = n;
return 0;
}
+
+int parse_ip_port(const char *s, uint16_t *ret) {
+ uint16_t l;
+ int r;
+
+ r = safe_atou16(s, &l);
+ if (r < 0)
+ return r;
+
+ if (l == 0)
+ return -EINVAL;
+
+ *ret = (uint16_t) l;
+
+ return 0;
+}
diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h
index 461e1cd4d8..4d132f0de5 100644
--- a/src/basic/parse-util.h
+++ b/src/basic/parse-util.h
@@ -110,3 +110,5 @@ int parse_percent_unbounded(const char *p);
int parse_percent(const char *p);
int parse_nice(const char *p, int *ret);
+
+int parse_ip_port(const char *s, uint16_t *ret);
diff --git a/src/basic/path-util.c b/src/basic/path-util.c
index fd38f51c4c..1313a52c9c 100644
--- a/src/basic/path-util.c
+++ b/src/basic/path-util.c
@@ -83,7 +83,7 @@ char *path_make_absolute(const char *p, const char *prefix) {
if (path_is_absolute(p) || !prefix)
return strdup(p);
- return strjoin(prefix, "/", p, NULL);
+ return strjoin(prefix, "/", p);
}
int path_make_absolute_cwd(const char *p, char **ret) {
@@ -104,7 +104,7 @@ int path_make_absolute_cwd(const char *p, char **ret) {
if (!cwd)
return negative_errno();
- c = strjoin(cwd, "/", p, NULL);
+ c = strjoin(cwd, "/", p);
}
if (!c)
return -ENOMEM;
@@ -220,10 +220,11 @@ int path_strv_make_absolute_cwd(char **l) {
return 0;
}
-char **path_strv_resolve(char **l, const char *prefix) {
+char **path_strv_resolve(char **l, const char *root) {
char **s;
unsigned k = 0;
bool enomem = false;
+ int r;
if (strv_isempty(l))
return l;
@@ -233,17 +234,17 @@ char **path_strv_resolve(char **l, const char *prefix) {
* changes on failure. */
STRV_FOREACH(s, l) {
- char *t, *u;
_cleanup_free_ char *orig = NULL;
+ char *t, *u;
if (!path_is_absolute(*s)) {
free(*s);
continue;
}
- if (prefix) {
+ if (root) {
orig = *s;
- t = strappend(prefix, orig);
+ t = prefix_root(root, orig);
if (!t) {
enomem = true;
continue;
@@ -251,28 +252,26 @@ char **path_strv_resolve(char **l, const char *prefix) {
} else
t = *s;
- errno = 0;
- u = canonicalize_file_name(t);
- if (!u) {
- if (errno == ENOENT) {
- if (prefix) {
- u = orig;
- orig = NULL;
- free(t);
- } else
- u = t;
- } else {
+ r = chase_symlinks(t, root, 0, &u);
+ if (r == -ENOENT) {
+ if (root) {
+ u = orig;
+ orig = NULL;
free(t);
- if (errno == ENOMEM || errno == 0)
- enomem = true;
+ } else
+ u = t;
+ } else if (r < 0) {
+ free(t);
- continue;
- }
- } else if (prefix) {
+ if (r == -ENOMEM)
+ enomem = true;
+
+ continue;
+ } else if (root) {
char *x;
free(t);
- x = path_startswith(u, prefix);
+ x = path_startswith(u, root);
if (x) {
/* restore the slash if it was lost */
if (!startswith(x, "/"))
@@ -304,12 +303,12 @@ char **path_strv_resolve(char **l, const char *prefix) {
return l;
}
-char **path_strv_resolve_uniq(char **l, const char *prefix) {
+char **path_strv_resolve_uniq(char **l, const char *root) {
if (strv_isempty(l))
return l;
- if (!path_strv_resolve(l, prefix))
+ if (!path_strv_resolve(l, root))
return NULL;
return strv_uniq(l);
@@ -454,13 +453,11 @@ char* path_join(const char *root, const char *path, const char *rest) {
return strjoin(root, endswith(root, "/") ? "" : "/",
path[0] == '/' ? path+1 : path,
rest ? (endswith(path, "/") ? "" : "/") : NULL,
- rest && rest[0] == '/' ? rest+1 : rest,
- NULL);
+ rest && rest[0] == '/' ? rest+1 : rest);
else
return strjoin(path,
rest ? (endswith(path, "/") ? "" : "/") : NULL,
- rest && rest[0] == '/' ? rest+1 : rest,
- NULL);
+ rest && rest[0] == '/' ? rest+1 : rest);
}
int find_binary(const char *name, char **ret) {
@@ -504,7 +501,7 @@ int find_binary(const char *name, char **ret) {
if (!path_is_absolute(element))
continue;
- j = strjoin(element, "/", name, NULL);
+ j = strjoin(element, "/", name);
if (!j)
return -ENOMEM;
@@ -702,10 +699,7 @@ bool filename_is_valid(const char *p) {
if (isempty(p))
return false;
- if (streq(p, "."))
- return false;
-
- if (streq(p, ".."))
+ if (dot_or_dot_dot(p))
return false;
e = strchrnul(p, '/');
@@ -723,14 +717,17 @@ bool path_is_safe(const char *p) {
if (isempty(p))
return false;
- if (streq(p, "..") || startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
+ if (dot_or_dot_dot(p))
+ return false;
+
+ if (startswith(p, "../") || endswith(p, "/..") || strstr(p, "/../"))
return false;
if (strlen(p)+1 > PATH_MAX)
return false;
/* The following two checks are not really dangerous, but hey, they still are confusing */
- if (streq(p, ".") || startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
+ if (startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
return false;
if (strstr(p, "//"))
@@ -895,3 +892,16 @@ int systemd_installation_has_version(const char *root, unsigned minimal_version)
return false;
}
+
+bool dot_or_dot_dot(const char *path) {
+ if (!path)
+ return false;
+ if (path[0] != '.')
+ return false;
+ if (path[1] == 0)
+ return true;
+ if (path[1] != '.')
+ return false;
+
+ return path[2] == 0;
+}
diff --git a/src/basic/path-util.h b/src/basic/path-util.h
index 66545f52d9..35aef3adc8 100644
--- a/src/basic/path-util.h
+++ b/src/basic/path-util.h
@@ -24,6 +24,7 @@
#include <stddef.h>
#include "macro.h"
+#include "string-util.h"
#include "time-util.h"
#define DEFAULT_PATH_NORMAL "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"
@@ -65,9 +66,21 @@ static inline bool path_equal_ptr(const char *a, const char *b) {
_found; \
})
+#define PATH_STARTSWITH_SET(p, ...) \
+ ({ \
+ char **s; \
+ bool _found = false; \
+ STRV_FOREACH(s, STRV_MAKE(__VA_ARGS__)) \
+ if (path_startswith(p, *s)) { \
+ _found = true; \
+ break; \
+ } \
+ _found; \
+ })
+
int path_strv_make_absolute_cwd(char **l);
-char** path_strv_resolve(char **l, const char *prefix);
-char** path_strv_resolve_uniq(char **l, const char *prefix);
+char** path_strv_resolve(char **l, const char *root);
+char** path_strv_resolve_uniq(char **l, const char *root);
int find_binary(const char *name, char **filename);
@@ -128,3 +141,5 @@ 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);
+
+bool dot_or_dot_dot(const char *path);
diff --git a/src/basic/proc-cmdline.c b/src/basic/proc-cmdline.c
index 8297a222b7..8592a428d5 100644
--- a/src/basic/proc-cmdline.c
+++ b/src/basic/proc-cmdline.c
@@ -34,17 +34,30 @@
#include "virt.h"
int proc_cmdline(char **ret) {
+ const char *e;
assert(ret);
+ /* For testing purposes it is sometimes useful to be able to override what we consider /proc/cmdline to be */
+ e = secure_getenv("SYSTEMD_PROC_CMDLINE");
+ if (e) {
+ char *m;
+
+ m = strdup(e);
+ if (!m)
+ return -ENOMEM;
+
+ *ret = m;
+ return 0;
+ }
+
if (detect_container() > 0)
return get_process_cmdline(1, 0, false, ret);
else
return read_one_line_file("/proc/cmdline", ret);
}
-int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value, void *data),
- void *data,
- bool strip_prefix) {
+int proc_cmdline_parse(proc_cmdline_parse_t parse_item, void *data, unsigned flags) {
+
_cleanup_free_ char *line = NULL;
const char *p;
int r;
@@ -58,7 +71,7 @@ int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value, voi
p = line;
for (;;) {
_cleanup_free_ char *word = NULL;
- char *value = NULL, *unprefixed;
+ char *value, *key, *q;
r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
if (r < 0)
@@ -66,17 +79,23 @@ int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value, voi
if (r == 0)
break;
- /* Filter out arguments that are intended only for the
- * initrd */
- unprefixed = startswith(word, "rd.");
- if (unprefixed && !in_initrd())
- continue;
+ key = word;
+
+ /* Filter out arguments that are intended only for the initrd */
+ q = startswith(word, "rd.");
+ if (q) {
+ if (!in_initrd())
+ continue;
+
+ if (flags & PROC_CMDLINE_STRIP_RD_PREFIX)
+ key = q;
+ }
- value = strchr(word, '=');
+ value = strchr(key, '=');
if (value)
*(value++) = 0;
- r = parse_item(strip_prefix && unprefixed ? unprefixed : word, value, data);
+ r = parse_item(key, value, data);
if (r < 0)
return r;
}
@@ -84,13 +103,64 @@ int parse_proc_cmdline(int (*parse_item)(const char *key, const char *value, voi
return 0;
}
-int get_proc_cmdline_key(const char *key, char **value) {
+static bool relaxed_equal_char(char a, char b) {
+
+ return a == b ||
+ (a == '_' && b == '-') ||
+ (a == '-' && b == '_');
+}
+
+char *proc_cmdline_key_startswith(const char *s, const char *prefix) {
+
+ assert(s);
+ assert(prefix);
+
+ /* Much like startswith(), but considers "-" and "_" the same */
+
+ for (; *prefix != 0; s++, prefix++)
+ if (!relaxed_equal_char(*s, *prefix))
+ return NULL;
+
+ return (char*) s;
+}
+
+bool proc_cmdline_key_streq(const char *x, const char *y) {
+ assert(x);
+ assert(y);
+
+ /* Much like streq(), but considers "-" and "_" the same */
+
+ for (; *x != 0 || *y != 0; x++, y++)
+ if (!relaxed_equal_char(*x, *y))
+ return false;
+
+ return true;
+}
+
+int proc_cmdline_get_key(const char *key, unsigned flags, char **value) {
_cleanup_free_ char *line = NULL, *ret = NULL;
bool found = false;
const char *p;
int r;
- assert(key);
+ /* Looks for a specific key on the kernel command line. Supports two modes:
+ *
+ * a) The "value" parameter is used. In this case a parameter beginning with the "key" string followed by "="
+ * is searched, and the value following this is returned in "value".
+ *
+ * b) as above, but the PROC_CMDLINE_VALUE_OPTIONAL flag is set. In this case if the key is found as a
+ * separate word (i.e. not followed by "=" but instead by whitespace or the end of the command line), then
+ * this is also accepted, and "value" is returned as NULL.
+ *
+ * c) The "value" parameter is NULL. In this case a search for the exact "key" parameter is performed.
+ *
+ * In all three cases, > 0 is returned if the key is found, 0 if not. */
+
+ if (isempty(key))
+ return -EINVAL;
+
+ if ((flags & PROC_CMDLINE_VALUE_OPTIONAL) && !value)
+ return -EINVAL;
r = proc_cmdline(&line);
if (r < 0)
@@ -107,21 +177,26 @@ int get_proc_cmdline_key(const char *key, char **value) {
if (r == 0)
break;
- /* Filter out arguments that are intended only for the
- * initrd */
+ /* Automatically filter out arguments that are intended only for the initrd, if we are not in the
+ * initrd. */
if (!in_initrd() && startswith(word, "rd."))
continue;
if (value) {
- e = startswith(word, key);
+ e = proc_cmdline_key_startswith(word, key);
if (!e)
continue;
- r = free_and_strdup(&ret, e);
- if (r < 0)
- return r;
+ if (*e == '=') {
+ r = free_and_strdup(&ret, e+1);
+ if (r < 0)
+ return r;
+
+ found = true;
+
+ } else if (*e == 0 && (flags & PROC_CMDLINE_VALUE_OPTIONAL))
+ found = true;
- found = true;
} else {
if (streq(word, key))
found = true;
@@ -134,20 +209,42 @@ int get_proc_cmdline_key(const char *key, char **value) {
}
return found;
+}
+
+int proc_cmdline_get_bool(const char *key, bool *ret) {
+ _cleanup_free_ char *v = NULL;
+ int r;
+
+ assert(ret);
+
+ r = proc_cmdline_get_key(key, PROC_CMDLINE_VALUE_OPTIONAL, &v);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ *ret = false;
+ return 0;
+ }
+
+ if (v) { /* parameter passed */
+ r = parse_boolean(v);
+ if (r < 0)
+ return r;
+ *ret = r;
+ } else /* no parameter passed */
+ *ret = true;
+ return 1;
}
int shall_restore_state(void) {
- _cleanup_free_ char *value = NULL;
+ bool ret;
int r;
- r = get_proc_cmdline_key("systemd.restore_state=", &value);
+ r = proc_cmdline_get_bool("systemd.restore_state", &ret);
if (r < 0)
return r;
- if (r == 0)
- return true;
- return parse_boolean(value);
+ return r > 0 ? ret : true;
}
static const char * const rlmap[] = {
diff --git a/src/basic/proc-cmdline.h b/src/basic/proc-cmdline.h
index 6d6ee95c11..ebfed355e9 100644
--- a/src/basic/proc-cmdline.h
+++ b/src/basic/proc-cmdline.h
@@ -19,11 +19,36 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include <stdbool.h>
+
+#include "log.h"
+
+enum {
+ PROC_CMDLINE_STRIP_RD_PREFIX = 1,
+ PROC_CMDLINE_VALUE_OPTIONAL = 2,
+};
+
+typedef int (*proc_cmdline_parse_t)(const char *key, const char *value, void *data);
+
int proc_cmdline(char **ret);
-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 proc_cmdline_parse(const proc_cmdline_parse_t parse, void *userdata, unsigned flags);
+
+int proc_cmdline_get_key(const char *parameter, unsigned flags, char **value);
+int proc_cmdline_get_bool(const char *key, bool *ret);
+
+char *proc_cmdline_key_startswith(const char *s, const char *prefix);
+bool proc_cmdline_key_streq(const char *x, const char *y);
int shall_restore_state(void);
const char* runlevel_to_target(const char *rl);
+
+/* A little helper call, to be used in proc_cmdline_parse_t callbacks */
+static inline bool proc_cmdline_value_missing(const char *key, const char *value) {
+ if (!value) {
+ log_warning("Missing argument for %s= kernel command line switch, ignoring.", key);
+ return true;
+ }
+
+ return false;
+}
diff --git a/src/basic/process-util.c b/src/basic/process-util.c
index 54b644ad56..0df3fed640 100644
--- a/src/basic/process-util.c
+++ b/src/basic/process-util.c
@@ -27,6 +27,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/mman.h>
#include <sys/personality.h>
#include <sys/prctl.h>
#include <sys/types.h>
@@ -103,7 +104,7 @@ int get_process_comm(pid_t pid, char **name) {
int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) {
_cleanup_fclose_ FILE *f = NULL;
bool space = false;
- char *r = NULL, *k;
+ char *k, *ans = NULL;
const char *p;
int c;
@@ -117,7 +118,7 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
* command line that resolves to the empty string will return the "comm" name of the process instead.
*
* Returns -ESRCH if the process doesn't exist, and -ENOENT if the process has no command line (and
- * comm_fallback is false). */
+ * comm_fallback is false). Returns 0 and sets *line otherwise. */
p = procfs_file_alloca(pid, "cmdline");
@@ -131,11 +132,11 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
if (max_length == 1) {
/* If there's only room for one byte, return the empty string */
- r = new0(char, 1);
- if (!r)
+ ans = new0(char, 1);
+ if (!ans)
return -ENOMEM;
- *line = r;
+ *line = ans;
return 0;
} else if (max_length == 0) {
@@ -143,36 +144,36 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
while ((c = getc(f)) != EOF) {
- if (!GREEDY_REALLOC(r, allocated, len+3)) {
- free(r);
+ if (!GREEDY_REALLOC(ans, allocated, len+3)) {
+ free(ans);
return -ENOMEM;
}
if (isprint(c)) {
if (space) {
- r[len++] = ' ';
+ ans[len++] = ' ';
space = false;
}
- r[len++] = c;
+ ans[len++] = c;
} else if (len > 0)
space = true;
}
if (len > 0)
- r[len] = 0;
+ ans[len] = '\0';
else
- r = mfree(r);
+ ans = mfree(ans);
} else {
bool dotdotdot = false;
size_t left;
- r = new(char, max_length);
- if (!r)
+ ans = new(char, max_length);
+ if (!ans)
return -ENOMEM;
- k = r;
+ k = ans;
left = max_length;
while ((c = getc(f)) != EOF) {
@@ -196,20 +197,20 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
*(k++) = (char) c;
left--;
- } else if (k > r)
+ } else if (k > ans)
space = true;
}
if (dotdotdot) {
if (max_length <= 4) {
- k = r;
+ k = ans;
left = max_length;
} else {
- k = r + max_length - 4;
+ k = ans + max_length - 4;
left = 4;
/* Eat up final spaces */
- while (k > r && isspace(k[-1])) {
+ while (k > ans && isspace(k[-1])) {
k--;
left++;
}
@@ -222,11 +223,11 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
}
/* Kernel threads have no argv[] */
- if (isempty(r)) {
+ if (isempty(ans)) {
_cleanup_free_ char *t = NULL;
int h;
- free(r);
+ free(ans);
if (!comm_fallback)
return -ENOENT;
@@ -236,22 +237,22 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
return h;
if (max_length == 0)
- r = strjoin("[", t, "]", NULL);
+ ans = strjoin("[", t, "]");
else {
size_t l;
l = strlen(t);
if (l + 3 <= max_length)
- r = strjoin("[", t, "]", NULL);
+ ans = strjoin("[", t, "]");
else if (max_length <= 6) {
- r = new(char, max_length);
- if (!r)
+ ans = new(char, max_length);
+ if (!ans)
return -ENOMEM;
- memcpy(r, "[...]", max_length-1);
- r[max_length-1] = 0;
+ memcpy(ans, "[...]", max_length-1);
+ ans[max_length-1] = 0;
} else {
char *e;
@@ -263,38 +264,111 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char *
e--;
*e = 0;
- r = strjoin("[", t, "...]", NULL);
+ ans = strjoin("[", t, "...]");
}
}
- if (!r)
+ if (!ans)
return -ENOMEM;
}
- *line = r;
+ *line = ans;
return 0;
}
-void rename_process(const char name[8]) {
- assert(name);
+int rename_process(const char name[]) {
+ static size_t mm_size = 0;
+ static char *mm = NULL;
+ bool truncated = false;
+ size_t l;
+
+ /* This is a like a poor man's setproctitle(). It changes the comm field, argv[0], and also the glibc's
+ * internally used name of the process. For the first one a limit of 16 chars applies; to the second one in
+ * many cases one of 10 (i.e. length of "/sbin/init") — however if we have CAP_SYS_RESOURCES it is unbounded;
+ * to the third one 7 (i.e. the length of "systemd". If you pass a longer string it will likely be
+ * truncated.
+ *
+ * Returns 0 if a name was set but truncated, > 0 if it was set but not truncated. */
+
+ if (isempty(name))
+ return -EINVAL; /* let's not confuse users unnecessarily with an empty name */
- /* This is a like a poor man's setproctitle(). It changes the
- * comm field, argv[0], and also the glibc's internally used
- * name of the process. For the first one a limit of 16 chars
- * applies, to the second one usually one of 10 (i.e. length
- * of "/sbin/init"), to the third one one of 7 (i.e. length of
- * "systemd"). If you pass a longer string it will be
- * truncated */
+ l = strlen(name);
+ /* First step, change the comm field. */
(void) prctl(PR_SET_NAME, name);
+ if (l > 15) /* Linux process names can be 15 chars at max */
+ truncated = true;
+
+ /* Second step, change glibc's ID of the process name. */
+ if (program_invocation_name) {
+ size_t k;
+
+ k = strlen(program_invocation_name);
+ strncpy(program_invocation_name, name, k);
+ if (l > k)
+ truncated = true;
+ }
+
+ /* Third step, completely replace the argv[] array the kernel maintains for us. This requires privileges, but
+ * has the advantage that the argv[] array is exactly what we want it to be, and not filled up with zeros at
+ * the end. This is the best option for changing /proc/self/cmdline. */
+ if (mm_size < l+1) {
+ size_t nn_size;
+ char *nn;
+
+ /* Let's not bother with this if we don't have euid == 0. Strictly speaking if people do weird stuff
+ * with capabilities this could work even for euid != 0, but our own code generally doesn't do that,
+ * hence let's use this as quick bypass check, to avoid calling mmap() if PR_SET_MM_ARG_START fails
+ * with EPERM later on anyway. After all geteuid() is dead cheap to call, but mmap() is not. */
+ if (geteuid() != 0) {
+ log_debug("Skipping PR_SET_MM_ARG_START, as we don't have privileges.");
+ goto use_saved_argv;
+ }
+
+ nn_size = PAGE_ALIGN(l+1);
+ nn = mmap(NULL, nn_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+ if (nn == MAP_FAILED) {
+ log_debug_errno(errno, "mmap() failed: %m");
+ goto use_saved_argv;
+ }
+
+ strncpy(nn, name, nn_size);
+
+ /* Now, let's tell the kernel about this new memory */
+ if (prctl(PR_SET_MM, PR_SET_MM_ARG_START, (unsigned long) nn, 0, 0) < 0) {
+ log_debug_errno(errno, "PR_SET_MM_ARG_START failed, proceeding without: %m");
+ (void) munmap(nn, nn_size);
+ goto use_saved_argv;
+ }
+
+ /* And update the end pointer to the new end, too. If this fails, we don't really know what to do, it's
+ * pretty unlikely that we can rollback, hence we'll just accept the failure, and continue. */
+ if (prctl(PR_SET_MM, PR_SET_MM_ARG_END, (unsigned long) nn + l + 1, 0, 0) < 0)
+ log_debug_errno(errno, "PR_SET_MM_ARG_END failed, proceeding without: %m");
- if (program_invocation_name)
- strncpy(program_invocation_name, name, strlen(program_invocation_name));
+ if (mm)
+ (void) munmap(mm, mm_size);
+
+ mm = nn;
+ mm_size = nn_size;
+ } else
+ strncpy(mm, name, mm_size);
+
+use_saved_argv:
+ /* Fourth step: in all cases we'll also update the original argv[], so that our own code gets it right too if
+ * it still looks here */
if (saved_argc > 0) {
int i;
- if (saved_argv[0])
- strncpy(saved_argv[0], name, strlen(saved_argv[0]));
+ if (saved_argv[0]) {
+ size_t k;
+
+ k = strlen(saved_argv[0]);
+ strncpy(saved_argv[0], name, k);
+ if (l > k)
+ truncated = true;
+ }
for (i = 1; i < saved_argc; i++) {
if (!saved_argv[i])
@@ -303,6 +377,8 @@ void rename_process(const char name[8]) {
memzero(saved_argv[i], strlen(saved_argv[i]));
}
}
+
+ return !truncated;
}
int is_kernel_thread(pid_t pid) {
@@ -627,7 +703,7 @@ int kill_and_sigcont(pid_t pid, int sig) {
/* If this worked, also send SIGCONT, unless we already just sent a SIGCONT, or SIGKILL was sent which isn't
* affected by a process being suspended anyway. */
- if (r >= 0 && !IN_SET(SIGCONT, SIGKILL))
+ if (r >= 0 && !IN_SET(sig, SIGCONT, SIGKILL))
(void) kill(pid, SIGCONT);
return r;
@@ -675,7 +751,7 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value) {
}
line[i] = 0;
- if (memcmp(line, field, l) == 0 && line[l] == '=') {
+ if (strneq(line, field, l) && line[l] == '=') {
value = strdup(line + l + 1);
if (!value)
return -ENOMEM;
diff --git a/src/basic/process-util.h b/src/basic/process-util.h
index 2568e3834f..d378901399 100644
--- a/src/basic/process-util.h
+++ b/src/basic/process-util.h
@@ -28,7 +28,7 @@
#include <sys/types.h>
#include <sys/resource.h>
-#include "formats-util.h"
+#include "format-util.h"
#include "macro.h"
#define procfs_file_alloca(pid, field) \
@@ -64,7 +64,7 @@ void sigkill_waitp(pid_t *pid);
int kill_and_sigcont(pid_t pid, int sig);
-void rename_process(const char name[8]);
+int rename_process(const char name[]);
int is_kernel_thread(pid_t pid);
int getenv_for_pid(pid_t pid, const char *field, char **_value);
diff --git a/src/basic/raw-clone.h b/src/basic/raw-clone.h
index d473828999..c6e531ada4 100644
--- a/src/basic/raw-clone.h
+++ b/src/basic/raw-clone.h
@@ -47,8 +47,8 @@
static inline int raw_clone(unsigned long flags) {
assert((flags & (CLONE_VM|CLONE_PARENT_SETTID|CLONE_CHILD_SETTID|
CLONE_CHILD_CLEARTID|CLONE_SETTLS)) == 0);
-#if defined(__s390__) || defined(__CRIS__)
- /* On s390 and cris the order of the first and second arguments
+#if defined(__s390x__) || defined(__s390__) || defined(__CRIS__)
+ /* On s390/s390x and cris the order of the first and second arguments
* of the raw clone() system call is reversed. */
return (int) syscall(__NR_clone, NULL, flags);
#elif defined(__sparc__) && defined(__arch64__)
diff --git a/src/basic/rlimit-util.c b/src/basic/rlimit-util.c
index ee063720ed..ca834df621 100644
--- a/src/basic/rlimit-util.c
+++ b/src/basic/rlimit-util.c
@@ -22,7 +22,7 @@
#include "alloc-util.h"
#include "extract-word.h"
-#include "formats-util.h"
+#include "format-util.h"
#include "macro.h"
#include "missing.h"
#include "rlimit-util.h"
diff --git a/src/basic/rm-rf.c b/src/basic/rm-rf.c
index baa70c2c8d..08497af729 100644
--- a/src/basic/rm-rf.c
+++ b/src/basic/rm-rf.c
@@ -17,7 +17,6 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
@@ -28,6 +27,7 @@
#include "btrfs-util.h"
#include "cgroup-util.h"
+#include "dirent-util.h"
#include "fd-util.h"
#include "log.h"
#include "macro.h"
@@ -43,6 +43,7 @@ static bool is_physical_fs(const struct statfs *sfs) {
int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
_cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
int ret = 0, r;
struct statfs sfs;
@@ -78,20 +79,11 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
return errno == ENOENT ? 0 : -errno;
}
- for (;;) {
- struct dirent *de;
+ FOREACH_DIRENT_ALL(de, d, return -errno) {
bool is_dir;
struct stat st;
- errno = 0;
- de = readdir(d);
- if (!de) {
- if (errno > 0 && ret == 0)
- ret = -errno;
- return ret;
- }
-
- if (streq(de->d_name, ".") || streq(de->d_name, ".."))
+ if (dot_or_dot_dot(de->d_name))
continue;
if (de->d_type == DT_UNKNOWN ||
@@ -178,6 +170,7 @@ int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev) {
}
}
}
+ return ret;
}
int rm_rf(const char *path, RemoveFlags flags) {
diff --git a/src/basic/rm-rf.h b/src/basic/rm-rf.h
index f693a5bb7c..e13f7003e3 100644
--- a/src/basic/rm-rf.h
+++ b/src/basic/rm-rf.h
@@ -33,8 +33,6 @@ int rm_rf(const char *path, RemoveFlags flags);
/* Useful for usage with _cleanup_(), destroys a directory and frees the pointer */
static inline void rm_rf_physical_and_free(char *p) {
- if (!p)
- return;
(void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL);
free(p);
}
diff --git a/src/basic/siphash24.c b/src/basic/siphash24.c
index 8c1cdc3db6..4bb41786c8 100644
--- a/src/basic/siphash24.c
+++ b/src/basic/siphash24.c
@@ -127,18 +127,25 @@ void siphash24_compress(const void *_in, size_t inlen, struct siphash *state) {
switch (left) {
case 7:
state->padding |= ((uint64_t) in[6]) << 48;
+ /* fall through */
case 6:
state->padding |= ((uint64_t) in[5]) << 40;
+ /* fall through */
case 5:
state->padding |= ((uint64_t) in[4]) << 32;
+ /* fall through */
case 4:
state->padding |= ((uint64_t) in[3]) << 24;
+ /* fall through */
case 3:
state->padding |= ((uint64_t) in[2]) << 16;
+ /* fall through */
case 2:
state->padding |= ((uint64_t) in[1]) << 8;
+ /* fall through */
case 1:
state->padding |= ((uint64_t) in[0]);
+ /* fall through */
case 0:
break;
}
diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c
index 1662c04705..e5847dce00 100644
--- a/src/basic/socket-util.c
+++ b/src/basic/socket-util.c
@@ -34,7 +34,7 @@
#include "alloc-util.h"
#include "fd-util.h"
#include "fileio.h"
-#include "formats-util.h"
+#include "format-util.h"
#include "log.h"
#include "macro.h"
#include "missing.h"
@@ -113,6 +113,30 @@ int socket_address_parse(SocketAddress *a, const char *s) {
memcpy(a->sockaddr.un.sun_path+1, s+1, l);
a->size = offsetof(struct sockaddr_un, sun_path) + 1 + l;
+ } else if (startswith(s, "vsock:")) {
+ /* AF_VSOCK socket in vsock:cid:port notation */
+ const char *cid_start = s + strlen("vsock:");
+
+ e = strchr(cid_start, ':');
+ if (!e)
+ return -EINVAL;
+
+ r = safe_atou(e+1, &u);
+ if (r < 0)
+ return r;
+
+ n = strndupa(cid_start, e - cid_start);
+ if (!isempty(n)) {
+ r = safe_atou(n, &a->sockaddr.vm.svm_cid);
+ if (r < 0)
+ return r;
+ } else
+ a->sockaddr.vm.svm_cid = VMADDR_CID_ANY;
+
+ a->sockaddr.vm.svm_family = AF_VSOCK;
+ a->sockaddr.vm.svm_port = u;
+ a->size = sizeof(struct sockaddr_vm);
+
} else {
e = strchr(s, ':');
if (e) {
@@ -289,6 +313,15 @@ int socket_address_verify(const SocketAddress *a) {
return 0;
+ case AF_VSOCK:
+ if (a->size != sizeof(struct sockaddr_vm))
+ return -EINVAL;
+
+ if (a->type != SOCK_STREAM && a->type != SOCK_DGRAM)
+ return -EINVAL;
+
+ return 0;
+
default:
return -EAFNOSUPPORT;
}
@@ -394,6 +427,15 @@ bool socket_address_equal(const SocketAddress *a, const SocketAddress *b) {
break;
+ case AF_VSOCK:
+ if (a->sockaddr.vm.svm_cid != b->sockaddr.vm.svm_cid)
+ return false;
+
+ if (a->sockaddr.vm.svm_port != b->sockaddr.vm.svm_port)
+ return false;
+
+ break;
+
default:
/* Cannot compare, so we assume the addresses are different */
return false;
@@ -480,15 +522,27 @@ bool socket_address_matches_fd(const SocketAddress *a, int fd) {
return socket_address_equal(a, &b);
}
-int sockaddr_port(const struct sockaddr *_sa) {
+int sockaddr_port(const struct sockaddr *_sa, unsigned *port) {
union sockaddr_union *sa = (union sockaddr_union*) _sa;
assert(sa);
- if (!IN_SET(sa->sa.sa_family, AF_INET, AF_INET6))
- return -EAFNOSUPPORT;
+ switch (sa->sa.sa_family) {
+ case AF_INET:
+ *port = be16toh(sa->in.sin_port);
+ return 0;
+
+ case AF_INET6:
+ *port = be16toh(sa->in6.sin6_port);
+ return 0;
- return be16toh(sa->sa.sa_family == AF_INET6 ? sa->in6.sin6_port : sa->in.sin_port);
+ case AF_VSOCK:
+ *port = sa->vm.svm_port;
+ return 0;
+
+ default:
+ return -EAFNOSUPPORT;
+ }
}
int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret) {
@@ -591,6 +645,18 @@ int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_
break;
+ case AF_VSOCK:
+ if (include_port)
+ r = asprintf(&p,
+ "vsock:%u:%u",
+ sa->vm.svm_cid,
+ sa->vm.svm_port);
+ else
+ r = asprintf(&p, "vsock:%u", sa->vm.svm_cid);
+ if (r < 0)
+ return -ENOMEM;
+ break;
+
default:
return -EOPNOTSUPP;
}
@@ -748,6 +814,9 @@ bool sockaddr_equal(const union sockaddr_union *a, const union sockaddr_union *b
if (a->sa.sa_family == AF_INET6)
return memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr)) == 0;
+ if (a->sa.sa_family == AF_VSOCK)
+ return a->vm.svm_cid == b->vm.svm_cid;
+
return false;
}
@@ -808,7 +877,7 @@ bool ifname_valid(const char *p) {
if (strlen(p) >= IFNAMSIZ)
return false;
- if (STR_IN_SET(p, ".", ".."))
+ if (dot_or_dot_dot(p))
return false;
while (*p) {
@@ -831,6 +900,26 @@ bool ifname_valid(const char *p) {
return true;
}
+bool address_label_valid(const char *p) {
+
+ if (isempty(p))
+ return false;
+
+ if (strlen(p) >= IFNAMSIZ)
+ return false;
+
+ while (*p) {
+ if ((uint8_t) *p >= 127U)
+ return false;
+
+ if ((uint8_t) *p <= 31U)
+ return false;
+ p++;
+ }
+
+ return true;
+}
+
int getpeercred(int fd, struct ucred *ucred) {
socklen_t n = sizeof(struct ucred);
struct ucred u;
diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h
index 2ef572badb..73c3a339fc 100644
--- a/src/basic/socket-util.h
+++ b/src/basic/socket-util.h
@@ -30,6 +30,7 @@
#include <linux/if_packet.h>
#include "macro.h"
+#include "missing.h"
#include "util.h"
union sockaddr_union {
@@ -40,6 +41,7 @@ union sockaddr_union {
struct sockaddr_nl nl;
struct sockaddr_storage storage;
struct sockaddr_ll ll;
+ struct sockaddr_vm vm;
};
typedef struct SocketAddress {
@@ -100,7 +102,7 @@ const char* socket_address_get_path(const SocketAddress *a);
bool socket_ipv6_is_supported(void);
-int sockaddr_port(const struct sockaddr *_sa) _pure_;
+int sockaddr_port(const struct sockaddr *_sa, unsigned *port);
int sockaddr_pretty(const struct sockaddr *_sa, socklen_t salen, bool translate_ipv6, bool include_port, char **ret);
int getpeername_pretty(int fd, bool include_port, char **ret);
@@ -124,6 +126,7 @@ int ip_tos_to_string_alloc(int i, char **s);
int ip_tos_from_string(const char *s);
bool ifname_valid(const char *p);
+bool address_label_valid(const char *p);
int getpeercred(int fd, struct ucred *ucred);
int getpeersec(int fd, char **ret);
diff --git a/src/basic/sparse-endian.h b/src/basic/sparse-endian.h
index c913fda8c5..a3573b84a9 100644
--- a/src/basic/sparse-endian.h
+++ b/src/basic/sparse-endian.h
@@ -26,19 +26,19 @@
#include <stdint.h>
#ifdef __CHECKER__
-#define __bitwise __attribute__((bitwise))
-#define __force __attribute__((force))
+#define __sd_bitwise __attribute__((bitwise))
+#define __sd_force __attribute__((force))
#else
-#define __bitwise
-#define __force
+#define __sd_bitwise
+#define __sd_force
#endif
-typedef uint16_t __bitwise le16_t;
-typedef uint16_t __bitwise be16_t;
-typedef uint32_t __bitwise le32_t;
-typedef uint32_t __bitwise be32_t;
-typedef uint64_t __bitwise le64_t;
-typedef uint64_t __bitwise be64_t;
+typedef uint16_t __sd_bitwise le16_t;
+typedef uint16_t __sd_bitwise be16_t;
+typedef uint32_t __sd_bitwise le32_t;
+typedef uint32_t __sd_bitwise be32_t;
+typedef uint64_t __sd_bitwise le64_t;
+typedef uint64_t __sd_bitwise be64_t;
#undef htobe16
#undef htole16
@@ -69,20 +69,23 @@ typedef uint64_t __bitwise be64_t;
#define bswap_64_on_be(x) __bswap_64(x)
#endif
-static inline le16_t htole16(uint16_t value) { return (le16_t __force) bswap_16_on_be(value); }
-static inline le32_t htole32(uint32_t value) { return (le32_t __force) bswap_32_on_be(value); }
-static inline le64_t htole64(uint64_t value) { return (le64_t __force) bswap_64_on_be(value); }
+static inline le16_t htole16(uint16_t value) { return (le16_t __sd_force) bswap_16_on_be(value); }
+static inline le32_t htole32(uint32_t value) { return (le32_t __sd_force) bswap_32_on_be(value); }
+static inline le64_t htole64(uint64_t value) { return (le64_t __sd_force) bswap_64_on_be(value); }
-static inline be16_t htobe16(uint16_t value) { return (be16_t __force) bswap_16_on_le(value); }
-static inline be32_t htobe32(uint32_t value) { return (be32_t __force) bswap_32_on_le(value); }
-static inline be64_t htobe64(uint64_t value) { return (be64_t __force) bswap_64_on_le(value); }
+static inline be16_t htobe16(uint16_t value) { return (be16_t __sd_force) bswap_16_on_le(value); }
+static inline be32_t htobe32(uint32_t value) { return (be32_t __sd_force) bswap_32_on_le(value); }
+static inline be64_t htobe64(uint64_t value) { return (be64_t __sd_force) bswap_64_on_le(value); }
-static inline uint16_t le16toh(le16_t value) { return bswap_16_on_be((uint16_t __force)value); }
-static inline uint32_t le32toh(le32_t value) { return bswap_32_on_be((uint32_t __force)value); }
-static inline uint64_t le64toh(le64_t value) { return bswap_64_on_be((uint64_t __force)value); }
+static inline uint16_t le16toh(le16_t value) { return bswap_16_on_be((uint16_t __sd_force)value); }
+static inline uint32_t le32toh(le32_t value) { return bswap_32_on_be((uint32_t __sd_force)value); }
+static inline uint64_t le64toh(le64_t value) { return bswap_64_on_be((uint64_t __sd_force)value); }
-static inline uint16_t be16toh(be16_t value) { return bswap_16_on_le((uint16_t __force)value); }
-static inline uint32_t be32toh(be32_t value) { return bswap_32_on_le((uint32_t __force)value); }
-static inline uint64_t be64toh(be64_t value) { return bswap_64_on_le((uint64_t __force)value); }
+static inline uint16_t be16toh(be16_t value) { return bswap_16_on_le((uint16_t __sd_force)value); }
+static inline uint32_t be32toh(be32_t value) { return bswap_32_on_le((uint32_t __sd_force)value); }
+static inline uint64_t be64toh(be64_t value) { return bswap_64_on_le((uint64_t __sd_force)value); }
+
+#undef __sd_bitwise
+#undef __sd_force
#endif /* SPARSE_ENDIAN_H */
diff --git a/src/basic/special.h b/src/basic/special.h
index 5276bcf598..feb8e5fe21 100644
--- a/src/basic/special.h
+++ b/src/basic/special.h
@@ -103,6 +103,7 @@
#define SPECIAL_DBUS_SOCKET "dbus.socket"
#define SPECIAL_JOURNALD_SOCKET "systemd-journald.socket"
#define SPECIAL_JOURNALD_SERVICE "systemd-journald.service"
+#define SPECIAL_TMPFILES_SETUP_SERVICE "systemd-tmpfiles-setup.service"
/* Magic init signals */
#define SPECIAL_KBREQUEST_TARGET "kbrequest.target"
diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c
index 309e84b93d..7e1914aa14 100644
--- a/src/basic/stat-util.c
+++ b/src/basic/stat-util.c
@@ -28,6 +28,7 @@
#include "dirent-util.h"
#include "fd-util.h"
+#include "fs-util.h"
#include "macro.h"
#include "missing.h"
#include "stat-util.h"
@@ -143,22 +144,29 @@ int path_is_read_only_fs(const char *path) {
}
int path_is_os_tree(const char *path) {
- char *p;
int r;
assert(path);
- /* We use /usr/lib/os-release as flag file if something is an OS */
- p = strjoina(path, "/usr/lib/os-release");
- r = access(p, F_OK);
- if (r >= 0)
- return 1;
+ /* Does the path exist at all? If not, generate an error immediately. This is useful so that a missing root dir
+ * always results in -ENOENT, and we can properly distuingish the case where the whole root doesn't exist from
+ * the case where just the os-release file is missing. */
+ if (laccess(path, F_OK) < 0)
+ return -errno;
- /* Also check for the old location in /etc, just in case. */
- p = strjoina(path, "/etc/os-release");
- r = access(p, F_OK);
+ /* We use /usr/lib/os-release as flag file if something is an OS */
+ r = chase_symlinks("/usr/lib/os-release", path, CHASE_PREFIX_ROOT, NULL);
+ if (r == -ENOENT) {
+
+ /* Also check for the old location in /etc, just in case. */
+ r = chase_symlinks("/etc/os-release", path, CHASE_PREFIX_ROOT, NULL);
+ if (r == -ENOENT)
+ return 0; /* We got nothing */
+ }
+ if (r < 0)
+ return r;
- return r >= 0;
+ return 1;
}
int files_same(const char *filea, const char *fileb) {
@@ -196,7 +204,7 @@ int fd_check_fstype(int fd, statfs_f_type_t magic_value) {
int path_check_fstype(const char *path, statfs_f_type_t magic_value) {
_cleanup_close_ int fd = -1;
- fd = open(path, O_RDONLY);
+ fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
if (fd < 0)
return -errno;
@@ -216,3 +224,13 @@ int fd_is_temporary_fs(int fd) {
return is_temporary_fs(&s);
}
+
+int path_is_temporary_fs(const char *path) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_PATH);
+ if (fd < 0)
+ return -errno;
+
+ return fd_is_temporary_fs(fd);
+}
diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h
index 56d28f791e..5d571efe18 100644
--- a/src/basic/stat-util.h
+++ b/src/basic/stat-util.h
@@ -61,6 +61,7 @@ int path_check_fstype(const char *path, statfs_f_type_t magic_value);
bool is_temporary_fs(const struct statfs *s) _pure_;
int fd_is_temporary_fs(int fd);
+int path_is_temporary_fs(const char *path);
/* Because statfs.t_type can be int on some architectures, we have to cast
* the const magic to the type, otherwise the compiler warns about
diff --git a/src/basic/string-util.c b/src/basic/string-util.c
index 6b06e643c9..9d2f4bc8f9 100644
--- a/src/basic/string-util.c
+++ b/src/basic/string-util.c
@@ -218,7 +218,7 @@ char *strappend(const char *s, const char *suffix) {
return strnappend(s, suffix, suffix ? strlen(suffix) : 0);
}
-char *strjoin(const char *x, ...) {
+char *strjoin_real(const char *x, ...) {
va_list ap;
size_t l;
char *r, *p;
@@ -821,6 +821,7 @@ int free_and_strdup(char **p, const char *s) {
return 1;
}
+#if !HAVE_DECL_EXPLICIT_BZERO
/*
* Pointer to memset is volatile so that compiler must de-reference
* the pointer and can't assume that it points to any function in
@@ -831,19 +832,19 @@ typedef void *(*memset_t)(void *,int,size_t);
static volatile memset_t memset_func = memset;
-void* memory_erase(void *p, size_t l) {
- return memset_func(p, 'x', l);
+void explicit_bzero(void *p, size_t l) {
+ memset_func(p, '\0', l);
}
+#endif
char* string_erase(char *x) {
-
if (!x)
return NULL;
/* A delicious drop of snake-oil! To be called on memory where
* we stored passphrases or so, after we used them. */
-
- return memory_erase(x, strlen(x));
+ explicit_bzero(x, strlen(x));
+ return x;
}
char *string_free_erase(char *s) {
diff --git a/src/basic/string-util.h b/src/basic/string-util.h
index d029d538bd..be44dedff4 100644
--- a/src/basic/string-util.h
+++ b/src/basic/string-util.h
@@ -107,16 +107,14 @@ const char* split(const char **state, size_t *l, const char *separator, bool quo
#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state) \
_FOREACH_WORD(word, length, s, separator, false, state)
-#define FOREACH_WORD_QUOTED(word, length, s, state) \
- _FOREACH_WORD(word, length, s, WHITESPACE, true, state)
-
#define _FOREACH_WORD(word, length, s, separator, quoted, state) \
for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted)))
char *strappend(const char *s, const char *suffix);
char *strnappend(const char *s, const char *suffix, size_t length);
-char *strjoin(const char *x, ...) _sentinel_;
+char *strjoin_real(const char *x, ...) _sentinel_;
+#define strjoin(a, ...) strjoin_real((a), __VA_ARGS__, NULL)
#define strjoina(a, ...) \
({ \
@@ -191,7 +189,10 @@ static inline void *memmem_safe(const void *haystack, size_t haystacklen, const
return memmem(haystack, haystacklen, needle, needlelen);
}
-void* memory_erase(void *p, size_t l);
+#if !HAVE_DECL_EXPLICIT_BZERO
+void explicit_bzero(void *p, size_t l);
+#endif
+
char *string_erase(char *x);
char *string_free_erase(char *s);
diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c
index eafdea9eb3..9a8ef825c5 100644
--- a/src/basic/terminal-util.c
+++ b/src/basic/terminal-util.c
@@ -144,12 +144,14 @@ int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
return 0;
}
-int ask_char(char *ret, const char *replies, const char *text, ...) {
+#define DEFAULT_ASK_REFRESH_USEC (2*USEC_PER_SEC)
+
+int ask_char(char *ret, const char *replies, const char *fmt, ...) {
int r;
assert(ret);
assert(replies);
- assert(text);
+ assert(fmt);
for (;;) {
va_list ap;
@@ -159,8 +161,10 @@ int ask_char(char *ret, const char *replies, const char *text, ...) {
if (colors_enabled())
fputs(ANSI_HIGHLIGHT, stdout);
- va_start(ap, text);
- vprintf(text, ap);
+ putchar('\r');
+
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
va_end(ap);
if (colors_enabled())
@@ -168,9 +172,12 @@ int ask_char(char *ret, const char *replies, const char *text, ...) {
fflush(stdout);
- r = read_one_char(stdin, &c, USEC_INFINITY, &need_nl);
+ r = read_one_char(stdin, &c, DEFAULT_ASK_REFRESH_USEC, &need_nl);
if (r < 0) {
+ if (r == -ETIMEDOUT)
+ continue;
+
if (r == -EBADMSG) {
puts("Bad input, please try again.");
continue;
@@ -455,7 +462,7 @@ int acquire_terminal(
goto fail;
}
- r = fd_wait_for_event(fd, POLLIN, ts + timeout - n);
+ r = fd_wait_for_event(notify, POLLIN, ts + timeout - n);
if (r < 0)
goto fail;
diff --git a/src/basic/time-util.c b/src/basic/time-util.c
index fedff1362c..a0db97c41a 100644
--- a/src/basic/time-util.c
+++ b/src/basic/time-util.c
@@ -185,7 +185,7 @@ usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock) {
usec_t timespec_load(const struct timespec *ts) {
assert(ts);
- if (ts->tv_sec == (time_t) -1 && ts->tv_nsec == (long) -1)
+ if (ts->tv_sec < 0 || ts->tv_nsec < 0)
return USEC_INFINITY;
if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
@@ -199,7 +199,7 @@ usec_t timespec_load(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)
+ if (ts->tv_sec < 0 || ts->tv_nsec < 0)
return NSEC_INFINITY;
if ((nsec_t) ts->tv_sec >= (UINT64_MAX - ts->tv_nsec) / NSEC_PER_SEC)
@@ -211,7 +211,8 @@ nsec_t timespec_load_nsec(const struct timespec *ts) {
struct timespec *timespec_store(struct timespec *ts, usec_t u) {
assert(ts);
- if (u == USEC_INFINITY) {
+ if (u == USEC_INFINITY ||
+ u / USEC_PER_SEC >= TIME_T_MAX) {
ts->tv_sec = (time_t) -1;
ts->tv_nsec = (long) -1;
return ts;
@@ -226,8 +227,7 @@ struct timespec *timespec_store(struct timespec *ts, usec_t u) {
usec_t timeval_load(const struct timeval *tv) {
assert(tv);
- if (tv->tv_sec == (time_t) -1 &&
- tv->tv_usec == (suseconds_t) -1)
+ if (tv->tv_sec < 0 || tv->tv_usec < 0)
return USEC_INFINITY;
if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
@@ -241,7 +241,8 @@ usec_t timeval_load(const struct timeval *tv) {
struct timeval *timeval_store(struct timeval *tv, usec_t u) {
assert(tv);
- if (u == USEC_INFINITY) {
+ if (u == USEC_INFINITY||
+ u / USEC_PER_SEC > TIME_T_MAX) {
tv->tv_sec = (time_t) -1;
tv->tv_usec = (suseconds_t) -1;
} else {
@@ -288,9 +289,11 @@ static char *format_timestamp_internal(
if (t <= 0 || t == USEC_INFINITY)
return NULL; /* Timestamp is unset */
+ /* Let's not format times with years > 9999 */
+ if (t > USEC_TIMESTAMP_FORMATTABLE_MAX)
+ return NULL;
+
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;
@@ -309,7 +312,7 @@ static char *format_timestamp_internal(
if (n + 8 > l)
return NULL; /* Microseconds part doesn't fit. */
- sprintf(buf + n, ".%06llu", (unsigned long long) (t % USEC_PER_SEC));
+ sprintf(buf + n, ".%06"PRI_USEC, t % USEC_PER_SEC);
}
/* Append the timezone */
@@ -499,11 +502,11 @@ char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
if (j > 0) {
k = snprintf(p, l,
- "%s"USEC_FMT".%0*llu%s",
+ "%s"USEC_FMT".%0*"PRI_USEC"%s",
p > buf ? " " : "",
a,
j,
- (unsigned long long) b,
+ b,
table[i].suffix);
t = 0;
@@ -551,12 +554,12 @@ void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
}
int dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
- unsigned long long a, b;
+ uint64_t a, b;
assert(value);
assert(t);
- if (sscanf(value, "%llu %llu", &a, &b) != 2) {
+ if (sscanf(value, "%" PRIu64 "%" PRIu64, &a, &b) != 2) {
log_debug("Failed to parse dual timestamp value \"%s\": %m", value);
return -EINVAL;
}
@@ -830,16 +833,23 @@ parse_usec:
from_tm:
x = mktime_or_timegm(&tm, utc);
- if (x == (time_t) -1)
+ if (x < 0)
return -EINVAL;
if (weekday >= 0 && tm.tm_wday != weekday)
return -EINVAL;
ret = (usec_t) x * USEC_PER_SEC + x_usec;
+ if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX)
+ return -EINVAL;
finish:
+ if (ret + plus < ret) /* overflow? */
+ return -EINVAL;
ret += plus;
+ if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX)
+ return -EINVAL;
+
if (ret > minus)
ret -= minus;
else
@@ -883,6 +893,7 @@ static char* extract_multiplier(char *p, usec_t *multiplier) {
{ "y", USEC_PER_YEAR },
{ "usec", 1ULL },
{ "us", 1ULL },
+ { "µs", 1ULL },
};
unsigned i;
@@ -1016,6 +1027,7 @@ int parse_nsec(const char *t, nsec_t *nsec) {
{ "y", NSEC_PER_YEAR },
{ "usec", NSEC_PER_USEC },
{ "us", NSEC_PER_USEC },
+ { "µs", NSEC_PER_USEC },
{ "nsec", 1ULL },
{ "ns", 1ULL },
{ "", 1ULL }, /* default is nsec */
@@ -1269,7 +1281,7 @@ bool clock_supported(clockid_t clock) {
if (!clock_boottime_supported())
return false;
- /* fall through, after checking the cached value for CLOCK_BOOTTIME. */
+ /* fall through */
default:
/* For everything else, check properly */
@@ -1320,7 +1332,7 @@ unsigned long usec_to_jiffies(usec_t u) {
r = sysconf(_SC_CLK_TCK);
assert(r > 0);
- hz = (unsigned long) r;
+ hz = r;
}
return DIV_ROUND_UP(u , USEC_PER_SEC / hz);
diff --git a/src/basic/time-util.h b/src/basic/time-util.h
index 558b0b5b7f..7463507f51 100644
--- a/src/basic/time-util.h
+++ b/src/basic/time-util.h
@@ -29,8 +29,10 @@
typedef uint64_t usec_t;
typedef uint64_t nsec_t;
-#define NSEC_FMT "%" PRIu64
-#define USEC_FMT "%" PRIu64
+#define PRI_NSEC PRIu64
+#define PRI_USEC PRIu64
+#define NSEC_FMT "%" PRI_NSEC
+#define USEC_FMT "%" PRI_USEC
#include "macro.h"
@@ -179,3 +181,14 @@ static inline usec_t usec_sub(usec_t timestamp, int64_t delta) {
return timestamp - delta;
}
+
+#if SIZEOF_TIME_T == 8
+/* The last second we can format is 31. Dec 9999, 1s before midnight, because otherwise we'd enter 5 digit year
+ * territory. However, since we want to stay away from this in all timezones we take one day off. */
+#define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 253402214399000000)
+#elif SIZEOF_TIME_T == 4
+/* With a 32bit time_t we can't go beyond 2038... */
+#define USEC_TIMESTAMP_FORMATTABLE_MAX ((usec_t) 2147483647000000)
+#else
+#error "Yuck, time_t is neither 4 not 8 bytes wide?"
+#endif
diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c
index fe883b95c7..0a6efa449a 100644
--- a/src/basic/unit-name.c
+++ b/src/basic/unit-name.c
@@ -273,7 +273,7 @@ int unit_name_build(const char *prefix, const char *instance, const char *suffix
if (!instance)
s = strappend(prefix, suffix);
else
- s = strjoin(prefix, "@", instance, suffix, NULL);
+ s = strjoin(prefix, "@", instance, suffix);
if (!s)
return -ENOMEM;
@@ -554,7 +554,7 @@ int unit_name_from_path_instance(const char *prefix, const char *path, const cha
if (r < 0)
return r;
- s = strjoin(prefix, "@", p, suffix, NULL);
+ s = strjoin(prefix, "@", p, suffix);
if (!s)
return -ENOMEM;
diff --git a/src/basic/user-util.c b/src/basic/user-util.c
index de6c93056e..c619dad527 100644
--- a/src/basic/user-util.c
+++ b/src/basic/user-util.c
@@ -34,7 +34,7 @@
#include "alloc-util.h"
#include "fd-util.h"
#include "fileio.h"
-#include "formats-util.h"
+#include "format-util.h"
#include "macro.h"
#include "missing.h"
#include "parse-util.h"
@@ -46,6 +46,8 @@
bool uid_is_valid(uid_t uid) {
+ /* Also see POSIX IEEE Std 1003.1-2008, 2016 Edition, 3.436. */
+
/* Some libc APIs use UID_INVALID as special placeholder */
if (uid == (uid_t) UINT32_C(0xFFFFFFFF))
return false;
@@ -519,7 +521,15 @@ bool valid_user_group_name(const char *u) {
const char *i;
long sz;
- /* Checks if the specified name is a valid user/group name. */
+ /* Checks if the specified name is a valid user/group name. Also see POSIX IEEE Std 1003.1-2008, 2016 Edition,
+ * 3.437. We are a bit stricter here however. Specifically we deviate from POSIX rules:
+ *
+ * - We don't allow any dots (this would break chown syntax which permits dots as user/group name separator)
+ * - We require that names fit into the appropriate utmp field
+ * - We don't allow empty user names
+ *
+ * Note that other systems are even more restrictive, and don't permit underscores or uppercase characters.
+ */
if (isempty(u))
return false;
diff --git a/src/basic/util.c b/src/basic/util.c
index ec7939dc83..3dce0ea92e 100644
--- a/src/basic/util.c
+++ b/src/basic/util.c
@@ -18,7 +18,6 @@
***/
#include <alloca.h>
-#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
@@ -41,7 +40,7 @@
#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
-#include "formats-util.h"
+#include "format-util.h"
#include "hashmap.h"
#include "hostname-util.h"
#include "log.h"
@@ -60,9 +59,6 @@
#include "user-util.h"
#include "util.h"
-/* Put this test here for a lack of better place */
-assert_cc(EAGAIN == EWOULDBLOCK);
-
int saved_argc = 0;
char **saved_argv = NULL;
static int saved_in_initrd = -1;
@@ -81,146 +77,6 @@ size_t page_size(void) {
return pgsz;
}
-static int do_execute(char **directories, usec_t timeout, char *argv[]) {
- _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
- _cleanup_set_free_free_ Set *seen = NULL;
- char **directory;
-
- /* We fork this all off from a child process so that we can
- * somewhat cleanly make use of SIGALRM to set a time limit */
-
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
-
- assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
- pids = hashmap_new(NULL);
- if (!pids)
- return log_oom();
-
- seen = set_new(&string_hash_ops);
- if (!seen)
- return log_oom();
-
- STRV_FOREACH(directory, directories) {
- _cleanup_closedir_ DIR *d;
- struct dirent *de;
-
- d = opendir(*directory);
- if (!d) {
- if (errno == ENOENT)
- continue;
-
- return log_error_errno(errno, "Failed to open directory %s: %m", *directory);
- }
-
- FOREACH_DIRENT(de, d, break) {
- _cleanup_free_ char *path = NULL;
- pid_t pid;
- int r;
-
- if (!dirent_is_file(de))
- continue;
-
- if (set_contains(seen, de->d_name)) {
- log_debug("%1$s/%2$s skipped (%2$s was already seen).", *directory, de->d_name);
- continue;
- }
-
- r = set_put_strdup(seen, de->d_name);
- if (r < 0)
- return log_oom();
-
- path = strjoin(*directory, "/", de->d_name, NULL);
- if (!path)
- return log_oom();
-
- if (null_or_empty_path(path)) {
- log_debug("%s is empty (a mask).", path);
- continue;
- }
-
- pid = fork();
- if (pid < 0) {
- log_error_errno(errno, "Failed to fork: %m");
- continue;
- } else if (pid == 0) {
- char *_argv[2];
-
- assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
- if (!argv) {
- _argv[0] = path;
- _argv[1] = NULL;
- argv = _argv;
- } else
- argv[0] = path;
-
- execv(path, argv);
- return log_error_errno(errno, "Failed to execute %s: %m", path);
- }
-
- log_debug("Spawned %s as " PID_FMT ".", path, pid);
-
- r = hashmap_put(pids, PID_TO_PTR(pid), path);
- if (r < 0)
- return log_oom();
- path = NULL;
- }
- }
-
- /* Abort execution of this process after the timout. We simply
- * rely on SIGALRM as default action terminating the process,
- * and turn on alarm(). */
-
- if (timeout != USEC_INFINITY)
- alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
-
- while (!hashmap_isempty(pids)) {
- _cleanup_free_ char *path = NULL;
- pid_t pid;
-
- pid = PTR_TO_PID(hashmap_first_key(pids));
- assert(pid > 0);
-
- path = hashmap_remove(pids, PID_TO_PTR(pid));
- assert(path);
-
- wait_for_terminate_and_warn(path, pid, true);
- }
-
- return 0;
-}
-
-void execute_directories(const char* const* directories, usec_t timeout, char *argv[]) {
- pid_t executor_pid;
- int r;
- char *name;
- char **dirs = (char**) directories;
-
- assert(!strv_isempty(dirs));
-
- name = basename(dirs[0]);
- assert(!isempty(name));
-
- /* Executes all binaries in the directories in parallel and waits
- * for them to finish. Optionally a timeout is applied. If a file
- * with the same name exists in more than one directory, the
- * earliest one wins. */
-
- executor_pid = fork();
- if (executor_pid < 0) {
- log_error_errno(errno, "Failed to fork: %m");
- return;
-
- } else if (executor_pid == 0) {
- r = do_execute(dirs, timeout, argv);
- _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
- }
-
- wait_for_terminate_and_warn(name, executor_pid, true);
-}
-
bool plymouth_running(void) {
return access("/run/plymouth/pid", F_OK) >= 0;
}
@@ -493,7 +349,7 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
u = nmemb;
while (l < u) {
idx = (l + u) / 2;
- p = (void *)(((const char *) base) + (idx * size));
+ p = (const char *) base + idx * size;
comparison = compar(key, p, arg);
if (comparison < 0)
u = idx;
@@ -508,28 +364,17 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
int on_ac_power(void) {
bool found_offline = false, found_online = false;
_cleanup_closedir_ DIR *d = NULL;
+ struct dirent *de;
d = opendir("/sys/class/power_supply");
if (!d)
return errno == ENOENT ? true : -errno;
- for (;;) {
- struct dirent *de;
+ FOREACH_DIRENT(de, d, return -errno) {
_cleanup_close_ int fd = -1, device = -1;
char contents[6];
ssize_t n;
- errno = 0;
- de = readdir(d);
- if (!de && errno > 0)
- return -errno;
-
- if (!de)
- break;
-
- if (hidden_or_backup_file(de->d_name))
- continue;
-
device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (device < 0) {
if (errno == ENOENT || errno == ENOTDIR)
diff --git a/src/basic/util.h b/src/basic/util.h
index bb2fc318ef..c7da6c39bf 100644
--- a/src/basic/util.h
+++ b/src/basic/util.h
@@ -41,7 +41,7 @@
#include <time.h>
#include <unistd.h>
-#include "formats-util.h"
+#include "format-util.h"
#include "macro.h"
#include "missing.h"
#include "time-util.h"
@@ -65,8 +65,6 @@ 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);
bool display_is_local(const char *display) _pure_;
diff --git a/src/basic/virt.c b/src/basic/virt.c
index 69b0f96183..ff4491d6d6 100644
--- a/src/basic/virt.c
+++ b/src/basic/virt.c
@@ -25,6 +25,7 @@
#include "alloc-util.h"
#include "dirent-util.h"
+#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "macro.h"
@@ -33,7 +34,6 @@
#include "string-table.h"
#include "string-util.h"
#include "virt.h"
-#include "env-util.h"
static int detect_vm_cpuid(void) {
@@ -48,7 +48,7 @@ static int detect_vm_cpuid(void) {
{ "KVMKVMKVM", VIRTUALIZATION_KVM },
/* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
{ "VMwareVMware", VIRTUALIZATION_VMWARE },
- /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
+ /* https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/reference/tlfs */
{ "Microsoft Hv", VIRTUALIZATION_MICROSOFT },
/* https://wiki.freebsd.org/bhyve */
{ "bhyve bhyve ", VIRTUALIZATION_BHYVE },
@@ -315,25 +315,31 @@ static int detect_vm_zvm(void) {
/* Returns a short identifier for the various VM implementations */
int detect_vm(void) {
static thread_local int cached_found = _VIRTUALIZATION_INVALID;
- int r;
+ int r, dmi;
if (cached_found >= 0)
return cached_found;
/* We have to use the correct order here:
- * Some virtualization technologies do use KVM hypervisor but are
- * expected to be detected as something else. So detect DMI first.
*
- * An example is Virtualbox since version 5.0, which uses KVM backend.
- * Detection via DMI works corretly, the CPU ID would find KVM
- * only. */
- r = detect_vm_dmi();
+ * -> First try to detect Oracle Virtualbox, even if it uses KVM.
+ * -> Second try to detect from cpuid, this will report KVM for
+ * whatever software is used even if info in dmi is overwritten.
+ * -> Third try to detect from dmi. */
+
+ dmi = detect_vm_dmi();
+ if (dmi == VIRTUALIZATION_ORACLE) {
+ r = dmi;
+ goto finish;
+ }
+
+ r = detect_vm_cpuid();
if (r < 0)
return r;
if (r != VIRTUALIZATION_NONE)
goto finish;
- r = detect_vm_cpuid();
+ r = dmi;
if (r < 0)
return r;
if (r != VIRTUALIZATION_NONE)
@@ -408,8 +414,7 @@ int detect_container(void) {
if (cached_found >= 0)
return cached_found;
- /* /proc/vz exists in container and outside of the container,
- * /proc/bc only outside of the container. */
+ /* /proc/vz exists in container and outside of the container, /proc/bc only outside of the container. */
if (access("/proc/vz", F_OK) >= 0 &&
access("/proc/bc", F_OK) < 0) {
r = VIRTUALIZATION_OPENVZ;
@@ -417,50 +422,58 @@ int detect_container(void) {
}
if (getpid() == 1) {
- /* If we are PID 1 we can just check our own
- * environment variable */
+ /* If we are PID 1 we can just check our own environment variable, and that's authoritative. */
e = getenv("container");
if (isempty(e)) {
r = VIRTUALIZATION_NONE;
goto finish;
}
- } else {
-
- /* Otherwise, PID 1 dropped this information into a
- * file in /run. This is better than accessing
- * /proc/1/environ, since we don't need CAP_SYS_PTRACE
- * for that. */
-
- r = read_one_line_file("/run/systemd/container", &m);
- if (r == -ENOENT) {
-
- /* Fallback for cases where PID 1 was not
- * systemd (for example, cases where
- * init=/bin/sh is used. */
-
- r = getenv_for_pid(1, "container", &m);
- if (r <= 0) {
-
- /* If that didn't work, give up,
- * assume no container manager.
- *
- * Note: This means we still cannot
- * detect containers if init=/bin/sh
- * is passed but privileges dropped,
- * as /proc/1/environ is only readable
- * with privileges. */
-
- r = VIRTUALIZATION_NONE;
- goto finish;
- }
- }
- if (r < 0)
- return r;
+ goto translate_name;
+ }
+
+ /* Otherwise, PID 1 might have dropped this information into a file in /run. This is better than accessing
+ * /proc/1/environ, since we don't need CAP_SYS_PTRACE for that. */
+ r = read_one_line_file("/run/systemd/container", &m);
+ if (r >= 0) {
+ e = m;
+ goto translate_name;
+ }
+ if (r != -ENOENT)
+ return log_debug_errno(r, "Failed to read /run/systemd/container: %m");
+
+ /* Fallback for cases where PID 1 was not systemd (for example, cases where init=/bin/sh is used. */
+ r = getenv_for_pid(1, "container", &m);
+ if (r > 0) {
e = m;
+ goto translate_name;
}
+ if (r < 0) /* This only works if we have CAP_SYS_PTRACE, hence let's better ignore failures here */
+ log_debug_errno(r, "Failed to read $container of PID 1, ignoring: %m");
+
+ /* Interestingly /proc/1/sched actually shows the host's PID for what we see as PID 1. Hence, if the PID shown
+ * there is not 1, we know we are in a PID namespace. and hence a container. */
+ r = read_one_line_file("/proc/1/sched", &m);
+ if (r >= 0) {
+ const char *t;
+
+ t = strrchr(m, '(');
+ if (!t)
+ return -EIO;
+
+ if (!startswith(t, "(1,")) {
+ r = VIRTUALIZATION_CONTAINER_OTHER;
+ goto finish;
+ }
+ } else if (r != -ENOENT)
+ return r;
+
+ /* If that didn't work, give up, assume no container manager. */
+ r = VIRTUALIZATION_NONE;
+ goto finish;
+translate_name:
for (j = 0; j < ELEMENTSOF(value_table); j++)
if (streq(e, value_table[j].value)) {
r = value_table[j].id;
@@ -470,7 +483,7 @@ int detect_container(void) {
r = VIRTUALIZATION_CONTAINER_OTHER;
finish:
- log_debug("Found container virtualization %s", virtualization_to_string(r));
+ log_debug("Found container virtualization %s.", virtualization_to_string(r));
cached_found = r;
return r;
}
@@ -496,7 +509,7 @@ static int userns_has_mapping(const char *name) {
f = fopen(name, "re");
if (!f) {
log_debug_errno(errno, "Failed to open %s: %m", name);
- return errno == -ENOENT ? false : -errno;
+ return errno == ENOENT ? false : -errno;
}
n = getline(&buf, &n_allocated, f);