diff options
Diffstat (limited to 'src')
62 files changed, 1330 insertions, 613 deletions
diff --git a/src/basic/calendarspec.c b/src/basic/calendarspec.c index e4cfab364e..fda293fcb9 100644 --- a/src/basic/calendarspec.c +++ b/src/basic/calendarspec.c @@ -302,6 +302,17 @@ int calendar_spec_to_string(const CalendarSpec *c, char **p) { if (c->utc) fputs(" UTC", f); + else if (IN_SET(c->dst, 0, 1)) { + + /* If daylight saving is explicitly on or off, let's show the used timezone. */ + + tzset(); + + if (!isempty(tzname[c->dst])) { + fputc(' ', f); + fputs(tzname[c->dst], f); + } + } r = fflush_and_check(f); if (r < 0) { @@ -747,9 +758,9 @@ fail: } int calendar_spec_from_string(const char *p, CalendarSpec **spec) { + const char *utc; CalendarSpec *c; int r; - const char *utc; assert(p); assert(spec); @@ -760,11 +771,39 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) { c = new0(CalendarSpec, 1); if (!c) return -ENOMEM; + c->dst = -1; utc = endswith_no_case(p, " UTC"); if (utc) { c->utc = true; p = strndupa(p, utc - p); + } else { + const char *e = NULL; + int j; + + tzset(); + + /* Check if the local timezone was specified? */ + for (j = 0; j <= 1; j++) { + if (isempty(tzname[j])) + continue; + + e = endswith_no_case(p, tzname[j]); + if(!e) + continue; + if (e == p) + continue; + if (e[-1] != ' ') + continue; + + break; + } + + /* Found one of the two timezones specified? */ + if (IN_SET(j, 0, 1)) { + p = strndupa(p, e - p - 1); + c->dst = j; + } } if (strcaseeq(p, "minutely")) { @@ -1017,7 +1056,7 @@ static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) { for (;;) { /* Normalize the current date */ (void) mktime_or_timegm(&c, spec->utc); - c.tm_isdst = -1; + c.tm_isdst = spec->dst; c.tm_year += 1900; r = find_matching_component(spec->year, &c.tm_year); diff --git a/src/basic/calendarspec.h b/src/basic/calendarspec.h index f6472c1244..c6087228fd 100644 --- a/src/basic/calendarspec.h +++ b/src/basic/calendarspec.h @@ -37,6 +37,7 @@ typedef struct CalendarComponent { typedef struct CalendarSpec { int weekdays_bits; bool utc; + int dst; CalendarComponent *year; CalendarComponent *month; diff --git a/src/basic/fileio.c b/src/basic/fileio.c index f183de4999..d642f3daea 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -47,6 +47,8 @@ #include "umask-util.h" #include "utf8.h" +#define READ_FULL_BYTES_MAX (4U*1024U*1024U) + int write_string_stream(FILE *f, const char *line, bool enforce_newline) { assert(f); @@ -230,7 +232,7 @@ int read_full_stream(FILE *f, char **contents, size_t *size) { if (S_ISREG(st.st_mode)) { /* Safety check */ - if (st.st_size > 4*1024*1024) + if (st.st_size > READ_FULL_BYTES_MAX) return -E2BIG; /* Start with the right file size, but be prepared for @@ -245,26 +247,31 @@ int read_full_stream(FILE *f, char **contents, size_t *size) { char *t; size_t k; - t = realloc(buf, n+1); + t = realloc(buf, n + 1); if (!t) return -ENOMEM; buf = t; k = fread(buf + l, 1, n - l, f); + if (k > 0) + l += k; - if (k <= 0) { - if (ferror(f)) - return -errno; + if (ferror(f)) + return -errno; + if (feof(f)) break; - } - l += k; - n *= 2; + /* We aren't expecting fread() to return a short read outside + * of (error && eof), assert buffer is full and enlarge buffer. + */ + assert(l == n); /* Safety check */ - if (n > 4*1024*1024) + if (n >= READ_FULL_BYTES_MAX) return -E2BIG; + + n = MIN(n * 2, READ_FULL_BYTES_MAX); } buf[l] = 0; @@ -1161,8 +1168,8 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) { char *t, *x; uint64_t u; unsigned i; + int r; - assert(p); assert(ret); /* Turns this: @@ -1171,6 +1178,12 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) { * /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0 */ + if (!p) { + r = tmp_dir(&p); + if (r < 0) + return r; + } + if (!extra) extra = ""; @@ -1257,10 +1270,13 @@ int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space) int open_tmpfile_unlinkable(const char *directory, int flags) { char *p; - int fd; + int fd, r; - if (!directory) - directory = "/tmp"; + if (!directory) { + r = tmp_dir(&directory); + if (r < 0) + return r; + } /* Returns an unlinked temporary file that cannot be linked into the file system anymore */ diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index f0c6f3265e..ce87257bc1 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -496,34 +496,94 @@ int get_files_in_directory(const char *path, char ***list) { return n; } -int var_tmp(char **ret) { - const char *tmp_dir = NULL; - const char *env_tmp_dir = NULL; - char *c = NULL; - int r; +static int getenv_tmp_dir(const char **ret_path) { + const char *n; + int r, ret = 0; - assert(ret); + assert(ret_path); - env_tmp_dir = getenv("TMPDIR"); - if (env_tmp_dir != NULL) { - r = is_dir(env_tmp_dir, true); - if (r < 0 && r != -ENOENT) - return r; - if (r > 0) - tmp_dir = env_tmp_dir; + /* We use the same order of environment variables python uses in tempfile.gettempdir(): + * https://docs.python.org/3/library/tempfile.html#tempfile.gettempdir */ + FOREACH_STRING(n, "TMPDIR", "TEMP", "TMP") { + const char *e; + + e = secure_getenv(n); + if (!e) + continue; + if (!path_is_absolute(e)) { + r = -ENOTDIR; + goto next; + } + if (!path_is_safe(e)) { + r = -EPERM; + goto next; + } + + r = is_dir(e, true); + if (r < 0) + goto next; + if (r == 0) { + r = -ENOTDIR; + goto next; + } + + *ret_path = e; + return 1; + + next: + /* Remember first error, to make this more debuggable */ + if (ret >= 0) + ret = r; } - if (!tmp_dir) - tmp_dir = "/var/tmp"; + if (ret < 0) + return ret; - c = strdup(tmp_dir); - if (!c) - return -ENOMEM; - *ret = c; + *ret_path = NULL; + return ret; +} +static int tmp_dir_internal(const char *def, const char **ret) { + const char *e; + int r, k; + + assert(def); + assert(ret); + + r = getenv_tmp_dir(&e); + if (r > 0) { + *ret = e; + return 0; + } + + k = is_dir(def, true); + if (k == 0) + k = -ENOTDIR; + if (k < 0) + return r < 0 ? r : k; + + *ret = def; return 0; } +int var_tmp_dir(const char **ret) { + + /* Returns the location for "larger" temporary files, that is backed by physical storage if available, and thus + * even might survive a boot: /var/tmp. If $TMPDIR (or related environment variables) are set, its value is + * returned preferably however. Note that both this function and tmp_dir() below are affected by $TMPDIR, + * making it a variable that overrides all temporary file storage locations. */ + + return tmp_dir_internal("/var/tmp", ret); +} + +int tmp_dir(const char **ret) { + + /* Similar to var_tmp_dir() above, but returns the location for "smaller" temporary files, which is usually + * backed by an in-memory file system: /tmp. */ + + return tmp_dir_internal("/tmp", ret); +} + int inotify_add_watch_fd(int fd, int what, uint32_t mask) { char path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; int r; diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index 075e5942b1..2c3b9a1c74 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -61,7 +61,8 @@ int mkfifo_atomic(const char *path, mode_t mode); int get_files_in_directory(const char *path, char ***list); -int var_tmp(char **ret); +int tmp_dir(const char **ret); +int var_tmp_dir(const char **ret); #define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1) diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c index 503a895731..c98815b9bc 100644 --- a/src/basic/parse-util.c +++ b/src/basic/parse-util.c @@ -29,6 +29,7 @@ #include "extract-word.h" #include "macro.h" #include "parse-util.h" +#include "process-util.h" #include "string-util.h" int parse_boolean(const char *v) { @@ -533,7 +534,7 @@ int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) { return 0; } -int parse_percent(const char *p) { +int parse_percent_unbounded(const char *p) { const char *pc, *n; unsigned v; int r; @@ -546,8 +547,30 @@ int parse_percent(const char *p) { r = safe_atou(n, &v); if (r < 0) return r; + + return (int) v; +} + +int parse_percent(const char *p) { + int v; + + v = parse_percent_unbounded(p); if (v > 100) return -ERANGE; - return (int) v; + return v; +} + +int parse_nice(const char *p, int *ret) { + int n, r; + + r = safe_atoi(p, &n); + if (r < 0) + return r; + + if (!nice_is_valid(n)) + return -ERANGE; + + *ret = n; + return 0; } diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h index 73441bb6fd..461e1cd4d8 100644 --- a/src/basic/parse-util.h +++ b/src/basic/parse-util.h @@ -106,4 +106,7 @@ int safe_atod(const char *s, double *ret_d); int parse_fractional_part_u(const char **s, size_t digits, unsigned *res); +int parse_percent_unbounded(const char *p); int parse_percent(const char *p); + +int parse_nice(const char *p, int *ret); diff --git a/src/basic/process-util.h b/src/basic/process-util.h index 9f75088796..2568e3834f 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -26,6 +26,7 @@ #include <stdio.h> #include <string.h> #include <sys/types.h> +#include <sys/resource.h> #include "formats-util.h" #include "macro.h" @@ -103,3 +104,7 @@ int sched_policy_from_string(const char *s); void valgrind_summary_hack(void); int pid_compare_func(const void *a, const void *b); + +static inline bool nice_is_valid(int n) { + return n >= PRIO_MIN && n < PRIO_MAX; +} diff --git a/src/basic/set.h b/src/basic/set.h index 12f64a8c57..a5f8beb0c4 100644 --- a/src/basic/set.h +++ b/src/basic/set.h @@ -23,8 +23,8 @@ #include "hashmap.h" #include "macro.h" -Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS) +Set *internal_set_new(const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define set_new(ops) internal_set_new(ops HASHMAP_DEBUG_SRC_ARGS) static inline Set *set_free(Set *s) { internal_hashmap_free(HASHMAP_BASE(s)); @@ -42,8 +42,8 @@ static inline Set *set_copy(Set *s) { return (Set*) internal_hashmap_copy(HASHMAP_BASE(s)); } -int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); -#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) +int internal_set_ensure_allocated(Set **s, const struct hash_ops *hash_ops HASHMAP_DEBUG_PARAMS); +#define set_ensure_allocated(h, ops) internal_set_ensure_allocated(h, ops HASHMAP_DEBUG_SRC_ARGS) int set_put(Set *s, const void *key); /* no set_update */ diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index df56d85317..f0a46c48cf 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -785,7 +785,7 @@ bool tty_is_vc_resolve(const char *tty) { } const char *default_term_for_tty(const char *tty) { - return tty && tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt220"; + return tty && tty_is_vc_resolve(tty) ? "linux" : "vt220"; } int fd_columns(int fd) { diff --git a/src/basic/time-util.c b/src/basic/time-util.c index 24e681bf85..0ef1f6393e 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -254,32 +254,95 @@ struct timeval *timeval_store(struct timeval *tv, usec_t u) { return tv; } -static char *format_timestamp_internal(char *buf, size_t l, usec_t t, - bool utc, bool us) { +static char *format_timestamp_internal( + char *buf, + size_t l, + usec_t t, + bool utc, + bool us) { + + /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that our + * generated timestamps may be parsed with parse_timestamp(), and always read the same. */ + static const char * const weekdays[] = { + [0] = "Sun", + [1] = "Mon", + [2] = "Tue", + [3] = "Wed", + [4] = "Thu", + [5] = "Fri", + [6] = "Sat", + }; + struct tm tm; time_t sec; - int k; + size_t n; assert(buf); - assert(l > 0); + if (l < + 3 + /* week day */ + 1 + 10 + /* space and date */ + 1 + 8 + /* space and time */ + (us ? 1 + 6 : 0) + /* "." and microsecond part */ + 1 + 1 + /* space and shortest possible zone */ + 1) + return NULL; /* Not enough space even for the shortest form. */ if (t <= 0 || t == USEC_INFINITY) + return NULL; /* Timestamp is unset */ + + sec = (time_t) (t / USEC_PER_SEC); /* Round down */ + if ((usec_t) sec != (t / USEC_PER_SEC)) + return NULL; /* overflow? */ + + if (!localtime_or_gmtime_r(&sec, &tm, utc)) return NULL; - sec = (time_t) (t / USEC_PER_SEC); - localtime_or_gmtime_r(&sec, &tm, utc); + /* Start with the week day */ + assert((size_t) tm.tm_wday < ELEMENTSOF(weekdays)); + memcpy(buf, weekdays[tm.tm_wday], 4); - if (us) - k = strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm); - else - k = strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", &tm); + /* Add the main components */ + if (strftime(buf + 3, l - 3, " %Y-%m-%d %H:%M:%S", &tm) <= 0) + return NULL; /* Doesn't fit */ - if (k <= 0) - return NULL; + /* Append the microseconds part, if that's requested */ if (us) { - snprintf(buf + strlen(buf), l - strlen(buf), ".%06llu", (unsigned long long) (t % USEC_PER_SEC)); - if (strftime(buf + strlen(buf), l - strlen(buf), " %Z", &tm) <= 0) - return NULL; + n = strlen(buf); + if (n + 8 > l) + return NULL; /* Microseconds part doesn't fit. */ + + sprintf(buf + n, ".%06llu", (unsigned long long) (t % USEC_PER_SEC)); + } + + /* Append the timezone */ + n = strlen(buf); + if (utc) { + /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r() normally uses the + * obsolete "GMT" instead. */ + if (n + 5 > l) + return NULL; /* "UTC" doesn't fit. */ + + strcpy(buf + n, " UTC"); + + } else if (!isempty(tm.tm_zone)) { + size_t tn; + + /* An explicit timezone is specified, let's use it, if it fits */ + tn = strlen(tm.tm_zone); + if (n + 1 + tn + 1 > l) { + /* The full time zone does not fit in. Yuck. */ + + if (n + 1 + _POSIX_TZNAME_MAX + 1 > l) + return NULL; /* Not even enough space for the POSIX minimum (of 6)? In that case, complain that it doesn't fit */ + + /* So the time zone doesn't fit in fully, but the caller passed enough space for the POSIX + * minimum time zone length. In this case suppress the timezone entirely, in order not to dump + * an overly long, hard to read string on the user. This should be safe, because the user will + * assume the local timezone anyway if none is shown. And so does parse_timestamp(). */ + } else { + buf[n++] = ' '; + strcpy(buf + n, tm.tm_zone); + } } return buf; @@ -539,12 +602,11 @@ int parse_timestamp(const char *t, usec_t *usec) { { "Sat", 6 }, }; - const char *k; - const char *utc; + const char *k, *utc, *tzn = NULL; struct tm tm, copy; time_t x; usec_t x_usec, plus = 0, minus = 0, ret; - int r, weekday = -1; + int r, weekday = -1, dst = -1; unsigned i; /* @@ -609,15 +671,55 @@ int parse_timestamp(const char *t, usec_t *usec) { goto finish; } + /* See if the timestamp is suffixed with UTC */ utc = endswith_no_case(t, " UTC"); if (utc) t = strndupa(t, utc - t); + else { + const char *e = NULL; + int j; + + tzset(); + + /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only + * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because + * there are no nice APIs available to cover this. By accepting the local time zone strings, we make + * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't + * support arbitrary timezone specifications. */ + + for (j = 0; j <= 1; j++) { + + if (isempty(tzname[j])) + continue; + + e = endswith_no_case(t, tzname[j]); + if (!e) + continue; + if (e == t) + continue; + if (e[-1] != ' ') + continue; + + break; + } - x = ret / USEC_PER_SEC; + if (IN_SET(j, 0, 1)) { + /* Found one of the two timezones specified. */ + t = strndupa(t, e - t - 1); + dst = j; + tzn = tzname[j]; + } + } + + x = (time_t) (ret / USEC_PER_SEC); x_usec = 0; - assert_se(localtime_or_gmtime_r(&x, &tm, utc)); - tm.tm_isdst = -1; + if (!localtime_or_gmtime_r(&x, &tm, utc)) + return -EINVAL; + + tm.tm_isdst = dst; + if (tzn) + tm.tm_zone = tzn; if (streq(t, "today")) { tm.tm_sec = tm.tm_min = tm.tm_hour = 0; @@ -634,7 +736,6 @@ int parse_timestamp(const char *t, usec_t *usec) { goto from_tm; } - for (i = 0; i < ELEMENTSOF(day_nr); i++) { size_t skip; @@ -727,7 +828,6 @@ parse_usec: return -EINVAL; x_usec = add; - } from_tm: diff --git a/src/basic/time-util.h b/src/basic/time-util.h index 1b058f0e49..99be5ce6ee 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -68,7 +68,9 @@ typedef struct triple_timestamp { #define USEC_PER_YEAR ((usec_t) (31557600ULL*USEC_PER_SEC)) #define NSEC_PER_YEAR ((nsec_t) (31557600ULL*NSEC_PER_SEC)) -#define FORMAT_TIMESTAMP_MAX ((4*4+1)+11+9+4+1) /* weekdays can be unicode */ +/* We assume a maximum timezone length of 6. TZNAME_MAX is not defined on Linux, but glibc internally initializes this + * to 6. Let's rely on that. */ +#define FORMAT_TIMESTAMP_MAX (3+1+10+1+8+1+6+1+6+1) #define FORMAT_TIMESTAMP_WIDTH 28 /* when outputting, assume this width */ #define FORMAT_TIMESTAMP_RELATIVE_MAX 256 #define FORMAT_TIMESPAN_MAX 64 diff --git a/src/core/automount.c b/src/core/automount.c index 4e9891569c..00295cf769 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -301,7 +301,7 @@ static void automount_dump(Unit *u, FILE *f, const char *prefix) { static void automount_enter_dead(Automount *a, AutomountResult f) { assert(a); - if (f != AUTOMOUNT_SUCCESS) + if (a->result == AUTOMOUNT_SUCCESS) a->result = f; automount_set_state(a, a->result != AUTOMOUNT_SUCCESS ? AUTOMOUNT_FAILED : AUTOMOUNT_DEAD); @@ -1105,6 +1105,9 @@ const UnitVTable automount_vtable = { .reset_failed = automount_reset_failed, .bus_vtable = bus_automount_vtable, + .bus_set_property = bus_automount_set_property, + + .can_transient = true, .shutdown = automount_shutdown, .supported = automount_supported, diff --git a/src/core/busname.c b/src/core/busname.c index 730be2ee14..7952cd31aa 100644 --- a/src/core/busname.c +++ b/src/core/busname.c @@ -442,7 +442,7 @@ fail: static void busname_enter_dead(BusName *n, BusNameResult f) { assert(n); - if (f != BUSNAME_SUCCESS) + if (n->result == BUSNAME_SUCCESS) n->result = f; busname_set_state(n, n->result != BUSNAME_SUCCESS ? BUSNAME_FAILED : BUSNAME_DEAD); @@ -454,7 +454,7 @@ static void busname_enter_signal(BusName *n, BusNameState state, BusNameResult f assert(n); - if (f != BUSNAME_SUCCESS) + if (n->result == BUSNAME_SUCCESS) n->result = f; kill_context_init(&kill_context); @@ -882,7 +882,7 @@ static void busname_sigchld_event(Unit *u, pid_t pid, int code, int status) { log_unit_full(u, f == BUSNAME_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0, "Control process exited, code=%s status=%i", sigchld_code_to_string(code), status); - if (f != BUSNAME_SUCCESS) + if (n->result == BUSNAME_SUCCESS) n->result = f; switch (n->state) { diff --git a/src/core/dbus-automount.c b/src/core/dbus-automount.c index b2806ad86f..26212b3a95 100644 --- a/src/core/dbus-automount.c +++ b/src/core/dbus-automount.c @@ -32,3 +32,57 @@ const sd_bus_vtable bus_automount_vtable[] = { SD_BUS_PROPERTY("TimeoutIdleUSec", "t", bus_property_get_usec, offsetof(Automount, timeout_idle_usec), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_VTABLE_END }; + +static int bus_automount_set_transient_property( + Automount *a, + const char *name, + sd_bus_message *message, + UnitSetPropertiesMode mode, + sd_bus_error *error) { + + int r; + + assert(a); + assert(name); + assert(message); + + if (streq(name, "TimeoutIdleUSec")) { + usec_t timeout_idle_usec; + r = sd_bus_message_read(message, "t", &timeout_idle_usec); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + char time[FORMAT_TIMESPAN_MAX]; + + a->timeout_idle_usec = timeout_idle_usec; + unit_write_drop_in_format(UNIT(a), mode, name, "[Automount]\nTimeoutIdleSec=%s\n", + format_timespan(time, sizeof(time), timeout_idle_usec, USEC_PER_MSEC)); + } + } else + return 0; + + return 1; +} + +int bus_automount_set_property( + Unit *u, + const char *name, + sd_bus_message *message, + UnitSetPropertiesMode mode, + sd_bus_error *error) { + + Automount *a = AUTOMOUNT(u); + int r = 0; + + assert(a); + assert(name); + assert(message); + + if (u->transient && u->load_state == UNIT_STUB) + /* This is a transient unit, let's load a little more */ + + r = bus_automount_set_transient_property(a, name, message, mode, error); + + return r; +} diff --git a/src/core/dbus-automount.h b/src/core/dbus-automount.h index 7b51eb973a..f41adda2a6 100644 --- a/src/core/dbus-automount.h +++ b/src/core/dbus-automount.h @@ -21,3 +21,5 @@ extern const sd_bus_vtable bus_automount_vtable[]; + +int bus_automount_set_property(Unit *u, const char *name, sd_bus_message *message, UnitSetPropertiesMode mode, sd_bus_error *error); diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 4b3bbfbc7d..e35d3ccd2e 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -936,7 +936,7 @@ int bus_exec_context_set_transient_property( if (r < 0) return r; - if (n < PRIO_MIN || n >= PRIO_MAX) + if (!nice_is_valid(n)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Nice value out of range"); if (mode != UNIT_CHECK) { diff --git a/src/core/dbus-mount.c b/src/core/dbus-mount.c index 935db7c48b..b4bbee0648 100644 --- a/src/core/dbus-mount.c +++ b/src/core/dbus-mount.c @@ -157,6 +157,9 @@ static int bus_mount_set_transient_property( if (!p) return -ENOMEM; + unit_write_drop_in_format(UNIT(m), mode, name, "[Mount]\n%s=%s\n", + name, new_property); + free(*property); *property = p; } diff --git a/src/core/execute.c b/src/core/execute.c index cec3b3cf40..6019df7ea6 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -220,12 +220,36 @@ static void exec_context_tty_reset(const ExecContext *context, const ExecParamet (void) vt_disallocate(path); } +static bool is_terminal_input(ExecInput i) { + return IN_SET(i, + EXEC_INPUT_TTY, + EXEC_INPUT_TTY_FORCE, + EXEC_INPUT_TTY_FAIL); +} + static bool is_terminal_output(ExecOutput o) { - return - o == EXEC_OUTPUT_TTY || - o == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || - o == EXEC_OUTPUT_KMSG_AND_CONSOLE || - o == EXEC_OUTPUT_JOURNAL_AND_CONSOLE; + return IN_SET(o, + EXEC_OUTPUT_TTY, + EXEC_OUTPUT_SYSLOG_AND_CONSOLE, + EXEC_OUTPUT_KMSG_AND_CONSOLE, + EXEC_OUTPUT_JOURNAL_AND_CONSOLE); +} + +static bool exec_context_needs_term(const ExecContext *c) { + assert(c); + + /* Return true if the execution context suggests we should set $TERM to something useful. */ + + if (is_terminal_input(c->std_input)) + return true; + + if (is_terminal_output(c->std_output)) + return true; + + if (is_terminal_output(c->std_error)) + return true; + + return !!c->tty_path; } static int open_null_as(int flags, int nfd) { @@ -364,13 +388,6 @@ static int open_terminal_as(const char *path, mode_t mode, int nfd) { return r; } -static bool is_terminal_input(ExecInput i) { - return - i == EXEC_INPUT_TTY || - i == EXEC_INPUT_TTY_FORCE || - i == EXEC_INPUT_TTY_FAIL; -} - static int fixup_input(ExecInput std_input, int socket_fd, bool apply_tty_stdin) { if (is_terminal_input(std_input) && !apply_tty_stdin) @@ -411,7 +428,7 @@ static int setup_input( return STDIN_FILENO; } - i = fixup_input(context->std_input, socket_fd, params->apply_tty_stdin); + i = fixup_input(context->std_input, socket_fd, params->flags & EXEC_APPLY_TTY_STDIN); switch (i) { @@ -486,7 +503,7 @@ static int setup_output( return STDERR_FILENO; } - i = fixup_input(context->std_input, socket_fd, params->apply_tty_stdin); + i = fixup_input(context->std_input, socket_fd, params->flags & EXEC_APPLY_TTY_STDIN); o = fixup_output(context->std_output, socket_fd); if (fileno == STDERR_FILENO) { @@ -1409,7 +1426,7 @@ static int build_environment( our_env[n_env++] = x; } - if (p->watchdog_usec > 0) { + if ((p->flags & EXEC_SET_WATCHDOG) && p->watchdog_usec > 0) { if (asprintf(&x, "WATCHDOG_PID="PID_FMT, getpid()) < 0) return -ENOMEM; our_env[n_env++] = x; @@ -1445,12 +1462,21 @@ static int build_environment( our_env[n_env++] = x; } - if (is_terminal_input(c->std_input) || - c->std_output == EXEC_OUTPUT_TTY || - c->std_error == EXEC_OUTPUT_TTY || - c->tty_path) { + if (exec_context_needs_term(c)) { + const char *tty_path, *term = NULL; + + tty_path = exec_context_tty_path(c); - x = strdup(default_term_for_tty(exec_context_tty_path(c))); + /* If we are forked off PID 1 and we are supposed to operate on /dev/console, then let's try to inherit + * the $TERM set for PID 1. This is useful for containers so that the $TERM the container manager + * passes to PID 1 ends up all the way in the console login shown. */ + + if (path_equal(tty_path, "/dev/console") && getppid() == 1) + term = getenv("TERM"); + if (!term) + term = default_term_for_tty(tty_path); + + x = strappend("TERM=", term); if (!x) return -ENOMEM; our_env[n_env++] = x; @@ -1803,7 +1829,7 @@ static int exec_child( exec_context_tty_reset(context, params); - if (params->confirm_spawn) { + if (params->flags & EXEC_CONFIRM_SPAWN) { char response; r = ask_for_confirmation(&response, argv); @@ -1852,6 +1878,17 @@ static int exec_child( *exit_status = EXIT_USER; return r; } + + /* Don't set $HOME or $SHELL if they are are not particularly enlightening anyway. */ + if (isempty(home) || path_equal(home, "/")) + home = NULL; + + if (isempty(shell) || PATH_IN_SET(shell, + "/bin/nologin", + "/sbin/nologin", + "/usr/bin/nologin", + "/usr/sbin/nologin")) + shell = NULL; } if (context->group) { @@ -2057,7 +2094,7 @@ static int exec_child( umask(context->umask); - if (params->apply_permissions && !command->privileged) { + if ((params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged) { r = enforce_groups(context, username, gid); if (r < 0) { *exit_status = EXIT_GROUP; @@ -2127,7 +2164,7 @@ static int exec_child( } r = setup_namespace( - params->apply_chroot ? context->root_directory : NULL, + (params->flags & EXEC_APPLY_CHROOT) ? context->root_directory : NULL, context->read_write_paths, context->read_only_paths, context->inaccessible_paths, @@ -2158,7 +2195,7 @@ static int exec_child( else wd = "/"; - if (params->apply_chroot) { + if (params->flags & EXEC_APPLY_CHROOT) { if (!needs_mount_namespace && context->root_directory) if (chroot(context->root_directory) < 0) { *exit_status = EXIT_CHROOT; @@ -2182,7 +2219,12 @@ static int exec_child( } #ifdef HAVE_SELINUX - if (params->apply_permissions && mac_selinux_use() && params->selinux_context_net && socket_fd >= 0 && !command->privileged) { + if ((params->flags & EXEC_APPLY_PERMISSIONS) && + mac_selinux_use() && + params->selinux_context_net && + socket_fd >= 0 && + !command->privileged) { + r = mac_selinux_get_child_mls_label(socket_fd, command->path, context->selinux_context, &mac_selinux_context_net); if (r < 0) { *exit_status = EXIT_SELINUX_CONTEXT; @@ -2191,7 +2233,7 @@ static int exec_child( } #endif - if (params->apply_permissions && context->private_users) { + if ((params->flags & EXEC_APPLY_PERMISSIONS) && context->private_users) { r = setup_private_users(uid, gid); if (r < 0) { *exit_status = EXIT_USER; @@ -2215,7 +2257,7 @@ static int exec_child( return r; } - if (params->apply_permissions && !command->privileged) { + if ((params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged) { bool use_address_families = context->address_families_whitelist || !set_isempty(context->address_families); @@ -3091,12 +3133,12 @@ void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix) { "%sPID: "PID_FMT"\n", prefix, s->pid); - if (s->start_timestamp.realtime > 0) + if (dual_timestamp_is_set(&s->start_timestamp)) fprintf(f, "%sStart Timestamp: %s\n", prefix, format_timestamp(buf, sizeof(buf), s->start_timestamp.realtime)); - if (s->exit_timestamp.realtime > 0) + if (dual_timestamp_is_set(&s->exit_timestamp)) fprintf(f, "%sExit Timestamp: %s\n" "%sExit Code: %s\n" diff --git a/src/core/execute.h b/src/core/execute.h index 5fac3e85e8..106154f81a 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -209,6 +209,19 @@ struct ExecContext { bool no_new_privileges_set:1; }; +typedef enum ExecFlags { + EXEC_CONFIRM_SPAWN = 1U << 0, + EXEC_APPLY_PERMISSIONS = 1U << 1, + EXEC_APPLY_CHROOT = 1U << 2, + EXEC_APPLY_TTY_STDIN = 1U << 3, + + /* The following are not used by execute.c, but by consumers internally */ + EXEC_PASS_FDS = 1U << 4, + EXEC_IS_CONTROL = 1U << 5, + EXEC_SETENV_RESULT = 1U << 6, + EXEC_SET_WATCHDOG = 1U << 7, +} ExecFlags; + struct ExecParameters { char **argv; char **environment; @@ -217,11 +230,7 @@ struct ExecParameters { char **fd_names; unsigned n_fds; - bool apply_permissions:1; - bool apply_chroot:1; - bool apply_tty_stdin:1; - - bool confirm_spawn:1; + ExecFlags flags; bool selinux_context_net:1; bool cgroup_delegate:1; diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index e8cb3a4249..420f368689 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -491,16 +491,17 @@ int config_parse_socket_bind(const char *unit, return 0; } -int config_parse_exec_nice(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_exec_nice( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { ExecContext *c = data; int priority, r; @@ -510,14 +511,13 @@ int config_parse_exec_nice(const char *unit, assert(rvalue); assert(data); - r = safe_atoi(rvalue, &priority); + r = parse_nice(rvalue, &priority); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse nice priority, ignoring: %s", rvalue); - return 0; - } + if (r == -ERANGE) + log_syntax(unit, LOG_ERR, filename, line, r, "Nice priority out of range, ignoring: %s", rvalue); + else + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse nice priority, ignoring: %s", rvalue); - if (priority < PRIO_MIN || priority >= PRIO_MAX) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Nice priority out of range, ignoring: %s", rvalue); return 0; } @@ -2903,7 +2903,7 @@ int config_parse_cpu_quota( return 0; } - r = parse_percent(rvalue); + r = parse_percent_unbounded(rvalue); if (r <= 0) { log_syntax(unit, LOG_ERR, filename, line, r, "CPU quota '%s' invalid. Ignoring.", rvalue); return 0; diff --git a/src/core/main.c b/src/core/main.c index c46d886653..02324d325e 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1318,7 +1318,7 @@ static int fixup_environment(void) { return r; if (r == 0) { - term = strdup(default_term_for_tty("/dev/console") + 5); + term = strdup(default_term_for_tty("/dev/console")); if (!term) return -ENOMEM; } @@ -1614,6 +1614,7 @@ int main(int argc, char *argv[]) { retval = version(); goto finish; } else if (arg_action == ACTION_DUMP_CONFIGURATION_ITEMS) { + pager_open(arg_no_pager, false); unit_dump_config_items(stdout); retval = EXIT_SUCCESS; goto finish; diff --git a/src/core/manager.c b/src/core/manager.c index e41b65da50..c20e185d78 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -553,7 +553,6 @@ static int manager_default_environment(Manager *m) { return 0; } - int manager_new(UnitFileScope scope, bool test_run, Manager **_m) { Manager *m; int r; diff --git a/src/core/mount.c b/src/core/mount.c index db5cafcb11..f3ccf6d48a 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -484,6 +484,7 @@ static int mount_add_default_dependencies(Mount *m) { static int mount_verify(Mount *m) { _cleanup_free_ char *e = NULL; + MountParameters *p; int r; assert(m); @@ -508,7 +509,8 @@ static int mount_verify(Mount *m) { return -EINVAL; } - if (UNIT(m)->fragment_path && !m->parameters_fragment.what) { + p = get_mount_parameters_fragment(m); + if (p && !p->what) { log_unit_error(UNIT(m), "What= setting is missing. Refusing."); return -EBADMSG; } @@ -699,12 +701,10 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { pid_t pid; int r; ExecParameters exec_params = { - .apply_permissions = true, - .apply_chroot = true, - .apply_tty_stdin = true, - .stdin_fd = -1, - .stdout_fd = -1, - .stderr_fd = -1, + .flags = EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN, + .stdin_fd = -1, + .stdout_fd = -1, + .stderr_fd = -1, }; assert(m); @@ -730,7 +730,7 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { return r; exec_params.environment = UNIT(m)->manager->environment; - exec_params.confirm_spawn = UNIT(m)->manager->confirm_spawn; + exec_params.flags |= UNIT(m)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0; exec_params.cgroup_supported = UNIT(m)->manager->cgroup_supported; exec_params.cgroup_path = UNIT(m)->cgroup_path; exec_params.cgroup_delegate = m->cgroup_context.delegate; @@ -759,7 +759,7 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { static void mount_enter_dead(Mount *m, MountResult f) { assert(m); - if (f != MOUNT_SUCCESS) + if (m->result == MOUNT_SUCCESS) m->result = f; mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD); @@ -775,7 +775,7 @@ static void mount_enter_dead(Mount *m, MountResult f) { static void mount_enter_mounted(Mount *m, MountResult f) { assert(m); - if (f != MOUNT_SUCCESS) + if (m->result == MOUNT_SUCCESS) m->result = f; mount_set_state(m, MOUNT_MOUNTED); @@ -786,7 +786,7 @@ static void mount_enter_signal(Mount *m, MountState state, MountResult f) { assert(m); - if (f != MOUNT_SUCCESS) + if (m->result == MOUNT_SUCCESS) m->result = f; r = unit_kill_context( @@ -862,11 +862,6 @@ fail: mount_enter_mounted(m, MOUNT_FAILURE_RESOURCES); } -static int mount_get_opts(Mount *m, char **ret) { - return fstab_filter_options(m->parameters_fragment.options, - "nofail\0" "noauto\0" "auto\0", NULL, NULL, ret); -} - static void mount_enter_mounting(Mount *m) { int r; MountParameters *p; @@ -889,19 +884,18 @@ static void mount_enter_mounting(Mount *m) { if (p && mount_is_bind(p)) (void) mkdir_p_label(p->what, m->directory_mode); - if (m->from_fragment) { + if (p) { _cleanup_free_ char *opts = NULL; - r = mount_get_opts(m, &opts); + r = fstab_filter_options(p->options, "nofail\0" "noauto\0" "auto\0", NULL, NULL, &opts); if (r < 0) goto fail; - r = exec_command_set(m->control_command, MOUNT_PATH, - m->parameters_fragment.what, m->where, NULL); + r = exec_command_set(m->control_command, MOUNT_PATH, p->what, m->where, NULL); if (r >= 0 && m->sloppy_options) r = exec_command_append(m->control_command, "-s", NULL); - if (r >= 0 && m->parameters_fragment.fstype) - r = exec_command_append(m->control_command, "-t", m->parameters_fragment.fstype, NULL); + if (r >= 0 && p->fstype) + r = exec_command_append(m->control_command, "-t", p->fstype, NULL); if (r >= 0 && !isempty(opts)) r = exec_command_append(m->control_command, "-o", opts, NULL); } else @@ -927,27 +921,29 @@ fail: static void mount_enter_remounting(Mount *m) { int r; + MountParameters *p; assert(m); m->control_command_id = MOUNT_EXEC_REMOUNT; m->control_command = m->exec_command + MOUNT_EXEC_REMOUNT; - if (m->from_fragment) { + p = get_mount_parameters_fragment(m); + if (p) { const char *o; - if (m->parameters_fragment.options) - o = strjoina("remount,", m->parameters_fragment.options); + if (p->options) + o = strjoina("remount,", p->options); else o = "remount"; r = exec_command_set(m->control_command, MOUNT_PATH, - m->parameters_fragment.what, m->where, + p->what, m->where, "-o", o, NULL); if (r >= 0 && m->sloppy_options) r = exec_command_append(m->control_command, "-s", NULL); - if (r >= 0 && m->parameters_fragment.fstype) - r = exec_command_append(m->control_command, "-t", m->parameters_fragment.fstype, NULL); + if (r >= 0 && p->fstype) + r = exec_command_append(m->control_command, "-t", p->fstype, NULL); } else r = -ENOENT; @@ -1162,7 +1158,7 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) { else assert_not_reached("Unknown code"); - if (f != MOUNT_SUCCESS) + if (m->result == MOUNT_SUCCESS) m->result = f; if (m->control_command) { diff --git a/src/core/path.c b/src/core/path.c index 0dd0d375d8..10f9b06974 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -454,7 +454,7 @@ static int path_coldplug(Unit *u) { static void path_enter_dead(Path *p, PathResult f) { assert(p); - if (f != PATH_SUCCESS) + if (p->result == PATH_SUCCESS) p->result = f; path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD); diff --git a/src/core/scope.c b/src/core/scope.c index b45e238974..b278aed3d6 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -221,7 +221,7 @@ static void scope_dump(Unit *u, FILE *f, const char *prefix) { static void scope_enter_dead(Scope *s, ScopeResult f) { assert(s); - if (f != SCOPE_SUCCESS) + if (s->result == SCOPE_SUCCESS) s->result = f; scope_set_state(s, s->result != SCOPE_SUCCESS ? SCOPE_FAILED : SCOPE_DEAD); @@ -233,7 +233,7 @@ static void scope_enter_signal(Scope *s, ScopeState state, ScopeResult f) { assert(s); - if (f != SCOPE_SUCCESS) + if (s->result == SCOPE_SUCCESS) s->result = f; unit_watch_all_pids(UNIT(s)); diff --git a/src/core/service.c b/src/core/service.c index eb125cb999..4a37702f52 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -761,6 +761,11 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { prefix, s->bus_name, prefix, yes_no(s->bus_name_good)); + if (UNIT_ISSET(s->accept_socket)) + fprintf(f, + "%sAccept Socket: %s\n", + prefix, UNIT_DEREF(s->accept_socket)->id); + kill_context_dump(&s->kill_context, f, prefix); exec_context_dump(&s->exec_context, f, prefix); @@ -1036,6 +1041,20 @@ static int service_coldplug(Unit *u) { if (!IN_SET(s->deserialized_state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART)) (void) unit_setup_dynamic_creds(u); + if (UNIT_ISSET(s->accept_socket)) { + Socket* socket = SOCKET(UNIT_DEREF(s->accept_socket)); + + if (socket->max_connections_per_source > 0) { + SocketPeer *peer; + + /* Make a best-effort attempt at bumping the connection count */ + if (socket_acquire_peer(socket, s->socket_fd, &peer) > 0) { + socket_peer_unref(s->peer); + s->peer = peer; + } + } + } + service_set_state(s, s->deserialized_state); return 0; } @@ -1152,11 +1171,7 @@ static int service_spawn( Service *s, ExecCommand *c, usec_t timeout, - bool pass_fds, - bool apply_permissions, - bool apply_chroot, - bool apply_tty_stdin, - bool is_control, + ExecFlags flags, pid_t *_pid) { _cleanup_strv_free_ char **argv = NULL, **final_env = NULL, **our_env = NULL, **fd_names = NULL; @@ -1166,12 +1181,10 @@ static int service_spawn( pid_t pid; ExecParameters exec_params = { - .apply_permissions = apply_permissions, - .apply_chroot = apply_chroot, - .apply_tty_stdin = apply_tty_stdin, - .stdin_fd = -1, - .stdout_fd = -1, - .stderr_fd = -1, + .flags = flags, + .stdin_fd = -1, + .stdout_fd = -1, + .stderr_fd = -1, }; int r; @@ -1180,6 +1193,14 @@ static int service_spawn( assert(c); assert(_pid); + if (flags & EXEC_IS_CONTROL) { + /* If this is a control process, mask the permissions/chroot application if this is requested. */ + if (s->permissions_start_only) + exec_params.flags &= ~EXEC_APPLY_PERMISSIONS; + if (s->root_directory_start_only) + exec_params.flags &= ~EXEC_APPLY_CHROOT; + } + (void) unit_realize_cgroup(UNIT(s)); if (s->reset_cpu_usage) { (void) unit_reset_cpu_usage(UNIT(s)); @@ -1194,7 +1215,7 @@ static int service_spawn( if (r < 0) return r; - if (pass_fds || + if ((flags & EXEC_PASS_FDS) || s->exec_context.std_input == EXEC_INPUT_SOCKET || s->exec_context.std_output == EXEC_OUTPUT_SOCKET || s->exec_context.std_error == EXEC_OUTPUT_SOCKET) { @@ -1214,11 +1235,11 @@ static int service_spawn( if (r < 0) return r; - our_env = new0(char*, 6); + our_env = new0(char*, 9); if (!our_env) return -ENOMEM; - if (is_control ? s->notify_access == NOTIFY_ALL : s->notify_access != NOTIFY_NONE) + if ((flags & EXEC_IS_CONTROL) ? s->notify_access == NOTIFY_ALL : s->notify_access != NOTIFY_NONE) if (asprintf(our_env + n_env++, "NOTIFY_SOCKET=%s", UNIT(s)->manager->notify_socket) < 0) return -ENOMEM; @@ -1226,7 +1247,7 @@ static int service_spawn( if (asprintf(our_env + n_env++, "MAINPID="PID_FMT, s->main_pid) < 0) return -ENOMEM; - if (!MANAGER_IS_SYSTEM(UNIT(s)->manager)) + if (MANAGER_IS_USER(UNIT(s)->manager)) if (asprintf(our_env + n_env++, "MANAGERPID="PID_FMT, getpid()) < 0) return -ENOMEM; @@ -1262,22 +1283,40 @@ static int service_spawn( } } + if (flags & EXEC_SETENV_RESULT) { + if (asprintf(our_env + n_env++, "SERVICE_RESULT=%s", service_result_to_string(s->result)) < 0) + return -ENOMEM; + + if (s->main_exec_status.pid > 0 && + dual_timestamp_is_set(&s->main_exec_status.exit_timestamp)) { + if (asprintf(our_env + n_env++, "EXIT_CODE=%s", sigchld_code_to_string(s->main_exec_status.code)) < 0) + return -ENOMEM; + + if (s->main_exec_status.code == CLD_EXITED) + r = asprintf(our_env + n_env++, "EXIT_STATUS=%i", s->main_exec_status.status); + else + r = asprintf(our_env + n_env++, "EXIT_STATUS=%s", signal_to_string(s->main_exec_status.status)); + if (r < 0) + return -ENOMEM; + } + } + final_env = strv_env_merge(2, UNIT(s)->manager->environment, our_env, NULL); if (!final_env) return -ENOMEM; - if (is_control && UNIT(s)->cgroup_path) { + if ((flags & EXEC_IS_CONTROL) && UNIT(s)->cgroup_path) { path = strjoina(UNIT(s)->cgroup_path, "/control"); (void) cg_create(SYSTEMD_CGROUP_CONTROLLER, path); } else path = UNIT(s)->cgroup_path; exec_params.argv = argv; + exec_params.environment = final_env; exec_params.fds = fds; exec_params.fd_names = fd_names; exec_params.n_fds = n_fds; - exec_params.environment = final_env; - exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn; + exec_params.flags |= UNIT(s)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0; exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported; exec_params.cgroup_path = path; exec_params.cgroup_delegate = s->cgroup_context.delegate; @@ -1403,7 +1442,7 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) int r; assert(s); - if (f != SERVICE_SUCCESS) + if (s->result == SERVICE_SUCCESS) s->result = f; service_set_state(s, s->result != SERVICE_SUCCESS ? SERVICE_FAILED : SERVICE_DEAD); @@ -1452,7 +1491,7 @@ static void service_enter_stop_post(Service *s, ServiceResult f) { int r; assert(s); - if (f != SERVICE_SUCCESS) + if (s->result == SERVICE_SUCCESS) s->result = f; service_unwatch_control_pid(s); @@ -1465,11 +1504,7 @@ static void service_enter_stop_post(Service *s, ServiceResult f) { r = service_spawn(s, s->control_command, s->timeout_stop_usec, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - true, - true, + EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_IS_CONTROL|EXEC_SETENV_RESULT, &s->control_pid); if (r < 0) goto fail; @@ -1509,7 +1544,7 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f assert(s); - if (f != SERVICE_SUCCESS) + if (s->result == SERVICE_SUCCESS) s->result = f; unit_watch_all_pids(UNIT(s)); @@ -1567,7 +1602,7 @@ static void service_enter_stop(Service *s, ServiceResult f) { assert(s); - if (f != SERVICE_SUCCESS) + if (s->result == SERVICE_SUCCESS) s->result = f; service_unwatch_control_pid(s); @@ -1580,11 +1615,7 @@ static void service_enter_stop(Service *s, ServiceResult f) { r = service_spawn(s, s->control_command, s->timeout_stop_usec, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - false, - true, + EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_SETENV_RESULT, &s->control_pid); if (r < 0) goto fail; @@ -1623,7 +1654,7 @@ static bool service_good(Service *s) { static void service_enter_running(Service *s, ServiceResult f) { assert(s); - if (f != SERVICE_SUCCESS) + if (s->result == SERVICE_SUCCESS) s->result = f; service_unwatch_control_pid(s); @@ -1661,11 +1692,7 @@ static void service_enter_start_post(Service *s) { r = service_spawn(s, s->control_command, s->timeout_start_usec, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - false, - true, + EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL, &s->control_pid); if (r < 0) goto fail; @@ -1735,11 +1762,7 @@ static void service_enter_start(Service *s) { r = service_spawn(s, c, timeout, - true, - true, - true, - true, - false, + EXEC_PASS_FDS|EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_SET_WATCHDOG, &pid); if (r < 0) goto fail; @@ -1798,11 +1821,7 @@ static void service_enter_start_pre(Service *s) { r = service_spawn(s, s->control_command, s->timeout_start_usec, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - true, - true, + EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_APPLY_TTY_STDIN, &s->control_pid); if (r < 0) goto fail; @@ -1877,11 +1896,7 @@ static void service_enter_reload(Service *s) { r = service_spawn(s, s->control_command, s->timeout_start_usec, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - false, - true, + EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL, &s->control_pid); if (r < 0) goto fail; @@ -1919,12 +1934,9 @@ static void service_run_next_control(Service *s) { r = service_spawn(s, s->control_command, timeout, - false, - !s->permissions_start_only, - !s->root_directory_start_only, - s->control_command_id == SERVICE_EXEC_START_PRE || - s->control_command_id == SERVICE_EXEC_STOP_POST, - true, + EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL| + (IN_SET(s->control_command_id, SERVICE_EXEC_START_PRE, SERVICE_EXEC_STOP_POST) ? EXEC_APPLY_TTY_STDIN : 0)| + (IN_SET(s->control_command_id, SERVICE_EXEC_STOP, SERVICE_EXEC_STOP_POST) ? EXEC_SETENV_RESULT : 0), &s->control_pid); if (r < 0) goto fail; @@ -1962,11 +1974,7 @@ static void service_run_next_main(Service *s) { r = service_spawn(s, s->main_command, s->timeout_start_usec, - true, - true, - true, - true, - false, + EXEC_PASS_FDS|EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_SET_WATCHDOG, &pid); if (r < 0) goto fail; @@ -2130,6 +2138,12 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) { if (r < 0) return r; + if (UNIT_ISSET(s->accept_socket)) { + r = unit_serialize_item(u, f, "accept-socket", UNIT_DEREF(s->accept_socket)->id); + if (r < 0) + return r; + } + r = unit_serialize_item_fd(u, f, fds, "socket-fd", s->socket_fd); if (r < 0) return r; @@ -2260,6 +2274,17 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value, s->control_command_id = id; s->control_command = s->exec_command[id]; } + } else if (streq(key, "accept-socket")) { + Unit *socket; + + r = manager_load_unit(u->manager, value, NULL, NULL, &socket); + if (r < 0) + log_unit_debug_errno(u, r, "Failed to load accept-socket unit: %s", value); + else { + unit_ref_set(&s->accept_socket, socket); + SOCKET(socket)->n_connections++; + } + } else if (streq(key, "socket-fd")) { int fd; @@ -2620,7 +2645,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { "EXIT_STATUS=%i", status, NULL); - if (f != SERVICE_SUCCESS) + if (s->result == SERVICE_SUCCESS) s->result = f; if (s->main_command && @@ -2701,7 +2726,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { "Control process exited, code=%s status=%i", sigchld_code_to_string(code), status); - if (f != SERVICE_SUCCESS) + if (s->result == SERVICE_SUCCESS) s->result = f; /* Immediately get rid of the cgroup, so that the diff --git a/src/core/socket.c b/src/core/socket.c index ff55885fb3..50872e8366 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -59,6 +59,13 @@ #include "user-util.h" #include "in-addr-util.h" +struct SocketPeer { + unsigned n_ref; + + Socket *socket; + union sockaddr_union peer; +}; + static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = { [SOCKET_DEAD] = UNIT_INACTIVE, [SOCKET_START_PRE] = UNIT_ACTIVATING, @@ -78,9 +85,6 @@ static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = { static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata); static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata); -SocketPeer *socket_peer_new(void); -int socket_find_peer(Socket *s, int fd, SocketPeer **p); - static void socket_init(Unit *u) { Socket *s = SOCKET(u); @@ -151,10 +155,10 @@ static void socket_done(Unit *u) { socket_free_ports(s); - while ((p = hashmap_steal_first(s->peers_by_address))) + while ((p = set_steal_first(s->peers_by_address))) p->socket = NULL; - s->peers_by_address = hashmap_free(s->peers_by_address); + s->peers_by_address = set_free(s->peers_by_address); s->exec_runtime = exec_runtime_unref(s->exec_runtime); exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX); @@ -482,10 +486,11 @@ static void peer_address_hash_func(const void *p, struct siphash *state) { const SocketPeer *s = p; assert(s); + assert(IN_SET(s->peer.sa.sa_family, AF_INET, AF_INET6)); if (s->peer.sa.sa_family == AF_INET) siphash24_compress(&s->peer.in.sin_addr, sizeof(s->peer.in.sin_addr), state); - else if (s->peer.sa.sa_family == AF_INET6) + else siphash24_compress(&s->peer.in6.sin6_addr, sizeof(s->peer.in6.sin6_addr), state); } @@ -503,8 +508,7 @@ static int peer_address_compare_func(const void *a, const void *b) { case AF_INET6: return memcmp(&x->peer.in6.sin6_addr, &y->peer.in6.sin6_addr, sizeof(x->peer.in6.sin6_addr)); } - - return -1; + assert_not_reached("Black sheep in the family!"); } const struct hash_ops peer_address_hash_ops = { @@ -519,7 +523,7 @@ static int socket_load(Unit *u) { assert(u); assert(u->load_state == UNIT_STUB); - r = hashmap_ensure_allocated(&s->peers_by_address, &peer_address_hash_ops); + r = set_ensure_allocated(&s->peers_by_address, &peer_address_hash_ops); if (r < 0) return r; @@ -537,6 +541,87 @@ static int socket_load(Unit *u) { return socket_verify(s); } +static SocketPeer *socket_peer_new(void) { + SocketPeer *p; + + p = new0(SocketPeer, 1); + if (!p) + return NULL; + + p->n_ref = 1; + + return p; +} + +SocketPeer *socket_peer_ref(SocketPeer *p) { + if (!p) + return NULL; + + assert(p->n_ref > 0); + p->n_ref++; + + return p; +} + +SocketPeer *socket_peer_unref(SocketPeer *p) { + if (!p) + return NULL; + + assert(p->n_ref > 0); + + p->n_ref--; + + if (p->n_ref > 0) + return NULL; + + if (p->socket) + set_remove(p->socket->peers_by_address, p); + + return mfree(p); +} + +int socket_acquire_peer(Socket *s, int fd, SocketPeer **p) { + _cleanup_(socket_peer_unrefp) SocketPeer *remote = NULL; + SocketPeer sa = {}, *i; + socklen_t salen = sizeof(sa.peer); + int r; + + assert(fd >= 0); + assert(s); + + r = getpeername(fd, &sa.peer.sa, &salen); + if (r < 0) + return log_error_errno(errno, "getpeername failed: %m"); + + if (!IN_SET(sa.peer.sa.sa_family, AF_INET, AF_INET6)) { + *p = NULL; + return 0; + } + + i = set_get(s->peers_by_address, &sa); + if (i) { + *p = socket_peer_ref(i); + return 1; + } + + remote = socket_peer_new(); + if (!remote) + return log_oom(); + + remote->peer = sa.peer; + + r = set_put(s->peers_by_address, remote); + if (r < 0) + return r; + + remote->socket = s; + + *p = remote; + remote = NULL; + + return 1; +} + _const_ static const char* listen_lookup(int family, int type) { if (family == AF_NETLINK) @@ -1664,12 +1749,10 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { pid_t pid; int r; ExecParameters exec_params = { - .apply_permissions = true, - .apply_chroot = true, - .apply_tty_stdin = true, - .stdin_fd = -1, - .stdout_fd = -1, - .stderr_fd = -1, + .flags = EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN, + .stdin_fd = -1, + .stdout_fd = -1, + .stderr_fd = -1, }; assert(s); @@ -1700,7 +1783,7 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { exec_params.argv = argv; exec_params.environment = UNIT(s)->manager->environment; - exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn; + exec_params.flags |= UNIT(s)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0; exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported; exec_params.cgroup_path = UNIT(s)->cgroup_path; exec_params.cgroup_delegate = s->cgroup_context.delegate; @@ -1812,7 +1895,7 @@ fail: static void socket_enter_dead(Socket *s, SocketResult f) { assert(s); - if (f != SOCKET_SUCCESS) + if (s->result == SOCKET_SUCCESS) s->result = f; socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD); @@ -1831,7 +1914,7 @@ static void socket_enter_stop_post(Socket *s, SocketResult f) { int r; assert(s); - if (f != SOCKET_SUCCESS) + if (s->result == SOCKET_SUCCESS) s->result = f; socket_unwatch_control_pid(s); @@ -1859,7 +1942,7 @@ static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) { assert(s); - if (f != SOCKET_SUCCESS) + if (s->result == SOCKET_SUCCESS) s->result = f; r = unit_kill_context( @@ -1903,7 +1986,7 @@ static void socket_enter_stop_pre(Socket *s, SocketResult f) { int r; assert(s); - if (f != SOCKET_SUCCESS) + if (s->result == SOCKET_SUCCESS) s->result = f; socket_unwatch_control_pid(s); @@ -2102,22 +2185,26 @@ static void socket_enter_running(Socket *s, int cfd) { Service *service; if (s->n_connections >= s->max_connections) { - log_unit_warning(UNIT(s), "Too many incoming connections (%u), refusing connection attempt.", s->n_connections); + log_unit_warning(UNIT(s), "Too many incoming connections (%u), dropping connection.", + s->n_connections); safe_close(cfd); return; } if (s->max_connections_per_source > 0) { - r = socket_find_peer(s, cfd, &p); + r = socket_acquire_peer(s, cfd, &p); if (r < 0) { safe_close(cfd); return; - } + } else if (r > 0 && p->n_ref > s->max_connections_per_source) { + _cleanup_free_ char *t = NULL; + + sockaddr_pretty(&p->peer.sa, FAMILY_ADDRESS_SIZE(p->peer.sa.sa_family), true, false, &t); - if (p->n_ref > s->max_connections_per_source) { - log_unit_warning(UNIT(s), "Too many incoming connections (%u) from source, refusing connection attempt.", p->n_ref); + log_unit_warning(UNIT(s), + "Too many incoming connections (%u) from source %s, dropping connection.", + p->n_ref, strnull(t)); safe_close(cfd); - p = NULL; return; } } @@ -2163,10 +2250,8 @@ static void socket_enter_running(Socket *s, int cfd) { cfd = -1; /* We passed ownership of the fd to the service now. Forget it here. */ s->n_connections++; - if (s->max_connections_per_source > 0) { - service->peer = socket_peer_ref(p); - p = NULL; - } + service->peer = p; /* Pass ownership of the peer reference */ + p = NULL; r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, &error, NULL); if (r < 0) { @@ -2313,9 +2398,7 @@ static int socket_stop(Unit *u) { static int socket_serialize(Unit *u, FILE *f, FDSet *fds) { Socket *s = SOCKET(u); - SocketPeer *k; SocketPort *p; - Iterator i; int r; assert(u); @@ -2366,19 +2449,14 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) { } } - HASHMAP_FOREACH(k, s->peers_by_address, i) { - _cleanup_free_ char *t = NULL; - - r = sockaddr_pretty(&k->peer.sa, FAMILY_ADDRESS_SIZE(k->peer.sa.sa_family), true, true, &t); - if (r < 0) - return r; - - unit_serialize_item_format(u, f, "peer", "%u %s", k->n_ref, t); - } - return 0; } +static void socket_port_take_fd(SocketPort *p, FDSet *fds, int fd) { + safe_close(p->fd); + p->fd = fdset_remove(fds, fd); +} + static int socket_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { Socket *s = SOCKET(u); @@ -2433,18 +2511,13 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse fifo value: %s", value); - else { - + else LIST_FOREACH(port, p, s->ports) if (p->type == SOCKET_FIFO && - path_equal_or_files_same(p->path, value+skip)) + path_equal_or_files_same(p->path, value+skip)) { + socket_port_take_fd(p, fds, fd); break; - - if (p) { - safe_close(p->fd); - p->fd = fdset_remove(fds, fd); - } - } + } } else if (streq(key, "special")) { int fd, skip = 0; @@ -2452,18 +2525,13 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse special value: %s", value); - else { - + else LIST_FOREACH(port, p, s->ports) if (p->type == SOCKET_SPECIAL && - path_equal_or_files_same(p->path, value+skip)) + path_equal_or_files_same(p->path, value+skip)) { + socket_port_take_fd(p, fds, fd); break; - - if (p) { - safe_close(p->fd); - p->fd = fdset_remove(fds, fd); - } - } + } } else if (streq(key, "mqueue")) { int fd, skip = 0; @@ -2471,18 +2539,13 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse mqueue value: %s", value); - else { - + else LIST_FOREACH(port, p, s->ports) if (p->type == SOCKET_MQUEUE && - streq(p->path, value+skip)) + streq(p->path, value+skip)) { + socket_port_take_fd(p, fds, fd); break; - - if (p) { - safe_close(p->fd); - p->fd = fdset_remove(fds, fd); - } - } + } } else if (streq(key, "socket")) { int fd, type, skip = 0; @@ -2490,17 +2553,12 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, if (sscanf(value, "%i %i %n", &fd, &type, &skip) < 2 || fd < 0 || type < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse socket value: %s", value); - else { - + else LIST_FOREACH(port, p, s->ports) - if (socket_address_is(&p->address, value+skip, type)) + if (socket_address_is(&p->address, value+skip, type)) { + socket_port_take_fd(p, fds, fd); break; - - if (p) { - safe_close(p->fd); - p->fd = fdset_remove(fds, fd); - } - } + } } else if (streq(key, "netlink")) { int fd, skip = 0; @@ -2508,17 +2566,12 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse socket value: %s", value); - else { - + else LIST_FOREACH(port, p, s->ports) - if (socket_address_is_netlink(&p->address, value+skip)) + if (socket_address_is_netlink(&p->address, value+skip)) { + socket_port_take_fd(p, fds, fd); break; - - if (p) { - safe_close(p->fd); - p->fd = fdset_remove(fds, fd); - } - } + } } else if (streq(key, "ffs")) { int fd, skip = 0; @@ -2526,46 +2579,14 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse ffs value: %s", value); - else { - + else LIST_FOREACH(port, p, s->ports) if (p->type == SOCKET_USB_FUNCTION && - path_equal_or_files_same(p->path, value+skip)) + path_equal_or_files_same(p->path, value+skip)) { + socket_port_take_fd(p, fds, fd); break; + } - if (p) { - safe_close(p->fd); - p->fd = fdset_remove(fds, fd); - } - } - - } else if (streq(key, "peer")) { - _cleanup_(socket_peer_unrefp) SocketPeer *p; - int n_ref, skip = 0; - SocketAddress a; - int r; - - if (sscanf(value, "%u %n", &n_ref, &skip) < 1 || n_ref < 1) - log_unit_debug(u, "Failed to parse socket peer value: %s", value); - else { - r = socket_address_parse(&a, value+skip); - if (r < 0) - return r; - - p = socket_peer_new(); - if (!p) - return log_oom(); - - p->n_ref = n_ref; - memcpy(&p->peer, &a.sockaddr, sizeof(a.sockaddr)); - p->socket = s; - - r = hashmap_put(s->peers_by_address, p, p); - if (r < 0) - return r; - - p = NULL; - } } else log_unit_debug(UNIT(s), "Unknown serialization key: %s", key); @@ -2662,83 +2683,6 @@ _pure_ static bool socket_check_gc(Unit *u) { return s->n_connections > 0; } -SocketPeer *socket_peer_new(void) { - SocketPeer *p; - - p = new0(SocketPeer, 1); - if (!p) - return NULL; - - p->n_ref = 1; - - return p; -} - -SocketPeer *socket_peer_ref(SocketPeer *p) { - if (!p) - return NULL; - - assert(p->n_ref > 0); - p->n_ref++; - - return p; -} - -SocketPeer *socket_peer_unref(SocketPeer *p) { - if (!p) - return NULL; - - assert(p->n_ref > 0); - - p->n_ref--; - - if (p->n_ref > 0) - return NULL; - - if (p->socket) - (void) hashmap_remove(p->socket->peers_by_address, p); - - free(p); - - return NULL; -} - -int socket_find_peer(Socket *s, int fd, SocketPeer **p) { - _cleanup_free_ SocketPeer *remote = NULL; - SocketPeer sa, *i; - socklen_t salen = sizeof(sa.peer); - int r; - - assert(fd >= 0); - assert(s); - - r = getpeername(fd, &sa.peer.sa, &salen); - if (r < 0) - return log_error_errno(errno, "getpeername failed: %m"); - - i = hashmap_get(s->peers_by_address, &sa); - if (i) { - *p = i; - return 1; - } - - remote = socket_peer_new(); - if (!remote) - return log_oom(); - - memcpy(&remote->peer, &sa.peer, sizeof(union sockaddr_union)); - remote->socket = s; - - r = hashmap_put(s->peers_by_address, remote, remote); - if (r < 0) - return r; - - *p = remote; - remote = NULL; - - return 0; -} - static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { SocketPort *p = userdata; int cfd = -1; @@ -2824,7 +2768,7 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) { "Control process exited, code=%s status=%i", sigchld_code_to_string(code), status); - if (f != SOCKET_SUCCESS) + if (s->result == SOCKET_SUCCESS) s->result = f; if (s->control_command && diff --git a/src/core/socket.h b/src/core/socket.h index 2fe38ef2aa..89f4664510 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -80,7 +80,7 @@ struct Socket { LIST_HEAD(SocketPort, ports); - Hashmap *peers_by_address; + Set *peers_by_address; unsigned n_accepted; unsigned n_connections; @@ -168,15 +168,9 @@ struct Socket { RateLimit trigger_limit; }; -struct SocketPeer { - unsigned n_ref; - - Socket *socket; - union sockaddr_union peer; -}; - SocketPeer *socket_peer_ref(SocketPeer *p); SocketPeer *socket_peer_unref(SocketPeer *p); +int socket_acquire_peer(Socket *s, int fd, SocketPeer **p); DEFINE_TRIVIAL_CLEANUP_FUNC(SocketPeer*, socket_peer_unref); diff --git a/src/core/swap.c b/src/core/swap.c index 66a318d01f..2c802da3b5 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -611,12 +611,10 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) { pid_t pid; int r; ExecParameters exec_params = { - .apply_permissions = true, - .apply_chroot = true, - .apply_tty_stdin = true, - .stdin_fd = -1, - .stdout_fd = -1, - .stderr_fd = -1, + .flags = EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN, + .stdin_fd = -1, + .stdout_fd = -1, + .stderr_fd = -1, }; assert(s); @@ -642,7 +640,7 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) { goto fail; exec_params.environment = UNIT(s)->manager->environment; - exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn; + exec_params.flags |= UNIT(s)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0; exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported; exec_params.cgroup_path = UNIT(s)->cgroup_path; exec_params.cgroup_delegate = s->cgroup_context.delegate; @@ -675,7 +673,7 @@ fail: static void swap_enter_dead(Swap *s, SwapResult f) { assert(s); - if (f != SWAP_SUCCESS) + if (s->result == SWAP_SUCCESS) s->result = f; swap_set_state(s, s->result != SWAP_SUCCESS ? SWAP_FAILED : SWAP_DEAD); @@ -691,7 +689,7 @@ static void swap_enter_dead(Swap *s, SwapResult f) { static void swap_enter_active(Swap *s, SwapResult f) { assert(s); - if (f != SWAP_SUCCESS) + if (s->result == SWAP_SUCCESS) s->result = f; swap_set_state(s, SWAP_ACTIVE); @@ -702,7 +700,7 @@ static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) { assert(s); - if (f != SWAP_SUCCESS) + if (s->result == SWAP_SUCCESS) s->result = f; r = unit_kill_context( @@ -999,7 +997,7 @@ static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) { else assert_not_reached("Unknown code"); - if (f != SWAP_SUCCESS) + if (s->result == SWAP_SUCCESS) s->result = f; if (s->control_command) { diff --git a/src/core/timer.c b/src/core/timer.c index 3206296f09..e2b43f02f8 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -291,7 +291,7 @@ static int timer_coldplug(Unit *u) { static void timer_enter_dead(Timer *t, TimerResult f) { assert(t); - if (f != TIMER_SUCCESS) + if (t->result == TIMER_SUCCESS) t->result = f; timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD); diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c index 27b1e0fb3f..bbf8793e57 100644 --- a/src/coredump/coredumpctl.c +++ b/src/coredump/coredumpctl.c @@ -30,6 +30,7 @@ #include "compress.h" #include "fd-util.h" #include "fileio.h" +#include "fs-util.h" #include "journal-internal.h" #include "log.h" #include "macro.h" @@ -609,7 +610,13 @@ static int save_core(sd_journal *j, int fd, char **path, bool *unlink_temp) { char *temp = NULL; if (fd < 0) { - temp = strdup("/var/tmp/coredump-XXXXXX"); + const char *vt; + + r = var_tmp_dir(&vt); + if (r < 0) + return log_error_errno(r, "Failed to acquire temporary directory path: %m"); + + temp = strjoin(vt, "/coredump-XXXXXX", NULL); if (!temp) return log_oom(); diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c index 4ad9184993..05c8698f78 100644 --- a/src/journal-remote/journal-gatewayd.c +++ b/src/journal-remote/journal-gatewayd.c @@ -19,9 +19,6 @@ #include <fcntl.h> #include <getopt.h> -#ifdef HAVE_GNUTLS -#include <gnutls/gnutls.h> -#endif #include <microhttpd.h> #include <stdlib.h> #include <string.h> @@ -48,6 +45,7 @@ static char *arg_key_pem = NULL; static char *arg_cert_pem = NULL; static char *arg_trust_pem = NULL; +static char *arg_directory = NULL; typedef struct RequestMeta { sd_journal *journal; @@ -118,7 +116,10 @@ static int open_journal(RequestMeta *m) { if (m->journal) return 0; - return sd_journal_open(&m->journal, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM); + if (arg_directory) + return sd_journal_open_directory(&m->journal, arg_directory, 0); + else + return sd_journal_open(&m->journal, SD_JOURNAL_LOCAL_ONLY|SD_JOURNAL_SYSTEM); } static int request_meta_ensure_tmp(RequestMeta *m) { @@ -239,6 +240,9 @@ static ssize_t request_reader_entries( m->size = (uint64_t) sz; } + if (m->tmp == NULL && m->follow) + return 0; + if (fseeko(m->tmp, pos, SEEK_SET) < 0) { log_error_errno(errno, "Failed to seek to position: %m"); return MHD_CONTENT_READER_END_WITH_ERROR; @@ -881,7 +885,8 @@ static void help(void) { " --version Show package version\n" " --cert=CERT.PEM Server certificate in PEM format\n" " --key=KEY.PEM Server key in PEM format\n" - " --trust=CERT.PEM Certificat authority certificate in PEM format\n", + " --trust=CERT.PEM Certificat authority certificate in PEM format\n" + " -D --directory=PATH Serve journal files in directory\n", program_invocation_short_name); } @@ -896,11 +901,12 @@ static int parse_argv(int argc, char *argv[]) { int r, c; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "key", required_argument, NULL, ARG_KEY }, - { "cert", required_argument, NULL, ARG_CERT }, - { "trust", required_argument, NULL, ARG_TRUST }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "key", required_argument, NULL, ARG_KEY }, + { "cert", required_argument, NULL, ARG_CERT }, + { "trust", required_argument, NULL, ARG_TRUST }, + { "directory", required_argument, NULL, 'D' }, {} }; @@ -954,6 +960,9 @@ static int parse_argv(int argc, char *argv[]) { #else log_error("Option --trust is not available."); #endif + case 'D': + arg_directory = optarg; + break; case '?': return -EINVAL; diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c index f1ef90ed7a..80e2adb100 100644 --- a/src/journal-remote/journal-remote.c +++ b/src/journal-remote/journal-remote.c @@ -27,10 +27,6 @@ #include <sys/socket.h> #include <unistd.h> -#ifdef HAVE_GNUTLS -#include <gnutls/gnutls.h> -#endif - #include "sd-daemon.h" #include "alloc-util.h" diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index f61f158e8a..4105abfccc 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -826,7 +826,7 @@ int journal_file_verify( int data_fd = -1, entry_fd = -1, entry_array_fd = -1; unsigned i; bool found_last = false; - _cleanup_free_ char *tmp_dir = NULL; + const char *tmp_dir = NULL; #ifdef HAVE_GCRYPT uint64_t last_tag = 0; @@ -846,7 +846,7 @@ int journal_file_verify( } else if (f->seal) return -ENOKEY; - r = var_tmp(&tmp_dir); + r = var_tmp_dir(&tmp_dir); if (r < 0) { log_error_errno(r, "Failed to determine temporary directory: %m"); goto fail; diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 53c6180864..6f841efb69 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -297,9 +297,9 @@ static void help(void) { " -n --lines[=INTEGER] Number of journal entries to show\n" " --no-tail Show all lines, even in follow mode\n" " -r --reverse Show the newest entries first\n" - " -o --output=STRING Change journal output mode (short, short-iso,\n" - " short-precise, short-monotonic, verbose,\n" - " export, json, json-pretty, json-sse, cat)\n" + " -o --output=STRING Change journal output mode (short, short-precise,\n" + " short-iso, short-full, short-monotonic, short-unix,\n" + " verbose, export, json, json-pretty, json-sse, cat)\n" " --utc Express time in Coordinated Universal Time (UTC)\n" " -x --catalog Add message explanations where available\n" " --no-full Ellipsize fields\n" diff --git a/src/journal/lookup3.c b/src/journal/lookup3.c index 3d791234f4..d8f1a4977d 100644 --- a/src/journal/lookup3.c +++ b/src/journal/lookup3.c @@ -317,7 +317,7 @@ uint32_t jenkins_hashlittle( const void *key, size_t length, uint32_t initval) * still catch it and complain. The masking trick does make the hash * noticeably faster for short strings (like English words). */ -#ifndef VALGRIND +#if !defined(VALGRIND) && !defined(__SANITIZE_ADDRESS__) switch(length) { @@ -503,7 +503,7 @@ void jenkins_hashlittle2( * still catch it and complain. The masking trick does make the hash * noticeably faster for short strings (like English words). */ -#ifndef VALGRIND +#if !defined(VALGRIND) && !defined(__SANITIZE_ADDRESS__) switch(length) { @@ -681,7 +681,7 @@ uint32_t jenkins_hashbig( const void *key, size_t length, uint32_t initval) * still catch it and complain. The masking trick does make the hash * noticeably faster for short strings (like English words). */ -#ifndef VALGRIND +#if !defined(VALGRIND) && !defined(__SANITIZE_ADDRESS__) switch(length) { diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 1923e8b971..5e2462cba2 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -954,7 +954,7 @@ static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_err /* Create a temporary file we can dump information about deleted images into. We use a temporary file for this * instead of a pipe or so, since this might grow quit large in theory and we don't want to process this * continuously */ - result_fd = open_tmpfile_unlinkable("/tmp/", O_RDWR|O_CLOEXEC); + result_fd = open_tmpfile_unlinkable(NULL, O_RDWR|O_CLOEXEC); if (result_fd < 0) return -errno; diff --git a/src/network/networkd-brvlan.c b/src/network/networkd-brvlan.c index 8bc330ebae..18ecd86858 100644 --- a/src/network/networkd-brvlan.c +++ b/src/network/networkd-brvlan.c @@ -257,6 +257,24 @@ static int parse_vid_range(const char *rvalue, uint16_t *vid, uint16_t *vid_end) return r; } +int config_parse_brvlan_pvid(const char *unit, const char *filename, + unsigned line, const char *section, + unsigned section_line, const char *lvalue, + int ltype, const char *rvalue, void *data, + void *userdata) { + Network *network = userdata; + int r; + uint16_t pvid; + r = parse_vlanid(rvalue, &pvid); + if (r < 0) + return r; + + network->pvid = pvid; + network->use_br_vlan = true; + + return 0; +} + int config_parse_brvlan_vlan(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, @@ -288,6 +306,7 @@ int config_parse_brvlan_vlan(const char *unit, const char *filename, for (; vid <= vid_end; vid++) set_bit(vid, network->br_vid_bitmap); } + network->use_br_vlan = true; return 0; } @@ -325,5 +344,6 @@ int config_parse_brvlan_untagged(const char *unit, const char *filename, set_bit(vid, network->br_untagged_bitmap); } } + network->use_br_vlan = true; return 0; } diff --git a/src/network/networkd-brvlan.h b/src/network/networkd-brvlan.h index 6aa6883bfc..b37633f94f 100644 --- a/src/network/networkd-brvlan.h +++ b/src/network/networkd-brvlan.h @@ -25,5 +25,6 @@ typedef struct Link Link; int br_vlan_configure(Link *link, uint16_t pvid, uint32_t *br_vid_bitmap, uint32_t *br_untagged_bitmap); +int config_parse_brvlan_pvid(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_brvlan_vlan(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_brvlan_untagged(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index a0da697707..69ee7424ce 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -1314,6 +1314,65 @@ int link_set_mtu(Link *link, uint32_t mtu) { return 0; } +static int set_flags_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { + _cleanup_link_unref_ Link *link = userdata; + int r; + + assert(m); + assert(link); + assert(link->ifname); + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 1; + + r = sd_netlink_message_get_errno(m); + if (r < 0) + log_link_warning_errno(link, r, "Could not set link flags: %m"); + + return 1; +} + +static int link_set_flags(Link *link) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + unsigned ifi_change = 0; + unsigned ifi_flags = 0; + int r; + + assert(link); + assert(link->manager); + assert(link->manager->rtnl); + + if (link->flags & IFF_LOOPBACK) + return 0; + + if (!link->network) + return 0; + + if (link->network->arp < 0) + return 0; + + r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex); + if (r < 0) + return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); + + if (link->network->arp >= 0) { + ifi_change |= IFF_NOARP; + ifi_flags |= link->network->arp ? 0 : IFF_NOARP; + } + + r = sd_rtnl_message_link_set_flags(req, ifi_flags, ifi_change); + if (r < 0) + return log_link_error_errno(link, r, "Could not set link flags: %m"); + + r = sd_netlink_call_async(link->manager->rtnl, req, set_flags_handler, link, 0, NULL); + if (r < 0) + return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); + + link_ref(link); + + return 0; +} + static int link_set_bridge(Link *link) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; @@ -2001,7 +2060,8 @@ static int link_joined(Link *link) { log_link_error_errno(link, r, "Could not set bridge message: %m"); } - if (link->network->bridge || streq_ptr("bridge", link->kind)) { + if (link->network->use_br_vlan && + (link->network->bridge || streq_ptr("bridge", link->kind))) { r = link_set_bridge_vlan(link); if (r < 0) log_link_error_errno(link, r, "Could not set bridge vlan: %m"); @@ -2314,6 +2374,35 @@ static int link_drop_foreign_config(Link *link) { return 0; } +static int link_drop_config(Link *link) { + Address *address; + Route *route; + Iterator i; + int r; + + SET_FOREACH(address, link->addresses, i) { + /* we consider IPv6LL addresses to be managed by the kernel */ + if (address->family == AF_INET6 && in_addr_is_link_local(AF_INET6, &address->in_addr) == 1) + continue; + + r = address_remove(address, link, link_address_remove_handler); + if (r < 0) + return r; + } + + SET_FOREACH(route, link->routes, i) { + /* do not touch routes managed by the kernel */ + if (route->protocol == RTPROT_KERNEL) + continue; + + r = route_remove(route, link, link_route_remove_handler); + if (r < 0) + return r; + } + + return 0; +} + static int link_update_lldp(Link *link) { int r; @@ -2382,6 +2471,10 @@ static int link_configure(Link *link) { if (r < 0) return r; + r = link_set_flags(link); + if (r < 0) + return r; + if (link_ipv4ll_enabled(link)) { r = ipv4ll_configure(link); if (r < 0) @@ -2860,6 +2953,14 @@ static int link_carrier_lost(Link *link) { return r; } + r = link_drop_config(link); + if (r < 0) + return r; + + r = link_drop_foreign_config(link); + if (r < 0) + return r; + r = link_handle_bound_by_list(link); if (r < 0) return r; diff --git a/src/network/networkd-netdev-bridge.c b/src/network/networkd-netdev-bridge.c index a5085d2b19..12b0fe972f 100644 --- a/src/network/networkd-netdev-bridge.c +++ b/src/network/networkd-netdev-bridge.c @@ -108,6 +108,12 @@ static int netdev_bridge_post_create(NetDev *netdev, Link *link, sd_netlink_mess return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_VLAN_FILTERING attribute: %m"); } + if (b->stp >= 0) { + r = sd_netlink_message_append_u32(req, IFLA_BR_STP_STATE, b->stp); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_STP_STATE attribute: %m"); + } + r = sd_netlink_message_close_container(req); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m"); @@ -135,6 +141,7 @@ static void bridge_init(NetDev *n) { b->mcast_querier = -1; b->mcast_snooping = -1; b->vlan_filtering = -1; + b->stp = -1; } const NetDevVTable bridge_vtable = { diff --git a/src/network/networkd-netdev-bridge.h b/src/network/networkd-netdev-bridge.h index a637aea0a3..4ce0fbb6f9 100644 --- a/src/network/networkd-netdev-bridge.h +++ b/src/network/networkd-netdev-bridge.h @@ -27,6 +27,7 @@ typedef struct Bridge { int mcast_querier; int mcast_snooping; int vlan_filtering; + int stp; usec_t forward_delay; usec_t hello_time; diff --git a/src/network/networkd-netdev-gperf.gperf b/src/network/networkd-netdev-gperf.gperf index 9d69f61376..a1ca1a3d4e 100644 --- a/src/network/networkd-netdev-gperf.gperf +++ b/src/network/networkd-netdev-gperf.gperf @@ -106,4 +106,5 @@ Bridge.ForwardDelaySec, config_parse_sec, 0, Bridge.MulticastQuerier, config_parse_tristate, 0, offsetof(Bridge, mcast_querier) Bridge.MulticastSnooping, config_parse_tristate, 0, offsetof(Bridge, mcast_snooping) Bridge.VLANFiltering, config_parse_tristate, 0, offsetof(Bridge, vlan_filtering) +Bridge.STP, config_parse_tristate, 0, offsetof(Bridge, stp) VRF.TableId, config_parse_uint32, 0, offsetof(Vrf, table_id) diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 5172a7b5e9..b96f0b7210 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -28,6 +28,7 @@ Match.KernelCommandLine, config_parse_net_condition, Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(Network, match_arch) Link.MACAddress, config_parse_hwaddr, 0, offsetof(Network, mac) Link.MTUBytes, config_parse_iec_size, 0, offsetof(Network, mtu) +Link.ARP, config_parse_tristate, 0, offsetof(Network, arp) Network.Description, config_parse_string, 0, offsetof(Network, description) Network.Bridge, config_parse_netdev, 0, offsetof(Network, bridge) Network.Bond, config_parse_netdev, 0, offsetof(Network, bond) @@ -114,7 +115,7 @@ Bridge.AllowPortToBeRoot, config_parse_bool, Bridge.UnicastFlood, config_parse_bool, 0, offsetof(Network, unicast_flood) BridgeFDB.MACAddress, config_parse_fdb_hwaddr, 0, 0 BridgeFDB.VLANId, config_parse_fdb_vlan_id, 0, 0 -BridgeVLAN.PVID, config_parse_vlanid, 0, offsetof(Network, pvid) +BridgeVLAN.PVID, config_parse_brvlan_pvid, 0, 0 BridgeVLAN.VLAN, config_parse_brvlan_vlan, 0, 0 BridgeVLAN.EgressUntagged, config_parse_brvlan_untagged, 0, 0 /* backwards compatibility: do not add new entries to this section */ diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 2b764d4f24..17bbe5de9f 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -134,6 +134,7 @@ static int network_load_one(Manager *manager, const char *filename) { network->ipv6_hop_limit = -1; network->duid.type = _DUID_TYPE_INVALID; network->proxy_arp = -1; + network->arp = -1; network->ipv6_accept_ra_use_dns = true; r = config_parse(NULL, filename, file, diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 08ee939faa..5460eb4d1c 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -151,6 +151,7 @@ struct Network { bool unicast_flood; unsigned cost; + bool use_br_vlan; uint16_t pvid; uint32_t br_vid_bitmap[BRIDGE_VLAN_BITMAP_LEN]; uint32_t br_untagged_bitmap[BRIDGE_VLAN_BITMAP_LEN]; @@ -171,6 +172,7 @@ struct Network { struct ether_addr *mac; unsigned mtu; + int arp; uint32_t iaid; DUID duid; diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 6cc1b9177d..fcf14bba4c 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -216,10 +216,10 @@ static void help(void) { " --uuid=UUID Set a specific machine UUID for the container\n" " -S --slice=SLICE Place the container in the specified slice\n" " --property=NAME=VALUE Set scope unit property\n" - " -U --private-users=pick Run within user namespace, pick UID/GID range automatically\n" + " -U --private-users=pick Run within user namespace, autoselect UID/GID range\n" " --private-users[=UIDBASE[:NUIDS]]\n" - " Run within user namespace, user configured UID/GID range\n" - " --private-user-chown Adjust OS tree file ownership for private UID/GID range\n" + " Similar, but with user configured UID/GID range\n" + " --private-user-chown Adjust OS tree ownership to private UID/GID range\n" " --private-network Disable network in container\n" " --network-interface=INTERFACE\n" " Assign an existing network interface to the\n" @@ -236,11 +236,10 @@ static void help(void) { " Add an additional virtual Ethernet link between\n" " host and container\n" " --network-bridge=INTERFACE\n" - " Add a virtual Ethernet connection between host\n" - " and container and add it to an existing bridge on\n" - " the host\n" - " --network-zone=NAME Add a virtual Ethernet connection to the container,\n" - " and add it to an automatically managed bridge interface\n" + " Add a virtual Ethernet connection to the container\n" + " and attach it to an existing bridge on the host\n" + " --network-zone=NAME Similar, but attach the new interface to an\n" + " an automatically managed bridge interface\n" " -p --port=[PROTOCOL:]HOSTPORT[:CONTAINERPORT]\n" " Expose a container IP port on the host\n" " -Z --selinux-context=SECLABEL\n" @@ -269,14 +268,12 @@ static void help(void) { " --overlay-ro=PATH[:PATH...]:PATH\n" " Similar, but creates a read-only overlay mount\n" " -E --setenv=NAME=VALUE Pass an environment variable to PID 1\n" - " --share-system Share system namespaces with host\n" " --register=BOOLEAN Register container as machine\n" " --keep-unit Do not register a scope for the machine, reuse\n" " the service unit nspawn is running in\n" " --volatile[=MODE] Run the system in volatile mode\n" " --settings=BOOLEAN Load additional settings from .nspawn file\n" - " --notify-ready=BOOLEAN Receive notifications from the container's init process,\n" - " accepted values: yes and no\n" + " --notify-ready=BOOLEAN Receive notifications from the child init process\n" , program_invocation_short_name); } @@ -405,7 +402,7 @@ static int parse_argv(int argc, char *argv[]) { { "selinux-context", required_argument, NULL, 'Z' }, { "selinux-apifs-context", required_argument, NULL, 'L' }, { "quiet", no_argument, NULL, 'q' }, - { "share-system", no_argument, NULL, ARG_SHARE_SYSTEM }, + { "share-system", no_argument, NULL, ARG_SHARE_SYSTEM }, /* not documented */ { "register", required_argument, NULL, ARG_REGISTER }, { "keep-unit", no_argument, NULL, ARG_KEEP_UNIT }, { "network-interface", required_argument, NULL, ARG_NETWORK_INTERFACE }, @@ -814,6 +811,8 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_SHARE_SYSTEM: + /* We don't officially support this anymore, except for compat reasons. People should use the + * $SYSTEMD_NSPAWN_SHARE_SYSTEM environment variable instead. */ arg_share_system = true; break; @@ -1018,6 +1017,9 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached("Unhandled option"); } + if (getenv_bool("SYSTEMD_NSPAWN_SHARE_SYSTEM") > 0) + arg_share_system = true; + if (arg_share_system) arg_register = false; @@ -1025,7 +1027,7 @@ static int parse_argv(int argc, char *argv[]) { arg_userns_chown = true; if (arg_start_mode != START_PID1 && arg_share_system) { - log_error("--boot and --share-system may not be combined."); + log_error("--boot and SYSTEMD_NSPAWN_SHARE_SYSTEM=1 may not be combined."); return -EINVAL; } @@ -1254,24 +1256,39 @@ static int setup_resolv_conf(const char *dest) { /* Fix resolv.conf, if possible */ where = prefix_roota(dest, "/etc/resolv.conf"); + if (access("/usr/lib/systemd/resolv.conf", F_OK) >= 0) { + /* resolved is enabled on the host. In this, case bind mount its static resolv.conf file into the + * container, so that the container can use the host's resolver. Given that network namespacing is + * disabled it's only natural of the container also uses the host's resolver. It also has the big + * advantage that the container will be able to follow the host's DNS server configuration changes + * transparently. */ + + if (mount("/usr/lib/systemd/resolv.conf", where, NULL, MS_BIND, NULL) < 0) + log_warning_errno(errno, "Failed to mount /etc/resolv.conf in the container, ignoring: %m"); + else { + if (mount(NULL, where, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NOSUID|MS_NODEV, NULL) < 0) + return log_error_errno(errno, "Failed to remount /etc/resolv.conf read-only: %m"); + + return 0; + } + } + + /* If that didn't work, let's copy the file */ r = copy_file("/etc/resolv.conf", where, O_TRUNC|O_NOFOLLOW, 0644, 0); if (r < 0) { - /* If the file already exists as symlink, let's - * suppress the warning, under the assumption that - * resolved or something similar runs inside and the - * symlink points there. + /* If the file already exists as symlink, let's suppress the warning, under the assumption that + * resolved or something similar runs inside and the symlink points there. * - * If the disk image is read-only, there's also no - * point in complaining. + * If the disk image is read-only, there's also no point in complaining. */ log_full_errno(IN_SET(r, -ELOOP, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, - "Failed to copy /etc/resolv.conf to %s: %m", where); + "Failed to copy /etc/resolv.conf to %s, ignoring: %m", where); return 0; } r = userns_lchown(where, 0, 0); if (r < 0) - log_warning_errno(r, "Failed to chown /etc/resolv.conf: %m"); + log_warning_errno(r, "Failed to chown /etc/resolv.conf, ignoring: %m"); return 0; } @@ -1301,7 +1318,7 @@ static int setup_boot_id(const char *dest) { if (mount(from, to, NULL, MS_BIND, NULL) < 0) r = log_error_errno(errno, "Failed to bind mount boot id: %m"); else if (mount(NULL, to, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NOSUID|MS_NODEV, NULL) < 0) - log_warning_errno(errno, "Failed to make boot id read-only, ignoring: %m"); + r = log_error_errno(errno, "Failed to make boot id read-only: %m"); (void) unlink(from); return r; diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c index e7a4393bb0..7078c0c50c 100644 --- a/src/nss-systemd/nss-systemd.c +++ b/src/nss-systemd/nss-systemd.c @@ -26,9 +26,52 @@ #include "macro.h" #include "nss-util.h" #include "signal-util.h" +#include "string-util.h" #include "user-util.h" #include "util.h" +#ifndef NOBODY_USER_NAME +#define NOBODY_USER_NAME "nobody" +#endif + +#ifndef NOBODY_GROUP_NAME +#define NOBODY_GROUP_NAME "nobody" +#endif + +static const struct passwd root_passwd = { + .pw_name = (char*) "root", + .pw_passwd = (char*) "x", /* see shadow file */ + .pw_uid = 0, + .pw_gid = 0, + .pw_gecos = (char*) "Super User", + .pw_dir = (char*) "/root", + .pw_shell = (char*) "/bin/sh", +}; + +static const struct passwd nobody_passwd = { + .pw_name = (char*) NOBODY_USER_NAME, + .pw_passwd = (char*) "*", /* locked */ + .pw_uid = 65534, + .pw_gid = 65534, + .pw_gecos = (char*) "User Nobody", + .pw_dir = (char*) "/", + .pw_shell = (char*) "/sbin/nologin", +}; + +static const struct group root_group = { + .gr_name = (char*) "root", + .gr_gid = 0, + .gr_passwd = (char*) "x", /* see shadow file */ + .gr_mem = (char*[]) { NULL }, +}; + +static const struct group nobody_group = { + .gr_name = (char*) NOBODY_GROUP_NAME, + .gr_gid = 65534, + .gr_passwd = (char*) "*", /* locked */ + .gr_mem = (char*[]) { NULL }, +}; + NSS_GETPW_PROTOTYPES(systemd); NSS_GETGR_PROTOTYPES(systemd); @@ -50,6 +93,23 @@ enum nss_status _nss_systemd_getpwnam_r( assert(name); assert(pwd); + if (!valid_user_group_name(name)) { + r = -EINVAL; + goto fail; + } + + /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */ + if (streq(name, root_passwd.pw_name)) { + *pwd = root_passwd; + *errnop = 0; + return NSS_STATUS_SUCCESS; + } + if (streq(name, nobody_passwd.pw_name)) { + *pwd = nobody_passwd; + *errnop = 0; + return NSS_STATUS_SUCCESS; + } + /* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */ if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) goto not_found; @@ -126,6 +186,18 @@ enum nss_status _nss_systemd_getpwuid_r( goto fail; } + /* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */ + if (uid == root_passwd.pw_uid) { + *pwd = root_passwd; + *errnop = 0; + return NSS_STATUS_SUCCESS; + } + if (uid == nobody_passwd.pw_uid) { + *pwd = nobody_passwd; + *errnop = 0; + return NSS_STATUS_SUCCESS; + } + if (uid <= SYSTEM_UID_MAX) goto not_found; @@ -202,6 +274,23 @@ enum nss_status _nss_systemd_getgrnam_r( assert(name); assert(gr); + if (!valid_user_group_name(name)) { + r = -EINVAL; + goto fail; + } + + /* Synthesize records for root and nobody, in case they are missing form /etc/group */ + if (streq(name, root_group.gr_name)) { + *gr = root_group; + *errnop = 0; + return NSS_STATUS_SUCCESS; + } + if (streq(name, nobody_group.gr_name)) { + *gr = nobody_group; + *errnop = 0; + return NSS_STATUS_SUCCESS; + } + if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) goto not_found; @@ -275,6 +364,18 @@ enum nss_status _nss_systemd_getgrgid_r( goto fail; } + /* Synthesize records for root and nobody, in case they are missing from /etc/group */ + if (gid == root_group.gr_gid) { + *gr = root_group; + *errnop = 0; + return NSS_STATUS_SUCCESS; + } + if (gid == nobody_group.gr_gid) { + *gr = nobody_group; + *errnop = 0; + return NSS_STATUS_SUCCESS; + } + if (gid <= SYSTEM_GID_MAX) goto not_found; diff --git a/src/resolve/resolve-tool.c b/src/resolve/resolve-tool.c index 6ae3750417..07e4cd7d1d 100644 --- a/src/resolve/resolve-tool.c +++ b/src/resolve/resolve-tool.c @@ -1542,7 +1542,7 @@ static void help(void) { "%1$s [OPTIONS...] --statistics\n" "%1$s [OPTIONS...] --reset-statistics\n" "\n" - "Resolve domain names, IPv4 and IPv6 addresses, DNS resource records, and services.\n\n" + "Resolve domain names, IPv4 and IPv6 addresses, DNS records, and services.\n\n" " -h --help Show this help\n" " --version Show package version\n" " --no-pager Do not pipe output into a pager\n" diff --git a/src/run/run.c b/src/run/run.c index 58fa49a4d1..1917ffd857 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -257,11 +257,9 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_NICE: - r = safe_atoi(optarg, &arg_nice); - if (r < 0 || arg_nice < PRIO_MIN || arg_nice >= PRIO_MAX) { - log_error("Failed to parse nice value"); - return -EINVAL; - } + r = parse_nice(optarg, &arg_nice); + if (r < 0) + return log_error_errno(r, "Failed to parse nice value: %s", optarg); arg_nice_set = true; break; diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 9d8061b539..7774d607c7 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -84,7 +84,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen if (isempty(eq)) r = sd_bus_message_append(m, "sv", "CPUQuotaPerSecUSec", "t", USEC_INFINITY); else { - r = parse_percent(eq); + r = parse_percent_unbounded(eq); if (r <= 0) { log_error_errno(r, "CPU quota '%s' invalid.", eq); return -EINVAL; @@ -366,15 +366,13 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen } } else if (streq(field, "Nice")) { - int32_t i; + int n; - r = safe_atoi32(eq, &i); - if (r < 0) { - log_error("Failed to parse %s value %s.", field, eq); - return -EINVAL; - } + r = parse_nice(eq, &n); + if (r < 0) + return log_error_errno(r, "Failed to parse nice value: %s", eq); - r = sd_bus_message_append(m, "v", "i", i); + r = sd_bus_message_append(m, "v", "i", (int32_t) n); } else if (STR_IN_SET(field, "Environment", "PassEnvironment")) { const char *p; diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index d04728f505..f9d9c4ed62 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -45,6 +45,7 @@ #include "parse-util.h" #include "process-util.h" #include "sparse-endian.h" +#include "stdio-util.h" #include "string-table.h" #include "string-util.h" #include "terminal-util.h" @@ -206,6 +207,108 @@ static bool print_multiline(FILE *f, unsigned prefix, unsigned n_columns, Output return ellipsized; } +static int output_timestamp_monotonic(FILE *f, sd_journal *j, const char *monotonic) { + sd_id128_t boot_id; + uint64_t t; + int r; + + assert(f); + assert(j); + + r = -ENXIO; + if (monotonic) + r = safe_atou64(monotonic, &t); + if (r < 0) + r = sd_journal_get_monotonic_usec(j, &t, &boot_id); + if (r < 0) + return log_error_errno(r, "Failed to get monotonic timestamp: %m"); + + fprintf(f, "[%5llu.%06llu]", + (unsigned long long) (t / USEC_PER_SEC), + (unsigned long long) (t % USEC_PER_SEC)); + + return 1 + 5 + 1 + 6 + 1; +} + +static int output_timestamp_realtime(FILE *f, sd_journal *j, OutputMode mode, OutputFlags flags, const char *realtime) { + char buf[MAX(FORMAT_TIMESTAMP_MAX, 64)]; + struct tm *(*gettime_r)(const time_t *, struct tm *); + struct tm tm; + uint64_t x; + time_t t; + int r; + + assert(f); + assert(j); + + r = -ENXIO; + if (realtime) + r = safe_atou64(realtime, &x); + if (r < 0) + r = sd_journal_get_realtime_usec(j, &x); + if (r < 0) + return log_error_errno(r, "Failed to get realtime timestamp: %m"); + + if (mode == OUTPUT_SHORT_FULL) { + const char *k; + + if (flags & OUTPUT_UTC) + k = format_timestamp_utc(buf, sizeof(buf), x); + else + k = format_timestamp(buf, sizeof(buf), x); + if (!k) { + log_error("Failed to format timestamp."); + return -EINVAL; + } + + } else { + gettime_r = (flags & OUTPUT_UTC) ? gmtime_r : localtime_r; + t = (time_t) (x / USEC_PER_SEC); + + switch (mode) { + + case OUTPUT_SHORT_UNIX: + xsprintf(buf, "%10llu.%06llu", (unsigned long long) t, (unsigned long long) (x % USEC_PER_SEC)); + break; + + case OUTPUT_SHORT_ISO: + if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", gettime_r(&t, &tm)) <= 0) { + log_error("Failed for format ISO time"); + return -EINVAL; + } + break; + + case OUTPUT_SHORT: + case OUTPUT_SHORT_PRECISE: + + if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm)) <= 0) { + log_error("Failed to format syslog time"); + return -EINVAL; + } + + if (mode == OUTPUT_SHORT_PRECISE) { + size_t k; + + assert(sizeof(buf) > strlen(buf)); + k = sizeof(buf) - strlen(buf); + + r = snprintf(buf + strlen(buf), k, ".%06llu", (unsigned long long) (x % USEC_PER_SEC)); + if (r <= 0 || (size_t) r >= k) { /* too long? */ + log_error("Failed to format precise time"); + return -EINVAL; + } + } + break; + + default: + assert_not_reached("Unknown time format"); + } + } + + fputs(buf, f); + return (int) strlen(buf); +} + static int output_short( FILE *f, sd_journal *j, @@ -305,78 +408,15 @@ static int output_short( if (priority_len == 1 && *priority >= '0' && *priority <= '7') p = *priority - '0'; - if (mode == OUTPUT_SHORT_MONOTONIC) { - uint64_t t; - sd_id128_t boot_id; - - r = -ENOENT; - - if (monotonic) - r = safe_atou64(monotonic, &t); - - if (r < 0) - r = sd_journal_get_monotonic_usec(j, &t, &boot_id); - - if (r < 0) - return log_error_errno(r, "Failed to get monotonic timestamp: %m"); - - fprintf(f, "[%5llu.%06llu]", - (unsigned long long) (t / USEC_PER_SEC), - (unsigned long long) (t % USEC_PER_SEC)); - - n += 1 + 5 + 1 + 6 + 1; - - } else { - char buf[64]; - uint64_t x; - time_t t; - struct tm tm; - struct tm *(*gettime_r)(const time_t *, struct tm *); - - r = -ENOENT; - gettime_r = (flags & OUTPUT_UTC) ? gmtime_r : localtime_r; - - if (realtime) - r = safe_atou64(realtime, &x); - - if (r < 0) - r = sd_journal_get_realtime_usec(j, &x); - - if (r < 0) - return log_error_errno(r, "Failed to get realtime timestamp: %m"); - - t = (time_t) (x / USEC_PER_SEC); - - switch (mode) { - - case OUTPUT_SHORT_UNIX: - r = snprintf(buf, sizeof(buf), "%10llu.%06llu", (unsigned long long) t, (unsigned long long) (x % USEC_PER_SEC)); - break; - - case OUTPUT_SHORT_ISO: - r = strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S%z", gettime_r(&t, &tm)); - break; - - case OUTPUT_SHORT_PRECISE: - r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm)); - if (r > 0) - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ".%06llu", (unsigned long long) (x % USEC_PER_SEC)); - break; - - default: - r = strftime(buf, sizeof(buf), "%b %d %H:%M:%S", gettime_r(&t, &tm)); - } - - if (r <= 0) { - log_error("Failed to format time."); - return -EINVAL; - } - - fputs(buf, f); - n += strlen(buf); - } + if (mode == OUTPUT_SHORT_MONOTONIC) + r = output_timestamp_monotonic(f, j, monotonic); + else + r = output_timestamp_realtime(f, j, mode, flags, realtime); + if (r < 0) + return r; + n += r; - if (hostname && (flags & OUTPUT_NO_HOSTNAME)) { + if (flags & OUTPUT_NO_HOSTNAME) { /* Suppress display of the hostname if this is requested. */ hostname = NULL; hostname_len = 0; @@ -910,6 +950,7 @@ static int (*output_funcs[_OUTPUT_MODE_MAX])( [OUTPUT_SHORT_PRECISE] = output_short, [OUTPUT_SHORT_MONOTONIC] = output_short, [OUTPUT_SHORT_UNIX] = output_short, + [OUTPUT_SHORT_FULL] = output_short, [OUTPUT_VERBOSE] = output_verbose, [OUTPUT_EXPORT] = output_export, [OUTPUT_JSON] = output_json, diff --git a/src/shared/output-mode.c b/src/shared/output-mode.c index bec53ee0ae..67d8208ad2 100644 --- a/src/shared/output-mode.c +++ b/src/shared/output-mode.c @@ -22,6 +22,7 @@ static const char *const output_mode_table[_OUTPUT_MODE_MAX] = { [OUTPUT_SHORT] = "short", + [OUTPUT_SHORT_FULL] = "short-full", [OUTPUT_SHORT_ISO] = "short-iso", [OUTPUT_SHORT_PRECISE] = "short-precise", [OUTPUT_SHORT_MONOTONIC] = "short-monotonic", diff --git a/src/shared/output-mode.h b/src/shared/output-mode.h index f37189e57f..ff29dafcb5 100644 --- a/src/shared/output-mode.h +++ b/src/shared/output-mode.h @@ -23,6 +23,7 @@ typedef enum OutputMode { OUTPUT_SHORT, + OUTPUT_SHORT_FULL, OUTPUT_SHORT_ISO, OUTPUT_SHORT_PRECISE, OUTPUT_SHORT_MONOTONIC, diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 4fa7adfc41..b4ce6fba5a 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -6591,9 +6591,9 @@ static void systemctl_help(void) { " --preset-mode= Apply only enable, only disable, or all presets\n" " --root=PATH Enable unit files in the specified root directory\n" " -n --lines=INTEGER Number of journal entries to show\n" - " -o --output=STRING Change journal output mode (short, short-iso,\n" - " short-precise, short-monotonic, verbose,\n" - " export, json, json-pretty, json-sse, cat)\n" + " -o --output=STRING Change journal output mode (short, short-precise,\n" + " short-iso, short-full, short-monotonic, short-unix,\n" + " verbose, export, json, json-pretty, json-sse, cat)\n" " --firmware-setup Tell the firmware to show the setup menu on next boot\n" " --plain Print unit dependencies as a list instead of a tree\n\n" "Unit Commands:\n" diff --git a/src/test/test-calendarspec.c b/src/test/test-calendarspec.c index 4a2b93de59..57d9da4855 100644 --- a/src/test/test-calendarspec.c +++ b/src/test/test-calendarspec.c @@ -88,6 +88,27 @@ static void test_next(const char *input, const char *new_tz, usec_t after, usec_ tzset(); } +static void test_timestamp(void) { + char buf[FORMAT_TIMESTAMP_MAX]; + _cleanup_free_ char *t = NULL; + CalendarSpec *c; + usec_t x, y; + + /* Ensure that a timestamp is also a valid calendar specification. Convert forth and back */ + + x = now(CLOCK_REALTIME); + + assert_se(format_timestamp_us(buf, sizeof(buf), x)); + printf("%s\n", buf); + assert_se(calendar_spec_from_string(buf, &c) >= 0); + assert_se(calendar_spec_to_string(c, &t) >= 0); + calendar_spec_free(c); + printf("%s\n", t); + + assert_se(parse_timestamp(t, &y) >= 0); + assert_se(y == x); +} + int main(int argc, char* argv[]) { CalendarSpec *c; @@ -155,5 +176,7 @@ int main(int argc, char* argv[]) { assert_se(calendar_spec_from_string("00:00:00/0.00000001", &c) < 0); assert_se(calendar_spec_from_string("00:00:00.0..00.9", &c) < 0); + test_timestamp(); + return 0; } diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index e0c040f39b..93eec3ef9c 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -83,47 +83,35 @@ static void test_get_files_in_directory(void) { } static void test_var_tmp(void) { - char *tmp_dir = NULL; - char *tmpdir_backup = NULL; - const char *default_var_tmp = NULL; - const char *var_name; - bool do_overwrite = true; - - default_var_tmp = "/var/tmp"; - var_name = "TMPDIR"; - - if (getenv(var_name) != NULL) { - tmpdir_backup = strdup(getenv(var_name)); - assert_se(tmpdir_backup != NULL); - } - - unsetenv(var_name); + _cleanup_free_ char *tmpdir_backup = NULL; + const char *tmp_dir = NULL, *t; - var_tmp(&tmp_dir); - assert_se(!strcmp(tmp_dir, default_var_tmp)); - - free(tmp_dir); + t = getenv("TMPDIR"); + if (t) { + tmpdir_backup = strdup(t); + assert_se(tmpdir_backup); + } - setenv(var_name, "/tmp", do_overwrite); - assert_se(!strcmp(getenv(var_name), "/tmp")); + assert(unsetenv("TMPDIR") >= 0); - var_tmp(&tmp_dir); - assert_se(!strcmp(tmp_dir, "/tmp")); + assert_se(var_tmp_dir(&tmp_dir) >= 0); + assert_se(streq(tmp_dir, "/var/tmp")); - free(tmp_dir); + assert_se(setenv("TMPDIR", "/tmp", true) >= 0); + assert_se(streq(getenv("TMPDIR"), "/tmp")); - setenv(var_name, "/88_does_not_exist_88", do_overwrite); - assert_se(!strcmp(getenv(var_name), "/88_does_not_exist_88")); + assert_se(var_tmp_dir(&tmp_dir) >= 0); + assert_se(streq(tmp_dir, "/tmp")); - var_tmp(&tmp_dir); - assert_se(!strcmp(tmp_dir, default_var_tmp)); + assert_se(setenv("TMPDIR", "/88_does_not_exist_88", true) >= 0); + assert_se(streq(getenv("TMPDIR"), "/88_does_not_exist_88")); - free(tmp_dir); + assert_se(var_tmp_dir(&tmp_dir) >= 0); + assert_se(streq(tmp_dir, "/var/tmp")); - if (tmpdir_backup != NULL) { - setenv(var_name, tmpdir_backup, do_overwrite); - assert_se(!strcmp(getenv(var_name), tmpdir_backup)); - free(tmpdir_backup); + if (tmpdir_backup) { + assert_se(setenv("TMPDIR", tmpdir_backup, true) >= 0); + assert_se(streq(getenv("TMPDIR"), tmpdir_backup)); } } diff --git a/src/test/test-id128.c b/src/test/test-id128.c index f01fbdd6b2..1c8e5549da 100644 --- a/src/test/test-id128.c +++ b/src/test/test-id128.c @@ -144,7 +144,7 @@ int main(int argc, char *argv[]) { assert_se(ftruncate(fd, 0) >= 0); assert_se(sd_id128_randomize(&id) >= 0); - assert_se(write(fd, id128_to_uuid_string(id, t), 36) == 36); + assert_se(write(fd, id128_to_uuid_string(id, q), 36) == 36); assert_se(lseek(fd, 0, SEEK_SET) == 0); assert_se(id128_read_fd(fd, ID128_PLAIN, &id2) == -EINVAL); diff --git a/src/test/test-parse-util.c b/src/test/test-parse-util.c index 0a76308f72..d08014100b 100644 --- a/src/test/test-parse-util.c +++ b/src/test/test-parse-util.c @@ -493,6 +493,39 @@ static void test_parse_percent(void) { assert_se(parse_percent("1%%") == -EINVAL); } +static void test_parse_percent_unbounded(void) { + assert_se(parse_percent_unbounded("101%") == 101); + assert_se(parse_percent_unbounded("400%") == 400); +} + +static void test_parse_nice(void) { + int n; + + assert_se(parse_nice("0", &n) >= 0 && n == 0); + assert_se(parse_nice("+0", &n) >= 0 && n == 0); + assert_se(parse_nice("-1", &n) >= 0 && n == -1); + assert_se(parse_nice("-2", &n) >= 0 && n == -2); + assert_se(parse_nice("1", &n) >= 0 && n == 1); + assert_se(parse_nice("2", &n) >= 0 && n == 2); + assert_se(parse_nice("+1", &n) >= 0 && n == 1); + assert_se(parse_nice("+2", &n) >= 0 && n == 2); + assert_se(parse_nice("-20", &n) >= 0 && n == -20); + assert_se(parse_nice("19", &n) >= 0 && n == 19); + assert_se(parse_nice("+19", &n) >= 0 && n == 19); + + + assert_se(parse_nice("", &n) == -EINVAL); + assert_se(parse_nice("-", &n) == -EINVAL); + assert_se(parse_nice("+", &n) == -EINVAL); + assert_se(parse_nice("xx", &n) == -EINVAL); + assert_se(parse_nice("-50", &n) == -ERANGE); + assert_se(parse_nice("50", &n) == -ERANGE); + assert_se(parse_nice("+50", &n) == -ERANGE); + assert_se(parse_nice("-21", &n) == -ERANGE); + assert_se(parse_nice("20", &n) == -ERANGE); + assert_se(parse_nice("+20", &n) == -ERANGE); +} + int main(int argc, char *argv[]) { log_parse_environment(); log_open(); @@ -507,6 +540,8 @@ int main(int argc, char *argv[]) { test_safe_atoi16(); test_safe_atod(); test_parse_percent(); + test_parse_percent_unbounded(); + test_parse_nice(); return 0; } diff --git a/src/test/test-sigbus.c b/src/test/test-sigbus.c index 17b81747be..02b8e24308 100644 --- a/src/test/test-sigbus.c +++ b/src/test/test-sigbus.c @@ -29,6 +29,9 @@ int main(int argc, char *argv[]) { void *addr = NULL; uint8_t *p; +#ifdef __SANITIZE_ADDRESS__ + return EXIT_TEST_SKIP; +#endif sigbus_install(); assert_se(sigbus_pop(&addr) == 0); diff --git a/src/test/test-time.c b/src/test/test-time.c index ee7d55c5ab..7078a0374d 100644 --- a/src/test/test-time.c +++ b/src/test/test-time.c @@ -19,6 +19,7 @@ #include "strv.h" #include "time-util.h" +#include "random-util.h" static void test_parse_sec(void) { usec_t u; @@ -201,6 +202,48 @@ static void test_usec_sub(void) { assert_se(usec_sub(USEC_INFINITY, 5) == USEC_INFINITY); } +static void test_format_timestamp(void) { + unsigned i; + + for (i = 0; i < 100; i++) { + char buf[MAX(FORMAT_TIMESTAMP_MAX, FORMAT_TIMESPAN_MAX)]; + usec_t x, y; + + random_bytes(&x, sizeof(x)); + x = x % (2147483600 * USEC_PER_SEC) + 1; + + assert_se(format_timestamp(buf, sizeof(buf), x)); + log_info("%s", buf); + assert_se(parse_timestamp(buf, &y) >= 0); + assert_se(x / USEC_PER_SEC == y / USEC_PER_SEC); + + assert_se(format_timestamp_utc(buf, sizeof(buf), x)); + log_info("%s", buf); + assert_se(parse_timestamp(buf, &y) >= 0); + assert_se(x / USEC_PER_SEC == y / USEC_PER_SEC); + + assert_se(format_timestamp_us(buf, sizeof(buf), x)); + log_info("%s", buf); + assert_se(parse_timestamp(buf, &y) >= 0); + assert_se(x == y); + + assert_se(format_timestamp_us_utc(buf, sizeof(buf), x)); + log_info("%s", buf); + assert_se(parse_timestamp(buf, &y) >= 0); + assert_se(x == y); + + assert_se(format_timestamp_relative(buf, sizeof(buf), x)); + log_info("%s", buf); + assert_se(parse_timestamp(buf, &y) >= 0); + + /* The two calls above will run with a slightly different local time. Make sure we are in the same + * range however, but give enough leeway that this is unlikely to explode. And of course, + * format_timestamp_relative() scales the accuracy with the distance from the current time up to one + * month, cover for that too. */ + assert_se(y > x ? y - x : x - y <= USEC_PER_MONTH + USEC_PER_DAY); + } +} + int main(int argc, char *argv[]) { uintmax_t x; @@ -214,6 +257,7 @@ int main(int argc, char *argv[]) { test_get_timezones(); test_usec_add(); test_usec_sub(); + test_format_timestamp(); /* Ensure time_t is signed */ assert_cc((time_t) -1 < (time_t) 1); |