diff options
Diffstat (limited to 'src')
49 files changed, 1060 insertions, 543 deletions
diff --git a/src/backlight/backlight.c b/src/backlight/backlight.c index 08980fba82..b0fa079fec 100644 --- a/src/backlight/backlight.c +++ b/src/backlight/backlight.c @@ -381,7 +381,7 @@ int main(int argc, char *argv[]) { _cleanup_free_ char *value = NULL; const char *clamp; - if (!shall_restore_state()) + if (shall_restore_state() == 0) return EXIT_SUCCESS; if (!validate_device(udev, device)) diff --git a/src/basic/def.h b/src/basic/def.h index 7c4161eb72..cbef137410 100644 --- a/src/basic/def.h +++ b/src/basic/def.h @@ -35,6 +35,9 @@ * the watchdog pings will keep the loop busy. */ #define DEFAULT_EXIT_USEC (30*USEC_PER_SEC) +/* The default value for the net.unix.max_dgram_qlen sysctl */ +#define DEFAULT_UNIX_MAX_DGRAM_QLEN 512UL + #define SYSTEMD_CGROUP_CONTROLLER "name=systemd" #define SIGNALS_CRASH_HANDLER SIGSEGV,SIGILL,SIGFPE,SIGBUS,SIGQUIT,SIGABRT diff --git a/src/basic/env-util.c b/src/basic/env-util.c index 94cb251698..9ddac5d6a1 100644 --- a/src/basic/env-util.c +++ b/src/basic/env-util.c @@ -25,6 +25,7 @@ #include "alloc-util.h" #include "def.h" #include "env-util.h" +#include "parse-util.h" #include "string-util.h" #include "strv.h" #include "utf8.h" @@ -594,3 +595,13 @@ char **replace_env_argv(char **argv, char **env) { ret[k] = NULL; return ret; } + +int getenv_bool(const char *p) { + const char *e; + + e = getenv(p); + if (!e) + return -ENXIO; + + return parse_boolean(e); +} diff --git a/src/basic/env-util.h b/src/basic/env-util.h index 803aa61cad..6485dade18 100644 --- a/src/basic/env-util.h +++ b/src/basic/env-util.h @@ -47,3 +47,5 @@ char **strv_env_unset_many(char **l, ...) _sentinel_; char *strv_env_get_n(char **l, const char *name, size_t k) _pure_; char *strv_env_get(char **x, const char *n) _pure_; + +int getenv_bool(const char *p); diff --git a/src/basic/fdset.c b/src/basic/fdset.c index 4b11e4ea09..42b0b2b98f 100644 --- a/src/basic/fdset.c +++ b/src/basic/fdset.c @@ -44,7 +44,7 @@ FDSet *fdset_new(void) { return MAKE_FDSET(set_new(NULL)); } -int fdset_new_array(FDSet **ret, int *fds, unsigned n_fds) { +int fdset_new_array(FDSet **ret, const int *fds, unsigned n_fds) { unsigned i; FDSet *s; int r; diff --git a/src/basic/fdset.h b/src/basic/fdset.h index 340438d7c4..70d8acbcff 100644 --- a/src/basic/fdset.h +++ b/src/basic/fdset.h @@ -35,7 +35,7 @@ int fdset_consume(FDSet *s, int fd); bool fdset_contains(FDSet *s, int fd); int fdset_remove(FDSet *s, int fd); -int fdset_new_array(FDSet **ret, int *fds, unsigned n_fds); +int fdset_new_array(FDSet **ret, const int *fds, unsigned n_fds); int fdset_new_fill(FDSet **ret); int fdset_new_listen_fds(FDSet **ret, bool unset); diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c index 1ee5783680..b6358c459a 100644 --- a/src/basic/parse-util.c +++ b/src/basic/parse-util.c @@ -81,6 +81,19 @@ int parse_mode(const char *s, mode_t *ret) { return 0; } +int parse_ifindex(const char *s, int *ret) { + int ifi, r; + + r = safe_atoi(s, &ifi); + if (r < 0) + return r; + if (ifi <= 0) + return -EINVAL; + + *ret = ifi; + return 0; +} + int parse_size(const char *t, uint64_t base, uint64_t *size) { /* Soo, sometimes we want to parse IEC binary suffixes, and diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h index 0e56848e26..408690d0b3 100644 --- a/src/basic/parse-util.h +++ b/src/basic/parse-util.h @@ -31,6 +31,7 @@ int parse_boolean(const char *v) _pure_; int parse_pid(const char *s, pid_t* ret_pid); int parse_mode(const char *s, mode_t *ret); +int parse_ifindex(const char *s, int *ret); int parse_size(const char *t, uint64_t base, uint64_t *size); int parse_range(const char *t, unsigned *lower, unsigned *upper); diff --git a/src/basic/proc-cmdline.c b/src/basic/proc-cmdline.c index f9f98af473..4464573c5b 100644 --- a/src/basic/proc-cmdline.c +++ b/src/basic/proc-cmdline.c @@ -142,7 +142,7 @@ int shall_restore_state(void) { if (r == 0) return true; - return parse_boolean(value) != 0; + return parse_boolean(value); } static const char * const rlmap[] = { diff --git a/src/basic/string-util.c b/src/basic/string-util.c index 63b9b79df9..c3be576816 100644 --- a/src/basic/string-util.c +++ b/src/basic/string-util.c @@ -748,23 +748,38 @@ int free_and_strdup(char **p, const char *s) { return 1; } -void string_erase(char *x) { +#pragma GCC push_options +#pragma GCC optimize("O0") + +void* memory_erase(void *p, size_t l) { + volatile uint8_t* x = (volatile uint8_t*) p; + + /* This basically does what memset() does, but hopefully isn't + * optimized away by the compiler. One of those days, when + * glibc learns memset_s() we should replace this call by + * memset_s(), but until then this has to do. */ + + for (; l > 0; l--) + *(x++) = 'x'; + + return p; +} + +#pragma GCC pop_options + +char* string_erase(char *x) { if (!x) - return; + return NULL; /* A delicious drop of snake-oil! To be called on memory where * we stored passphrases or so, after we used them. */ - memory_erase(x, strlen(x)); + return memory_erase(x, strlen(x)); } char *string_free_erase(char *s) { - if (!s) - return NULL; - - string_erase(s); - return mfree(s); + return mfree(string_erase(s)); } bool string_is_safe(const char *p) { diff --git a/src/basic/string-util.h b/src/basic/string-util.h index 297b8f8232..15244b8184 100644 --- a/src/basic/string-util.h +++ b/src/basic/string-util.h @@ -162,8 +162,8 @@ static inline void *memmem_safe(const void *haystack, size_t haystacklen, const return memmem(haystack, haystacklen, needle, needlelen); } -#define memory_erase(p, l) memset((p), 'x', (l)) -void string_erase(char *x); +void* memory_erase(void *p, size_t l); +char *string_erase(char *x); char *string_free_erase(char *s); DEFINE_TRIVIAL_CLEANUP_FUNC(char *, string_free_erase); diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 04a5a22b72..db4206a523 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -1391,6 +1391,41 @@ int bus_exec_context_set_transient_property( return 1; + } else if (streq(name, "RuntimeDirectory")) { + _cleanup_strv_free_ char **l = NULL; + char **p; + + r = sd_bus_message_read_strv(message, &l); + if (r < 0) + return r; + + STRV_FOREACH(p, l) { + if (!filename_is_valid(*p)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Runtime directory is not valid %s", *p); + } + + if (mode != UNIT_CHECK) { + _cleanup_free_ char *joined = NULL; + + if (strv_isempty(l)) { + c->runtime_directory = strv_free(c->runtime_directory); + unit_write_drop_in_private_format(u, mode, name, "%s=\n", name); + } else { + r = strv_extend_strv(&c->runtime_directory, l, true); + + if (r < 0) + return -ENOMEM; + + joined = strv_join_quoted(c->runtime_directory); + if (!joined) + return -ENOMEM; + + unit_write_drop_in_private_format(u, mode, name, "%s=%s\n", name, joined); + } + } + + return 1; + } else if (rlimit_from_string(name) >= 0) { uint64_t rl; rlim_t x; diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c index c41b3e1723..24f611a593 100644 --- a/src/core/dbus-service.c +++ b/src/core/dbus-service.c @@ -63,7 +63,8 @@ const sd_bus_vtable bus_service_vtable[] = { SD_BUS_PROPERTY("MainPID", "u", bus_property_get_pid, offsetof(Service, main_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("ControlPID", "u", bus_property_get_pid, offsetof(Service, control_pid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("BusName", "s", NULL, offsetof(Service, bus_name), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("FileDescriptorStoreMax", "u", NULL, offsetof(Service, n_fd_store_max), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("FileDescriptorStoreMax", "u", bus_property_get_unsigned, offsetof(Service, n_fd_store_max), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("NFileDescriptorStore", "u", bus_property_get_unsigned, offsetof(Service, n_fd_store), 0), SD_BUS_PROPERTY("StatusText", "s", NULL, offsetof(Service, status_text), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("StatusErrno", "i", NULL, offsetof(Service, status_errno), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Service, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), diff --git a/src/core/dbus.c b/src/core/dbus.c index 834745aeb5..6c44b28adf 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -784,7 +784,7 @@ static int bus_setup_api(Manager *m, sd_bus *bus) { HASHMAP_FOREACH_KEY(u, name, m->watch_bus, i) { r = unit_install_bus_match(u, bus, name); if (r < 0) - log_error_errno(r, "Failed to subscribe to NameOwnerChanged signal: %m"); + log_error_errno(r, "Failed to subscribe to NameOwnerChanged signal for '%s': %m", name); } r = sd_bus_add_match( diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 333fca46c4..a30cd0967d 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -983,10 +983,10 @@ int config_parse_bounding_set(const char *unit, uint64_t *capability_bounding_set_drop = data; uint64_t capability_bounding_set; - const char *word, *state; - size_t l; bool invert = false; uint64_t sum = 0; + const char *prev; + const char *cur; assert(filename); assert(lvalue); @@ -1003,24 +1003,32 @@ int config_parse_bounding_set(const char *unit, * non-inverted everywhere to have a fully normalized * interface. */ - FOREACH_WORD_QUOTED(word, l, rvalue, state) { - _cleanup_free_ char *t = NULL; + prev = cur = rvalue; + for (;;) { + _cleanup_free_ char *word = NULL; int cap; + int r; - t = strndup(word, l); - if (!t) + r = extract_first_word(&cur, &word, NULL, EXTRACT_QUOTES); + if (r == 0) + break; + if (r == -ENOMEM) return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Trailing garbage in bounding set, ignoring: %s", prev); + break; + } - cap = capability_from_name(t); + cap = capability_from_name(word); if (cap < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse capability in bounding set, ignoring: %s", t); + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse capability in bounding set, ignoring: %s", word); + prev = cur; continue; } sum |= ((uint64_t) 1ULL) << (uint64_t) cap; + prev = cur; } - if (!isempty(state)) - log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring."); capability_bounding_set = invert ? ~sum : sum; if (*capability_bounding_set_drop && capability_bounding_set) diff --git a/src/core/main.c b/src/core/main.c index 299ff7cee6..ea4fd0589d 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1099,33 +1099,6 @@ static int bump_rlimit_nofile(struct rlimit *saved_rlimit) { return 0; } -static void test_mtab(void) { - - static const char ok[] = - "/proc/self/mounts\0" - "/proc/mounts\0" - "../proc/self/mounts\0" - "../proc/mounts\0"; - - _cleanup_free_ char *p = NULL; - int r; - - /* Check that /etc/mtab is a symlink to the right place or - * non-existing. But certainly not a file, or a symlink to - * some weird place... */ - - r = readlink_malloc("/etc/mtab", &p); - if (r == -ENOENT) - return; - if (r >= 0 && nulstr_contains(ok, p)) - return; - - log_error("/etc/mtab is not a symlink or not pointing to /proc/self/mounts. " - "This is not supported anymore. " - "Please replace /etc/mtab with a symlink to /proc/self/mounts."); - freeze_or_reboot(); -} - static void test_usr(void) { /* Check that /usr is not a separate fs */ @@ -1228,12 +1201,50 @@ static int status_welcome(void) { static int write_container_id(void) { const char *c; + int r; c = getenv("container"); if (isempty(c)) return 0; - return write_string_file("/run/systemd/container", c, WRITE_STRING_FILE_CREATE); + r = write_string_file("/run/systemd/container", c, WRITE_STRING_FILE_CREATE); + if (r < 0) + return log_warning_errno(r, "Failed to write /run/systed/container, ignoring: %m"); + + return 1; +} + +static int bump_unix_max_dgram_qlen(void) { + _cleanup_free_ char *qlen = NULL; + unsigned long v; + int r; + + /* Let's bump the net.unix.max_dgram_qlen sysctl. The kernel + * default of 16 is simply too low. We set the value really + * really early during boot, so that it is actually applied to + * all our sockets, including the $NOTIFY_SOCKET one. */ + + r = read_one_line_file("/proc/sys/net/unix/max_dgram_qlen", &qlen); + if (r < 0) + return log_warning_errno(r, "Failed to read AF_UNIX datagram queue length, ignoring: %m"); + + r = safe_atolu(qlen, &v); + if (r < 0) + return log_warning_errno(r, "Failed to parse AF_UNIX datagram queue length, ignoring: %m"); + + if (v >= DEFAULT_UNIX_MAX_DGRAM_QLEN) + return 0; + + qlen = mfree(qlen); + if (asprintf(&qlen, "%lu\n", DEFAULT_UNIX_MAX_DGRAM_QLEN) < 0) + return log_oom(); + + r = write_string_file("/proc/sys/net/unix/max_dgram_qlen", qlen, 0); + if (r < 0) + return log_full_errno(IN_SET(r, -EROFS, -EPERM, -EACCES) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to bump AF_UNIX datagram queue length, ignoring: %m"); + + return 1; } int main(int argc, char *argv[]) { @@ -1599,8 +1610,8 @@ int main(int argc, char *argv[]) { hostname_setup(); machine_id_setup(NULL); loopback_setup(); + bump_unix_max_dgram_qlen(); - test_mtab(); test_usr(); } diff --git a/src/core/manager.c b/src/core/manager.c index d161e6c57b..b13663e702 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -86,6 +86,8 @@ #include "virt.h" #include "watchdog.h" +#define NOTIFY_RCVBUF_SIZE (8*1024*1024) + /* Initial delay and the interval for printing status messages about running jobs */ #define JOBS_IN_PROGRESS_WAIT_USEC (5*USEC_PER_SEC) #define JOBS_IN_PROGRESS_PERIOD_USEC (USEC_PER_SEC / 3) @@ -689,6 +691,8 @@ static int manager_setup_notify(Manager *m) { if (fd < 0) return log_error_errno(errno, "Failed to allocate notification socket: %m"); + fd_inc_rcvbuf(fd, NOTIFY_RCVBUF_SIZE); + if (m->running_as == MANAGER_SYSTEM) m->notify_socket = strdup("/run/systemd/notify"); else { @@ -1488,7 +1492,7 @@ static unsigned manager_dispatch_dbus_queue(Manager *m) { return n; } -static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, char *buf, size_t n, FDSet *fds) { +static void manager_invoke_notify_message(Manager *m, Unit *u, pid_t pid, const char *buf, size_t n, FDSet *fds) { _cleanup_strv_free_ char **tags = NULL; assert(m); @@ -1618,7 +1622,7 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t return 0; } -static void invoke_sigchld_event(Manager *m, Unit *u, siginfo_t *si) { +static void invoke_sigchld_event(Manager *m, Unit *u, const siginfo_t *si) { assert(m); assert(u); assert(si); @@ -2000,8 +2004,7 @@ int manager_loop(Manager *m) { m->exit_code = MANAGER_OK; /* Release the path cache */ - set_free_free(m->unit_path_cache); - m->unit_path_cache = NULL; + m->unit_path_cache = set_free_free(m->unit_path_cache); manager_check_finished(m); @@ -2111,6 +2114,9 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { const char *msg; int audit_fd, r; + if (m->running_as != MANAGER_SYSTEM) + return; + audit_fd = get_audit_fd(); if (audit_fd < 0) return; @@ -2120,9 +2126,6 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) { if (m->n_reloading > 0) return; - if (m->running_as != MANAGER_SYSTEM) - return; - if (u->type != UNIT_SERVICE) return; @@ -2771,8 +2774,7 @@ static int create_generator_dir(Manager *m, char **generator, const char *name) return log_oom(); if (!mkdtemp(p)) { - log_error_errno(errno, "Failed to create generator directory %s: %m", - p); + log_error_errno(errno, "Failed to create generator directory %s: %m", p); free(p); return -errno; } diff --git a/src/core/service.c b/src/core/service.c index bafb532e1e..586eddd99a 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -420,7 +420,7 @@ static int service_add_fd_store_set(Service *s, FDSet *fds, const char *name) { } if (fdset_size(fds) > 0) - log_unit_warning(UNIT(s), "Tried to store more fds than FDStoreMax=%u allows, closing remaining.", s->n_fd_store_max); + log_unit_warning(UNIT(s), "Tried to store more fds than FileDescriptorStoreMax=%u allows, closing remaining.", s->n_fd_store_max); return 0; } diff --git a/src/core/unit.c b/src/core/unit.c index f5ec630de2..6c130d4cd1 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -2511,7 +2511,7 @@ int unit_watch_bus_name(Unit *u, const char *name) { * Otherwise, just put the name in the list. bus_setup_api() will take care later. */ r = unit_install_bus_match(u, u->manager->api_bus, name); if (r < 0) - return log_warning_errno(r, "Failed to subscribe to NameOwnerChanged signal: %m"); + return log_warning_errno(r, "Failed to subscribe to NameOwnerChanged signal for '%s': %m", name); } r = hashmap_put(u->manager->watch_bus, name, u); diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c index 20be542096..dc69bb8679 100644 --- a/src/journal-remote/journal-remote.c +++ b/src/journal-remote/journal-remote.c @@ -1256,7 +1256,6 @@ static int parse_argv(int argc, char *argv[]) { }; int c, r; - const char *p; bool type_a, type_b; assert(argc >= 0); @@ -1417,7 +1416,7 @@ static int parse_argv(int argc, char *argv[]) { case ARG_GNUTLS_LOG: { #ifdef HAVE_GNUTLS - p = optarg; + const char* p = optarg; for (;;) { _cleanup_free_ char *word = NULL; diff --git a/src/journal-remote/log-generator.py b/src/journal-remote/log-generator.py index 9a8fb07c7f..fd6964e758 100755 --- a/src/journal-remote/log-generator.py +++ b/src/journal-remote/log-generator.py @@ -6,6 +6,8 @@ import argparse PARSER = argparse.ArgumentParser() PARSER.add_argument('n', type=int) PARSER.add_argument('--dots', action='store_true') +PARSER.add_argument('--data-size', type=int, default=4000) +PARSER.add_argument('--data-type', choices={'random', 'simple'}) OPTIONS = PARSER.parse_args() template = """\ @@ -38,10 +40,16 @@ facility = 6 src = open('/dev/urandom', 'rb') bytes = 0 +counter = 0 for i in range(OPTIONS.n): message = repr(src.read(2000)) - data = repr(src.read(4000)) + if OPTIONS.data_type == 'random': + data = repr(src.read(OPTIONS.data_size)) + else: + # keep the pattern non-repeating so we get a different blob every time + data = '{:0{}}'.format(counter, OPTIONS.data_size) + counter += 1 entry = template.format(m=m, realtime_ts=realtime_ts, diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index 12b39dbb27..e00a0d711d 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -2705,7 +2705,7 @@ int journal_file_open( } if (f->last_stat.st_size < (off_t) HEADER_SIZE_MIN) { - r = -EIO; + r = -ENODATA; goto fail; } diff --git a/src/journal/journal-internal.h b/src/journal/journal-internal.h index 1221799c1d..06847402e0 100644 --- a/src/journal/journal-internal.h +++ b/src/journal/journal-internal.h @@ -121,7 +121,7 @@ struct sd_journal { Hashmap *directories_by_path; Hashmap *directories_by_wd; - Set *errors; + Hashmap *errors; }; char *journal_make_match_string(sd_journal *j); diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index a35783e3ff..98a852cb50 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -1708,36 +1708,50 @@ static int access_check_var_log_journal(sd_journal *j) { static int access_check(sd_journal *j) { Iterator it; void *code; + char *path; int r = 0; assert(j); - if (set_isempty(j->errors)) { + if (hashmap_isempty(j->errors)) { if (ordered_hashmap_isempty(j->files)) log_notice("No journal files were found."); return 0; } - if (set_contains(j->errors, INT_TO_PTR(-EACCES))) { + if (hashmap_contains(j->errors, INT_TO_PTR(-EACCES))) { (void) access_check_var_log_journal(j); if (ordered_hashmap_isempty(j->files)) r = log_error_errno(EACCES, "No journal files were opened due to insufficient permissions."); } - SET_FOREACH(code, j->errors, it) { + HASHMAP_FOREACH_KEY(path, code, j->errors, it) { int err; - err = -PTR_TO_INT(code); - assert(err > 0); + err = abs(PTR_TO_INT(code)); - if (err == EACCES) + switch (err) { + case EACCES: continue; - log_warning_errno(err, "Error was encountered while opening journal files: %m"); - if (r == 0) - r = -err; + case ENODATA: + log_warning_errno(err, "Journal file %s is truncated, ignoring file.", path); + break; + + case EPROTONOSUPPORT: + log_warning_errno(err, "Journal file %s uses an unsupported feature, ignoring file.", path); + break; + + case EBADMSG: + log_warning_errno(err, "Journal file %s corrupted, ignoring file.", path); + break; + + default: + log_warning_errno(err, "An error was encountered while opening journal file %s, ignoring file.", path); + break; + } } return r; diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index ecf7b7a476..299b0a848f 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -78,6 +78,8 @@ #define RECHECK_SPACE_USEC (30*USEC_PER_SEC) +#define NOTIFY_SNDBUF_SIZE (8*1024*1024) + static int determine_space_for( Server *s, JournalMetrics *metrics, @@ -1457,6 +1459,126 @@ static int server_open_hostname(Server *s) { return 0; } +static int dispatch_notify_event(sd_event_source *es, int fd, uint32_t revents, void *userdata) { + Server *s = userdata; + int r; + + assert(s); + assert(s->notify_event_source == es); + assert(s->notify_fd == fd); + + if (revents != EPOLLOUT) { + log_error("Invalid events on notify file descriptor."); + return -EINVAL; + } + + /* The $NOTIFY_SOCKET is writable again, now send exactly one + * message on it. Either it's the initial READY=1 event or an + * stdout stream event. If there's nothing to write anymore, + * turn our event source off. The next time there's something + * to send it will be turned on again. */ + + if (!s->sent_notify_ready) { + static const char p[] = + "READY=1\n" + "STATUS=Processing requests..."; + ssize_t l; + + l = send(s->notify_fd, p, strlen(p), MSG_DONTWAIT); + if (l < 0) { + if (errno == EAGAIN) + return 0; + + return log_error_errno(errno, "Failed to send READY=1 notification message: %m"); + } + + s->sent_notify_ready = true; + log_debug("Sent READY=1 notification."); + + } else if (s->stdout_streams_notify_queue) + /* Dispatch one stream notification event */ + stdout_stream_send_notify(s->stdout_streams_notify_queue); + + /* Leave us enabled if there's still more to to do. */ + if (s->stdout_streams_notify_queue) + return 0; + + /* There was nothing to do anymore, let's turn ourselves off. */ + r = sd_event_source_set_enabled(es, SD_EVENT_OFF); + if (r < 0) + return log_error_errno(r, "Failed to turn off notify event source: %m"); + + return 0; +} + +static int server_connect_notify(Server *s) { + union sockaddr_union sa = { + .un.sun_family = AF_UNIX, + }; + const char *e; + int r; + + assert(s); + assert(s->notify_fd < 0); + assert(!s->notify_event_source); + + /* + So here's the problem: we'd like to send notification + messages to PID 1, but we cannot do that via sd_notify(), + since that's synchronous, and we might end up blocking on + it. Specifically: given that PID 1 might block on + dbus-daemon during IPC, and dbus-daemon is logging to us, + and might hence block on us, we might end up in a deadlock + if we block on sending PID 1 notification messages -- by + generating a full blocking circle. To avoid this, let's + create a non-blocking socket, and connect it to the + notification socket, and then wait for POLLOUT before we + send anything. This should efficiently avoid any deadlocks, + as we'll never block on PID 1, hence PID 1 can safely block + on dbus-daemon which can safely block on us again. + + Don't think that this issue is real? It is, see: + https://github.com/systemd/systemd/issues/1505 + */ + + e = getenv("NOTIFY_SOCKET"); + if (!e) + return 0; + + if ((e[0] != '@' && e[0] != '/') || e[1] == 0) { + log_error("NOTIFY_SOCKET set to an invalid value: %s", e); + return -EINVAL; + } + + if (strlen(e) > sizeof(sa.un.sun_path)) { + log_error("NOTIFY_SOCKET path too long: %s", e); + return -EINVAL; + } + + s->notify_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); + if (s->notify_fd < 0) + return log_error_errno(errno, "Failed to create notify socket: %m"); + + (void) fd_inc_sndbuf(s->notify_fd, NOTIFY_SNDBUF_SIZE); + + strncpy(sa.un.sun_path, e, sizeof(sa.un.sun_path)); + if (sa.un.sun_path[0] == '@') + sa.un.sun_path[0] = 0; + + r = connect(s->notify_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(e)); + if (r < 0) + return log_error_errno(errno, "Failed to connect to notify socket: %m"); + + r = sd_event_add_io(s->event, &s->notify_event_source, s->notify_fd, EPOLLOUT, dispatch_notify_event, s); + if (r < 0) + return log_error_errno(r, "Failed to watch notification socket: %m"); + + /* This should fire pretty soon, which we'll use to send the + * READY=1 event. */ + + return 0; +} + int server_init(Server *s) { _cleanup_fdset_free_ FDSet *fds = NULL; int n, r, fd; @@ -1465,7 +1587,7 @@ int server_init(Server *s) { assert(s); zero(*s); - s->syslog_fd = s->native_fd = s->stdout_fd = s->dev_kmsg_fd = s->audit_fd = s->hostname_fd = -1; + s->syslog_fd = s->native_fd = s->stdout_fd = s->dev_kmsg_fd = s->audit_fd = s->hostname_fd = s->notify_fd = -1; s->compress = true; s->seal = true; @@ -1511,8 +1633,6 @@ int server_init(Server *s) { if (r < 0) return log_error_errno(r, "Failed to create event loop: %m"); - sd_event_set_watchdog(s->event, true); - n = sd_listen_fds(true); if (n < 0) return log_error_errno(n, "Failed to read listening file descriptors from environment: %m"); @@ -1637,6 +1757,8 @@ int server_init(Server *s) { server_cache_boot_id(s); server_cache_machine_id(s); + (void) server_connect_notify(s); + return system_journal_open(s, false); } @@ -1685,6 +1807,7 @@ void server_done(Server *s) { sd_event_source_unref(s->sigterm_event_source); sd_event_source_unref(s->sigint_event_source); sd_event_source_unref(s->hostname_event_source); + sd_event_source_unref(s->notify_event_source); sd_event_unref(s->event); safe_close(s->syslog_fd); @@ -1693,6 +1816,7 @@ void server_done(Server *s) { safe_close(s->dev_kmsg_fd); safe_close(s->audit_fd); safe_close(s->hostname_fd); + safe_close(s->notify_fd); if (s->rate_limit) journal_rate_limit_free(s->rate_limit); diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h index a2631c6017..170602ea16 100644 --- a/src/journal/journald-server.h +++ b/src/journal/journald-server.h @@ -26,9 +26,12 @@ #include "sd-event.h" +typedef struct Server Server; + #include "hashmap.h" #include "journal-file.h" #include "journald-rate-limit.h" +#include "journald-stream.h" #include "list.h" typedef enum Storage { @@ -48,15 +51,14 @@ typedef enum SplitMode { _SPLIT_INVALID = -1 } SplitMode; -typedef struct StdoutStream StdoutStream; - -typedef struct Server { +struct Server { int syslog_fd; int native_fd; int stdout_fd; int dev_kmsg_fd; int audit_fd; int hostname_fd; + int notify_fd; sd_event *event; @@ -71,6 +73,7 @@ typedef struct Server { sd_event_source *sigterm_event_source; sd_event_source *sigint_event_source; sd_event_source *hostname_event_source; + sd_event_source *notify_event_source; JournalFile *runtime_journal; JournalFile *system_journal; @@ -111,6 +114,7 @@ typedef struct Server { usec_t oldest_file_usec; LIST_HEAD(StdoutStream, stdout_streams); + LIST_HEAD(StdoutStream, stdout_streams_notify_queue); unsigned n_stdout_streams; char *tty_path; @@ -132,6 +136,7 @@ typedef struct Server { struct udev *udev; + bool sent_notify_ready; bool sync_scheduled; char machine_id_field[sizeof("_MACHINE_ID=") + 32]; @@ -140,7 +145,7 @@ typedef struct Server { /* Cached cgroup root, so that we don't have to query that all the time */ char *cgroup_root; -} Server; +}; #define SERVER_MACHINE_ID(s) ((s)->machine_id_field + strlen("_MACHINE_ID=")) diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c index 5300c61c02..fb6afee171 100644 --- a/src/journal/journald-stream.c +++ b/src/journal/journald-stream.c @@ -79,6 +79,7 @@ struct StdoutStream { bool forward_to_console:1; bool fdstore:1; + bool in_notify_queue:1; char buffer[LINE_MAX+1]; size_t length; @@ -88,6 +89,7 @@ struct StdoutStream { char *state_file; LIST_FIELDS(StdoutStream, stdout_stream); + LIST_FIELDS(StdoutStream, stdout_stream_notify_queue); }; void stdout_stream_free(StdoutStream *s) { @@ -98,6 +100,9 @@ void stdout_stream_free(StdoutStream *s) { assert(s->server->n_stdout_streams > 0); s->server->n_stdout_streams --; LIST_REMOVE(stdout_stream, s->server->stdout_streams, s); + + if (s->in_notify_queue) + LIST_REMOVE(stdout_stream_notify_queue, s->server->stdout_streams_notify_queue, s); } if (s->event_source) { @@ -121,7 +126,7 @@ static void stdout_stream_destroy(StdoutStream *s) { return; if (s->state_file) - unlink(s->state_file); + (void) unlink(s->state_file); stdout_stream_free(s); } @@ -200,11 +205,15 @@ static int stdout_stream_save(StdoutStream *s) { goto fail; } - /* Store the connection fd in PID 1, so that we get it passed - * in again on next start */ - if (!s->fdstore) { - sd_pid_notify_with_fds(0, false, "FDSTORE=1", &s->fd, 1); - s->fdstore = true; + if (!s->fdstore && !s->in_notify_queue) { + LIST_PREPEND(stdout_stream_notify_queue, s->server->stdout_streams_notify_queue, s); + s->in_notify_queue = true; + + if (s->server->notify_event_source) { + r = sd_event_source_set_enabled(s->server->notify_event_source, SD_EVENT_ON); + if (r < 0) + log_warning_errno(r, "Failed to enable notify event source: %m"); + } } return 0; @@ -729,3 +738,50 @@ int server_open_stdout_socket(Server *s) { return 0; } + +void stdout_stream_send_notify(StdoutStream *s) { + struct iovec iovec = { + .iov_base = (char*) "FDSTORE=1", + .iov_len = strlen("FDSTORE=1"), + }; + struct msghdr msghdr = { + .msg_iov = &iovec, + .msg_iovlen = 1, + }; + struct cmsghdr *cmsg; + ssize_t l; + + assert(s); + assert(!s->fdstore); + assert(s->in_notify_queue); + assert(s->server); + assert(s->server->notify_fd >= 0); + + /* Store the connection fd in PID 1, so that we get it passed + * in again on next start */ + + msghdr.msg_controllen = CMSG_SPACE(sizeof(int)); + msghdr.msg_control = alloca0(msghdr.msg_controllen); + + cmsg = CMSG_FIRSTHDR(&msghdr); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + + memcpy(CMSG_DATA(cmsg), &s->fd, sizeof(int)); + + l = sendmsg(s->server->notify_fd, &msghdr, MSG_DONTWAIT|MSG_NOSIGNAL); + if (l < 0) { + if (errno == EAGAIN) + return; + + log_error_errno(errno, "Failed to send stream file descriptor to service manager: %m"); + } else { + log_debug("Successfully sent stream file descriptor to service manager."); + s->fdstore = 1; + } + + LIST_REMOVE(stdout_stream_notify_queue, s->server->stdout_streams_notify_queue, s); + s->in_notify_queue = false; + +} diff --git a/src/journal/journald-stream.h b/src/journal/journald-stream.h index 257dce45df..e3497f0ded 100644 --- a/src/journal/journald-stream.h +++ b/src/journal/journald-stream.h @@ -21,9 +21,13 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +typedef struct StdoutStream StdoutStream; + #include "fdset.h" #include "journald-server.h" int server_open_stdout_socket(Server *s); int server_restore_streams(Server *s, FDSet *fds); + void stdout_stream_free(StdoutStream *s); +void stdout_stream_send_notify(StdoutStream *s); diff --git a/src/journal/journald.c b/src/journal/journald.c index 83236ceba9..b137e3c7be 100644 --- a/src/journal/journald.c +++ b/src/journal/journald.c @@ -61,10 +61,6 @@ int main(int argc, char *argv[]) { log_debug("systemd-journald running as pid "PID_FMT, getpid()); server_driver_message(&server, SD_MESSAGE_JOURNAL_START, "Journal started"); - sd_notify(false, - "READY=1\n" - "STATUS=Processing requests..."); - for (;;) { usec_t t = USEC_INFINITY, n; @@ -117,10 +113,6 @@ int main(int argc, char *argv[]) { server_driver_message(&server, SD_MESSAGE_JOURNAL_STOP, "Journal stopped"); finish: - sd_notify(false, - "STOPPING=1\n" - "STATUS=Shutting down..."); - server_done(&server); return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index 043087485b..5cde7f17f7 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -73,19 +73,46 @@ static bool journal_pid_changed(sd_journal *j) { return j->original_pid != getpid(); } -/* We return an error here only if we didn't manage to - memorize the real error. */ -static int set_put_error(sd_journal *j, int r) { +static int journal_put_error(sd_journal *j, int r, const char *path) { + char *copy; int k; + /* Memorize an error we encountered, and store which + * file/directory it was generated from. Note that we store + * only *one* path per error code, as the error code is the + * key into the hashmap, and the path is the value. This means + * we keep track only of all error kinds, but not of all error + * locations. This has the benefit that the hashmap cannot + * grow beyond bounds. + * + * We return an error here only if we didn't manage to + * memorize the real error. */ + if (r >= 0) return r; - k = set_ensure_allocated(&j->errors, NULL); + k = hashmap_ensure_allocated(&j->errors, NULL); if (k < 0) return k; - return set_put(j->errors, INT_TO_PTR(r)); + if (path) { + copy = strdup(path); + if (!copy) + return -ENOMEM; + } else + copy = NULL; + + k = hashmap_put(j->errors, INT_TO_PTR(r), copy); + if (k < 0) { + free(copy); + + if (k == -EEXIST) + return 0; + + return k; + } + + return 0; } static void detach_location(sd_journal *j) { @@ -1025,8 +1052,6 @@ _public_ int sd_journal_seek_cursor(sd_journal *j, const char *cursor) { _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) { int r; - const char *word, *state; - size_t l; Object *o; assert_return(j, -EINVAL); @@ -1040,20 +1065,23 @@ _public_ int sd_journal_test_cursor(sd_journal *j, const char *cursor) { if (r < 0) return r; - FOREACH_WORD_SEPARATOR(word, l, cursor, ";", state) { + for(;;) { _cleanup_free_ char *item = NULL; - sd_id128_t id; unsigned long long ll; + sd_id128_t id; int k = 0; - if (l < 2 || word[1] != '=') - return -EINVAL; + r = extract_first_word(&cursor, &item, ";", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r < 0) + return r; - item = strndup(word, l); - if (!item) - return -ENOMEM; + if (r == 0) + break; - switch (word[0]) { + if (strlen(item) < 2 || item[1] != '=') + return -EINVAL; + + switch (item[0]) { case 's': k = sd_id128_from_string(item+2, &id); @@ -1182,6 +1210,8 @@ static bool file_has_type_prefix(const char *prefix, const char *filename) { } static bool file_type_wanted(int flags, const char *filename) { + assert(filename); + if (!endswith(filename, ".journal") && !endswith(filename, ".journal~")) return false; @@ -1206,7 +1236,7 @@ static bool file_type_wanted(int flags, const char *filename) { static int add_any_file(sd_journal *j, const char *path) { JournalFile *f = NULL; - int r; + int r, k; assert(j); assert(path); @@ -1215,20 +1245,23 @@ static int add_any_file(sd_journal *j, const char *path) { return 0; if (ordered_hashmap_size(j->files) >= JOURNAL_FILES_MAX) { - log_warning("Too many open journal files, not adding %s.", path); - return set_put_error(j, -ETOOMANYREFS); + log_debug("Too many open journal files, not adding %s.", path); + r = -ETOOMANYREFS; + goto fail; } r = journal_file_open(path, O_RDONLY, 0, false, false, NULL, j->mmap, NULL, &f); - if (r < 0) - return r; + if (r < 0) { + log_debug_errno(r, "Failed to open journal file %s: %m", path); + goto fail; + } /* journal_file_dump(f); */ r = ordered_hashmap_put(j->files, f->path, f); if (r < 0) { journal_file_close(f); - return r; + goto fail; } log_debug("File %s added.", f->path); @@ -1238,11 +1271,17 @@ static int add_any_file(sd_journal *j, const char *path) { j->current_invalidate_counter ++; return 0; + +fail: + k = journal_put_error(j, r, path); + if (k < 0) + return k; + + return r; } static int add_file(sd_journal *j, const char *prefix, const char *filename) { - _cleanup_free_ char *path = NULL; - int r; + const char *path; assert(j); assert(prefix); @@ -1252,34 +1291,24 @@ static int add_file(sd_journal *j, const char *prefix, const char *filename) { !file_type_wanted(j->flags, filename)) return 0; - path = strjoin(prefix, "/", filename, NULL); - if (!path) - return -ENOMEM; - - r = add_any_file(j, path); - if (r == -ENOENT) - return 0; - return r; + path = strjoina(prefix, "/", filename); + return add_any_file(j, path); } -static int remove_file(sd_journal *j, const char *prefix, const char *filename) { - _cleanup_free_ char *path; +static void remove_file(sd_journal *j, const char *prefix, const char *filename) { + const char *path; JournalFile *f; assert(j); assert(prefix); assert(filename); - path = strjoin(prefix, "/", filename, NULL); - if (!path) - return -ENOMEM; - + path = strjoina(prefix, "/", filename); f = ordered_hashmap_get(j->files, path); if (!f) - return 0; + return; remove_file_real(j, f); - return 0; } static void remove_file_real(sd_journal *j, JournalFile *f) { @@ -1308,12 +1337,27 @@ static void remove_file_real(sd_journal *j, JournalFile *f) { j->current_invalidate_counter ++; } +static int dirname_is_machine_id(const char *fn) { + sd_id128_t id, machine; + int r; + + r = sd_id128_get_machine(&machine); + if (r < 0) + return r; + + r = sd_id128_from_string(fn, &id); + if (r < 0) + return r; + + return sd_id128_equal(id, machine); +} + static int add_directory(sd_journal *j, const char *prefix, const char *dirname) { _cleanup_free_ char *path = NULL; - int r; _cleanup_closedir_ DIR *d = NULL; - sd_id128_t id, mid; + struct dirent *de = NULL; Directory *m; + int r, k; assert(j); assert(prefix); @@ -1322,35 +1366,36 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname) log_debug("Considering %s/%s.", prefix, dirname); if ((j->flags & SD_JOURNAL_LOCAL_ONLY) && - (sd_id128_from_string(dirname, &id) < 0 || - sd_id128_get_machine(&mid) < 0 || - !(sd_id128_equal(id, mid) || path_startswith(prefix, "/run")))) + !(dirname_is_machine_id(dirname) > 0 || path_startswith(prefix, "/run"))) return 0; path = strjoin(prefix, "/", dirname, NULL); - if (!path) - return -ENOMEM; + if (!path) { + r = -ENOMEM; + goto fail; + } d = opendir(path); if (!d) { - log_debug_errno(errno, "Failed to open %s: %m", path); - if (errno == ENOENT) - return 0; - return -errno; + r = log_debug_errno(errno, "Failed to open directory %s: %m", path); + goto fail; } m = hashmap_get(j->directories_by_path, path); if (!m) { m = new0(Directory, 1); - if (!m) - return -ENOMEM; + if (!m) { + r = -ENOMEM; + goto fail; + } m->is_root = false; m->path = path; if (hashmap_put(j->directories_by_path, m->path, m) < 0) { free(m); - return -ENOMEM; + r = -ENOMEM; + goto fail; } path = NULL; /* avoid freeing in cleanup */ @@ -1372,41 +1417,30 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname) inotify_rm_watch(j->inotify_fd, m->wd); } - for (;;) { - struct dirent *de; - - errno = 0; - de = readdir(d); - if (!de && errno != 0) { - r = -errno; - log_debug_errno(errno, "Failed to read directory %s: %m", m->path); - return r; - } - if (!de) - break; + FOREACH_DIRENT_ALL(de, d, return log_debug_errno(errno, "Failed to read directory %s: %m", m->path)) { if (dirent_is_file_with_suffix(de, ".journal") || - dirent_is_file_with_suffix(de, ".journal~")) { - r = add_file(j, m->path, de->d_name); - if (r < 0) { - log_debug_errno(r, "Failed to add file %s/%s: %m", - m->path, de->d_name); - r = set_put_error(j, r); - if (r < 0) - return r; - } - } + dirent_is_file_with_suffix(de, ".journal~")) + (void) add_file(j, m->path, de->d_name); } check_network(j, dirfd(d)); return 0; + +fail: + k = journal_put_error(j, r, path ?: dirname); + if (k < 0) + return k; + + return r; } -static int add_root_directory(sd_journal *j, const char *p) { +static int add_root_directory(sd_journal *j, const char *p, bool missing_ok) { _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; Directory *m; - int r; + int r, k; assert(j); assert(p); @@ -1419,26 +1453,35 @@ static int add_root_directory(sd_journal *j, const char *p) { p = strjoina(j->prefix, p); d = opendir(p); - if (!d) - return -errno; + if (!d) { + if (errno == ENOENT && missing_ok) + return 0; + + r = log_debug_errno(errno, "Failed to open root directory %s: %m", p); + goto fail; + } m = hashmap_get(j->directories_by_path, p); if (!m) { m = new0(Directory, 1); - if (!m) - return -ENOMEM; + if (!m) { + r = -ENOMEM; + goto fail; + } m->is_root = true; m->path = strdup(p); if (!m->path) { free(m); - return -ENOMEM; + r = -ENOMEM; + goto fail; } if (hashmap_put(j->directories_by_path, m->path, m) < 0) { free(m->path); free(m); - return -ENOMEM; + r = -ENOMEM; + goto fail; } j->current_invalidate_counter ++; @@ -1461,42 +1504,27 @@ static int add_root_directory(sd_journal *j, const char *p) { if (j->no_new_files) return 0; - for (;;) { - struct dirent *de; + FOREACH_DIRENT_ALL(de, d, return log_debug_errno(errno, "Failed to read directory %s: %m", m->path)) { sd_id128_t id; - errno = 0; - de = readdir(d); - if (!de && errno != 0) { - r = -errno; - log_debug_errno(errno, "Failed to read directory %s: %m", m->path); - return r; - } - if (!de) - break; - if (dirent_is_file_with_suffix(de, ".journal") || - dirent_is_file_with_suffix(de, ".journal~")) { - r = add_file(j, m->path, de->d_name); - if (r < 0) { - log_debug_errno(r, "Failed to add file %s/%s: %m", - m->path, de->d_name); - r = set_put_error(j, r); - if (r < 0) - return r; - } - } else if ((de->d_type == DT_DIR || de->d_type == DT_LNK || de->d_type == DT_UNKNOWN) && - sd_id128_from_string(de->d_name, &id) >= 0) { - - r = add_directory(j, m->path, de->d_name); - if (r < 0) - log_debug_errno(r, "Failed to add directory %s/%s: %m", m->path, de->d_name); - } + dirent_is_file_with_suffix(de, ".journal~")) + (void) add_file(j, m->path, de->d_name); + else if (IN_SET(de->d_type, DT_DIR, DT_LNK, DT_UNKNOWN) && + sd_id128_from_string(de->d_name, &id) >= 0) + (void) add_directory(j, m->path, de->d_name); } check_network(j, dirfd(d)); return 0; + +fail: + k = journal_put_error(j, r, p); + if (k < 0) + return k; + + return r; } static void remove_directory(sd_journal *j, Directory *d) { @@ -1521,8 +1549,8 @@ static void remove_directory(sd_journal *j, Directory *d) { } static int add_search_paths(sd_journal *j) { - int r; - const char search_paths[] = + + static const char search_paths[] = "/run/log/journal\0" "/var/log/journal\0"; const char *p; @@ -1532,14 +1560,8 @@ static int add_search_paths(sd_journal *j) { /* We ignore most errors here, since the idea is to only open * what's actually accessible, and ignore the rest. */ - NULSTR_FOREACH(p, search_paths) { - r = add_root_directory(j, p); - if (r < 0 && r != -ENOENT) { - r = set_put_error(j, r); - if (r < 0) - return r; - } - } + NULSTR_FOREACH(p, search_paths) + (void) add_root_directory(j, p, true); return 0; } @@ -1563,17 +1585,14 @@ static int add_current_paths(sd_journal *j) { if (!dir) return -ENOMEM; - r = add_root_directory(j, dir); - if (r < 0) { - set_put_error(j, r); + r = add_root_directory(j, dir, true); + if (r < 0) return r; - } } return 0; } - static int allocate_inotify(sd_journal *j) { assert(j); @@ -1701,11 +1720,9 @@ _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int f if (!j) return -ENOMEM; - r = add_root_directory(j, path); - if (r < 0) { - set_put_error(j, r); + r = add_root_directory(j, path, false); + if (r < 0) goto fail; - } *ret = j; return 0; @@ -1730,10 +1747,8 @@ _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int fla STRV_FOREACH(path, paths) { r = add_any_file(j, *path); - if (r < 0) { - log_error_errno(r, "Failed to open %s: %m", *path); + if (r < 0) goto fail; - } } j->no_new_files = true; @@ -1750,6 +1765,7 @@ fail: _public_ void sd_journal_close(sd_journal *j) { Directory *d; JournalFile *f; + char *p; if (!j) return; @@ -1777,10 +1793,13 @@ _public_ void sd_journal_close(sd_journal *j) { mmap_cache_unref(j->mmap); } + while ((p = hashmap_steal_first(j->errors))) + free(p); + hashmap_free(j->errors); + free(j->path); free(j->prefix); free(j->unique_field); - set_free(j->errors); free(j); } @@ -2073,7 +2092,7 @@ _public_ int sd_journal_get_fd(sd_journal *j) { if (j->no_new_files) r = add_current_paths(j); else if (j->path) - r = add_root_directory(j, j->path); + r = add_root_directory(j, j->path, true); else r = add_search_paths(j); if (r < 0) @@ -2120,7 +2139,6 @@ _public_ int sd_journal_get_timeout(sd_journal *j, uint64_t *timeout_usec) { static void process_inotify_event(sd_journal *j, struct inotify_event *e) { Directory *d; - int r; assert(j); assert(e); @@ -2136,20 +2154,10 @@ static void process_inotify_event(sd_journal *j, struct inotify_event *e) { /* Event for a journal file */ - if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) { - r = add_file(j, d->path, e->name); - if (r < 0) { - log_debug_errno(r, "Failed to add file %s/%s: %m", - d->path, e->name); - set_put_error(j, r); - } - - } else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) { - - r = remove_file(j, d->path, e->name); - if (r < 0) - log_debug_errno(r, "Failed to remove file %s/%s: %m", d->path, e->name); - } + if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) + (void) add_file(j, d->path, e->name); + else if (e->mask & (IN_DELETE|IN_MOVED_FROM|IN_UNMOUNT)) + remove_file(j, d->path, e->name); } else if (!d->is_root && e->len == 0) { @@ -2162,11 +2170,8 @@ static void process_inotify_event(sd_journal *j, struct inotify_event *e) { /* Event for root directory */ - if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) { - r = add_directory(j, d->path, e->name); - if (r < 0) - log_debug_errno(r, "Failed to add directory %s/%s: %m", d->path, e->name); - } + if (e->mask & (IN_CREATE|IN_MOVED_TO|IN_MODIFY|IN_ATTRIB)) + (void) add_directory(j, d->path, e->name); } return; @@ -2175,7 +2180,7 @@ static void process_inotify_event(sd_journal *j, struct inotify_event *e) { if (e->mask & IN_IGNORED) return; - log_warning("Unknown inotify event."); + log_debug("Unknown inotify event."); } static int determine_change(sd_journal *j) { diff --git a/src/libsystemd/sd-daemon/sd-daemon.c b/src/libsystemd/sd-daemon/sd-daemon.c index 77b5dd52f6..27045e25d0 100644 --- a/src/libsystemd/sd-daemon/sd-daemon.c +++ b/src/libsystemd/sd-daemon/sd-daemon.c @@ -44,6 +44,8 @@ #include "strv.h" #include "util.h" +#define SNDBUF_SIZE (8*1024*1024) + static void unsetenv_all(bool unset_environment) { if (!unset_environment) @@ -434,12 +436,19 @@ _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char goto finish; } + if (strlen(e) > sizeof(sockaddr.un.sun_path)) { + r = -EINVAL; + goto finish; + } + fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0); if (fd < 0) { r = -errno; goto finish; } + fd_inc_sndbuf(fd, SNDBUF_SIZE); + iovec.iov_len = strlen(state); strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path)); diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c index 0d40bc5c00..0e49262087 100644 --- a/src/libsystemd/sd-device/sd-device.c +++ b/src/libsystemd/sd-device/sd-device.c @@ -356,13 +356,10 @@ int device_set_ifindex(sd_device *device, const char *_ifindex) { assert(device); assert(_ifindex); - r = safe_atoi(_ifindex, &ifindex); + r = parse_ifindex(_ifindex, &ifindex); if (r < 0) return r; - if (ifindex <= 0) - return -EINVAL; - r = device_add_property_internal(device, "IFINDEX", _ifindex); if (r < 0) return r; @@ -632,11 +629,9 @@ _public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) { struct ifreq ifr = {}; int ifindex; - r = safe_atoi(&id[1], &ifr.ifr_ifindex); + r = parse_ifindex(&id[1], &ifr.ifr_ifindex); if (r < 0) return r; - else if (ifr.ifr_ifindex <= 0) - return -EINVAL; sk = socket(PF_INET, SOCK_DGRAM, 0); if (sk < 0) diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c index cd766c3f91..3f2e459825 100644 --- a/src/libsystemd/sd-login/sd-login.c +++ b/src/libsystemd/sd-login/sd-login.c @@ -932,9 +932,7 @@ _public_ int sd_machine_get_ifindices(const char *machine, int **ifindices) { *(char*) (mempcpy(buf, word, l)) = 0; - if (safe_atoi(buf, &ifi) < 0) - continue; - if (ifi <= 0) + if (parse_ifindex(buf, &ifi) < 0) continue; if (!GREEDY_REALLOC(ni, allocated, nr+1)) { diff --git a/src/machine/machine.c b/src/machine/machine.c index fa6b70f788..cbc03640c1 100644 --- a/src/machine/machine.c +++ b/src/machine/machine.c @@ -42,6 +42,7 @@ #include "terminal-util.h" #include "unit-name.h" #include "util.h" +#include "extract-word.h" Machine* machine_new(Manager *manager, MachineClass class, const char *name) { Machine *m; @@ -312,19 +313,26 @@ int machine_load(Machine *m) { } if (netif) { - size_t l, allocated = 0, nr = 0; - const char *word, *state; + size_t allocated = 0, nr = 0; + const char *p; int *ni = NULL; - FOREACH_WORD(word, l, netif, state) { - char buf[l+1]; + p = netif; + for(;;) { + _cleanup_free_ char *word = NULL; int ifi; - *(char*) (mempcpy(buf, word, l)) = 0; + r = extract_first_word(&p, &word, NULL, 0); + if (r == 0) + break; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_warning_errno(r, "Failed to parse NETIF: %s", netif); + break; + } - if (safe_atoi(buf, &ifi) < 0) - continue; - if (ifi <= 0) + if (parse_ifindex(word, &ifi) < 0) continue; if (!GREEDY_REALLOC(ni, allocated, nr+1)) { diff --git a/src/network/networkctl.c b/src/network/networkctl.c index 97c1fe6560..ba7e3ba74a 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -521,7 +521,7 @@ static int link_status_one( assert(rtnl); assert(name); - if (safe_atoi(name, &ifindex) >= 0 && ifindex > 0) + if (parse_ifindex(name, &ifindex) >= 0) r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex); else { r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0); diff --git a/src/network/networkd-link-bus.c b/src/network/networkd-link-bus.c index 7fc4510ac9..11b35d6cf8 100644 --- a/src/network/networkd-link-bus.c +++ b/src/network/networkd-link-bus.c @@ -103,7 +103,7 @@ int link_object_find(sd_bus *bus, const char *path, const char *interface, void if (r < 0) return 0; - r = safe_atoi(identifier, &ifindex); + r = parse_ifindex(identifier, &ifindex); if (r < 0) return 0; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index dcc2569660..12ca02868d 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -349,12 +349,12 @@ static void link_free(Link *link) { while (!set_isempty(link->addresses)) address_free(set_first(link->addresses)); - set_free(link->addresses); - while (!set_isempty(link->addresses_foreign)) address_free(set_first(link->addresses_foreign)); - set_free(link->addresses_foreign); + link->addresses = set_free(link->addresses); + + link->addresses_foreign = set_free(link->addresses_foreign); while ((address = link->pool_addresses)) { LIST_REMOVE(addresses, link->pool_addresses, address); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 44f08ab1b4..ff12ca6498 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -3227,8 +3227,7 @@ int main(int argc, char *argv[]) { } for (;;) { - _cleanup_close_pair_ int kmsg_socket_pair[2] = { -1, -1 }, rtnl_socket_pair[2] = { -1, -1 }, pid_socket_pair[2] = { -1, -1 }, - uid_shift_socket_pair[2] = { -1, -1 }; + _cleanup_close_pair_ int kmsg_socket_pair[2] = { -1, -1 }, rtnl_socket_pair[2] = { -1, -1 }, pid_socket_pair[2] = { -1, -1 }, uid_shift_socket_pair[2] = { -1, -1 }; ContainerStatus container_status; _cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL; static const struct sigaction sa = { diff --git a/src/resolve-host/resolve-host.c b/src/resolve-host/resolve-host.c index 57739d1f3e..432e62dd9f 100644 --- a/src/resolve-host/resolve-host.c +++ b/src/resolve-host/resolve-host.c @@ -300,8 +300,7 @@ static int parse_address(const char *s, int *family, union in_addr_union *addres percent = strchr(s, '%'); if (percent) { - r = safe_atoi(percent+1, &ifi); - if (r < 0 || ifi <= 0) { + if (parse_ifindex(percent+1, &ifi) < 0) { ifi = if_nametoindex(percent+1); if (ifi <= 0) return -EINVAL; @@ -521,7 +520,7 @@ static int parse_argv(int argc, char *argv[]) { case 'i': { int ifi; - if (safe_atoi(optarg, &ifi) >= 0 && ifi > 0) + if (parse_ifindex(optarg, &ifi) >= 0) arg_ifindex = ifi; else { ifi = if_nametoindex(optarg); diff --git a/src/rfkill/rfkill.c b/src/rfkill/rfkill.c index bb00f99ecf..5c45a3ae6c 100644 --- a/src/rfkill/rfkill.c +++ b/src/rfkill/rfkill.c @@ -212,7 +212,7 @@ static int load_state( assert(udev); assert(event); - if (!shall_restore_state()) + if (shall_restore_state() == 0) return 0; r = find_device(udev, event, &device); diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 940e393318..a13991a960 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -1450,7 +1450,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies", "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit", "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges", - "SyslogLevelPrefix")) { + "SyslogLevelPrefix", "Delegate")) { r = parse_boolean(eq); if (r < 0) { @@ -1789,6 +1789,40 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen r = sd_bus_message_close_container(m); + } else if (streq(field, "RuntimeDirectory")) { + const char *p; + + r = sd_bus_message_open_container(m, 'v', "as"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "s"); + if (r < 0) + return bus_log_create_error(r); + + p = eq; + + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); + if (r < 0) + return log_error_errno(r, "Failed to parse %s value %s", field, eq); + + if (r == 0) + break; + + r = sd_bus_message_append_basic(m, 's', word); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + } else { log_error("Unknown assignment %s.", assignment); return -EINVAL; diff --git a/src/shared/ptyfwd.c b/src/shared/ptyfwd.c index 786752ea94..63e81f4894 100644 --- a/src/shared/ptyfwd.c +++ b/src/shared/ptyfwd.c @@ -413,6 +413,7 @@ PTYForward *pty_forward_free(PTYForward *f) { sd_event_source_unref(f->stdin_event_source); sd_event_source_unref(f->stdout_event_source); sd_event_source_unref(f->master_event_source); + sd_event_source_unref(f->sigwinch_event_source); sd_event_unref(f->event); if (f->saved_stdout) diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index f5efa1a064..70871cf3e6 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -296,6 +296,10 @@ static bool install_client_side(void) { if (arg_scope == UNIT_FILE_GLOBAL) return true; + /* Unsupported environment variable, mostly for debugging purposes */ + if (getenv_bool("SYSTEMCTL_INSTALL_CLIENT_SIDE") > 0) + return true; + return false; } @@ -5317,6 +5321,9 @@ static int enable_sysv_units(const char *verb, char **args) { if (arg_scope != UNIT_FILE_SYSTEM) return 0; + if (getenv_bool("SYSTEMCTL_SKIP_SYSV") > 0) + return 0; + if (!STR_IN_SET(verb, "enable", "disable", @@ -6624,6 +6631,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { {} }; + const char *p; int c, r; assert(argc >= 0); @@ -6644,15 +6652,19 @@ static int systemctl_parse_argv(int argc, char *argv[]) { return version(); case 't': { - const char *word, *state; - size_t size; + if (isempty(optarg)) + return log_error_errno(r, "--type requires arguments."); - FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) { - _cleanup_free_ char *type; + p = optarg; + for(;;) { + _cleanup_free_ char *type = NULL; - type = strndup(word, size); - if (!type) - return -ENOMEM; + r = extract_first_word(&p, &type, ",", 0); + if (r < 0) + return log_error_errno(r, "Failed to parse type: %s", optarg); + + if (r == 0) + break; if (streq(type, "help")) { help_types(); @@ -6693,18 +6705,21 @@ static int systemctl_parse_argv(int argc, char *argv[]) { if (!arg_properties) return log_oom(); } else { - const char *word, *state; - size_t size; + p = optarg; + for(;;) { + _cleanup_free_ char *prop = NULL; - FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) { - char *prop; + r = extract_first_word(&p, &prop, ",", 0); + if (r < 0) + return log_error_errno(r, "Failed to parse property: %s", optarg); - prop = strndup(word, size); - if (!prop) - return log_oom(); + if (r == 0) + break; - if (strv_consume(&arg_properties, prop) < 0) + if (strv_push(&arg_properties, prop) < 0) return log_oom(); + + prop = NULL; } } @@ -6870,15 +6885,19 @@ static int systemctl_parse_argv(int argc, char *argv[]) { break; case ARG_STATE: { - const char *word, *state; - size_t size; + if (isempty(optarg)) + return log_error_errno(r, "--signal requires arguments."); - FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) { + p = optarg; + for(;;) { _cleanup_free_ char *s = NULL; - s = strndup(word, size); - if (!s) - return log_oom(); + r = extract_first_word(&p, &s, ",", 0); + if (r < 0) + return log_error_errno(r, "Failed to parse signal: %s", optarg); + + if (r == 0) + break; if (streq(s, "help")) { help_states(); diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c index 0a0b9269b3..042be97840 100644 --- a/src/sysv-generator/sysv-generator.c +++ b/src/sysv-generator/sysv-generator.c @@ -26,7 +26,9 @@ #include <unistd.h> #include "alloc-util.h" +#include "dirent-util.h" #include "fd-util.h" +#include "fileio.h" #include "hashmap.h" #include "hexdecoct.h" #include "install.h" @@ -85,9 +87,13 @@ typedef struct SysvStub { char **conflicts; bool has_lsb; bool reload; + bool loaded; } SysvStub; static void free_sysvstub(SysvStub *s) { + if (!s) + return; + free(s->name); free(s->path); free(s->description); @@ -112,19 +118,14 @@ static void free_sysvstub_hashmapp(Hashmap **h) { } static int add_symlink(const char *service, const char *where) { - _cleanup_free_ char *from = NULL, *to = NULL; + const char *from, *to; int r; assert(service); assert(where); - from = strjoin(arg_dest, "/", service, NULL); - if (!from) - return log_oom(); - - to = strjoin(arg_dest, "/", where, ".wants/", service, NULL); - if (!to) - return log_oom(); + from = strjoina(arg_dest, "/", service); + to = strjoina(arg_dest, "/", where, ".wants/", service); mkdir_parents_label(to, 0755); @@ -132,6 +133,7 @@ static int add_symlink(const char *service, const char *where) { if (r < 0) { if (errno == EEXIST) return 0; + return -errno; } @@ -139,20 +141,19 @@ static int add_symlink(const char *service, const char *where) { } static int add_alias(const char *service, const char *alias) { - _cleanup_free_ char *link = NULL; + const char *link; int r; assert(service); assert(alias); - link = strjoin(arg_dest, "/", alias, NULL); - if (!link) - return log_oom(); + link = strjoina(arg_dest, "/", alias); r = symlink(service, link); if (r < 0) { if (errno == EEXIST) return 0; + return -errno; } @@ -160,26 +161,32 @@ static int add_alias(const char *service, const char *alias) { } static int generate_unit_file(SysvStub *s) { - char **p; + _cleanup_free_ char *before = NULL, *after = NULL, *wants = NULL, *conflicts = NULL; _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *unit = NULL, - *before = NULL, *after = NULL, - *wants = NULL, *conflicts = NULL; + const char *unit; + char **p; int r; + assert(s); + + if (!s->loaded) + return 0; + + unit = strjoina(arg_dest, "/", s->name); + before = strv_join(s->before, " "); after = strv_join(s->after, " "); wants = strv_join(s->wants, " "); conflicts = strv_join(s->conflicts, " "); - unit = strjoin(arg_dest, "/", s->name, NULL); - if (!before || !after || !wants || !conflicts || !unit) + + if (!before || !after || !wants || !conflicts) return log_oom(); /* We might already have a symlink with the same name from a Provides:, * or from backup files like /etc/init.d/foo.bak. Real scripts always win, * so remove an existing link */ if (is_symlink(unit) > 0) { - log_warning("Overwriting existing symlink %s with real service", unit); + log_warning("Overwriting existing symlink %s with real service.", unit); (void) unlink(unit); } @@ -191,9 +198,11 @@ static int generate_unit_file(SysvStub *s) { "# Automatically generated by systemd-sysv-generator\n\n" "[Unit]\n" "Documentation=man:systemd-sysv-generator(8)\n" - "SourcePath=%s\n" - "Description=%s\n", - s->path, s->description); + "SourcePath=%s\n", + s->path); + + if (s->description) + fprintf(f, "Description=%s\n", s->description); if (!isempty(before)) fprintf(f, "Before=%s\n", before); @@ -226,13 +235,17 @@ static int generate_unit_file(SysvStub *s) { if (s->reload) fprintf(f, "ExecReload=%s reload\n", s->path); + r = fflush_and_check(f); + if (r < 0) + return log_error_errno(r, "Failed to write unit %s: %m", unit); + STRV_FOREACH(p, s->wanted_by) { r = add_symlink(s->name, *p); if (r < 0) - log_error_errno(r, "Failed to create 'Wants' symlink to %s: %m", *p); + log_warning_errno(r, "Failed to create 'Wants' symlink to %s, ignoring: %m", *p); } - return 0; + return 1; } static bool usage_contains_reload(const char *line) { @@ -262,7 +275,7 @@ static char *sysv_translate_name(const char *name) { return res; } -static int sysv_translate_facility(const char *name, const char *filename, char **_r) { +static int sysv_translate_facility(const char *name, const char *filename, char **ret) { /* We silently ignore the $ prefix here. According to the LSB * spec it simply indicates whether something is a @@ -281,31 +294,45 @@ static int sysv_translate_facility(const char *name, const char *filename, char "time", SPECIAL_TIME_SYNC_TARGET, }; - char *filename_no_sh, *e, *r; + char *filename_no_sh, *e, *m; const char *n; unsigned i; + int r; assert(name); - assert(_r); + assert(filename); + assert(ret); n = *name == '$' ? name + 1 : name; for (i = 0; i < ELEMENTSOF(table); i += 2) { - if (!streq(table[i], n)) continue; if (!table[i+1]) return 0; - r = strdup(table[i+1]); - if (!r) + m = strdup(table[i+1]); + if (!m) return log_oom(); - goto finish; + *ret = m; + return 1; } - /* strip ".sh" suffix from file name for comparison */ + /* If we don't know this name, fallback heuristics to figure + * out whether something is a target or a service alias. */ + + /* Facilities starting with $ are most likely targets */ + if (*name == '$') { + r = unit_name_build(n, NULL, ".target", ret); + if (r < 0) + return log_error_errno(r, "Failed to build name: %m"); + + return r; + } + + /* Strip ".sh" suffix from file name for comparison */ filename_no_sh = strdupa(filename); e = endswith(filename_no_sh, ".sh"); if (e) { @@ -313,103 +340,103 @@ static int sysv_translate_facility(const char *name, const char *filename, char filename = filename_no_sh; } - /* If we don't know this name, fallback heuristics to figure - * out whether something is a target or a service alias. */ - - if (*name == '$') { - int k; - - /* Facilities starting with $ are most likely targets */ - k = unit_name_build(n, NULL, ".target", &r); - if (k < 0) - return k; - - } else if (streq_ptr(n, filename)) - /* Names equaling the file name of the services are redundant */ + /* Names equaling the file name of the services are redundant */ + if (streq_ptr(n, filename)) return 0; - else - /* Everything else we assume to be normal service names */ - r = sysv_translate_name(n); - if (!r) - return -ENOMEM; -finish: - *_r = r; + /* Everything else we assume to be normal service names */ + m = sysv_translate_name(n); + if (!m) + return log_oom(); + *ret = m; return 1; } static int handle_provides(SysvStub *s, unsigned line, const char *full_text, const char *text) { - const char *word, *state_; - size_t z; int r; - FOREACH_WORD_QUOTED(word, z, text, state_) { - _cleanup_free_ char *n = NULL, *m = NULL; - UnitType t; + assert(s); + assert(full_text); + assert(text); - n = strndup(word, z); - if (!n) - return log_oom(); + for (;;) { + _cleanup_free_ char *word = NULL, *m = NULL; - r = sysv_translate_facility(n, basename(s->path), &m); + r = extract_first_word(&text, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX); if (r < 0) - return r; + return log_error_errno(r, "Failed to parse word from provides string: %m"); if (r == 0) + break; + + r = sysv_translate_facility(word, basename(s->path), &m); + if (r <= 0) /* continue on error */ continue; - t = unit_name_to_type(m); - if (t == UNIT_SERVICE) { + switch (unit_name_to_type(m)) { + + case UNIT_SERVICE: log_debug("Adding Provides: alias '%s' for '%s'", m, s->name); r = add_alias(s->name, m); if (r < 0) log_warning_errno(r, "[%s:%u] Failed to add LSB Provides name %s, ignoring: %m", s->path, line, m); - } else if (t == UNIT_TARGET) { + break; + + case UNIT_TARGET: + /* NB: SysV targets which are provided by a * service are pulled in by the services, as * an indication that the generic service is * now available. This is strictly one-way. * The targets do NOT pull in SysV services! */ + r = strv_extend(&s->before, m); if (r < 0) return log_oom(); + r = strv_extend(&s->wants, m); if (r < 0) return log_oom(); + if (streq(m, SPECIAL_NETWORK_ONLINE_TARGET)) { r = strv_extend(&s->before, SPECIAL_NETWORK_TARGET); if (r < 0) return log_oom(); } - } else if (t == _UNIT_TYPE_INVALID) + + break; + + case _UNIT_TYPE_INVALID: log_warning("Unit name '%s' is invalid", m); - else + break; + + default: log_warning("Unknown unit type for unit '%s'", m); + } } - if (!isempty(state_)) - log_error("[%s:%u] Trailing garbage in Provides, ignoring.", s->path, line); + return 0; } static int handle_dependencies(SysvStub *s, unsigned line, const char *full_text, const char *text) { - const char *word, *state_; - size_t z; int r; - FOREACH_WORD_QUOTED(word, z, text, state_) { - _cleanup_free_ char *n = NULL, *m = NULL; - bool is_before; + assert(s); + assert(full_text); + assert(text); - n = strndup(word, z); - if (!n) - return log_oom(); + for (;;) { + _cleanup_free_ char *word = NULL, *m = NULL; + bool is_before; - r = sysv_translate_facility(n, basename(s->path), &m); - if (r < 0) { - log_warning_errno(r, "[%s:%u] Failed to translate LSB dependency %s, ignoring: %m", s->path, line, n); - continue; - } + r = extract_first_word(&text, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX); + if (r < 0) + return log_error_errno(r, "Failed to parse word from provides string: %m"); if (r == 0) + break; + + r = sysv_translate_facility(word, basename(s->path), &m); + if (r <= 0) /* continue on error */ continue; is_before = startswith_no_case(full_text, "X-Start-Before:"); @@ -419,15 +446,14 @@ static int handle_dependencies(SysvStub *s, unsigned line, const char *full_text r = strv_extend(&s->after, m); if (r < 0) return log_oom(); + r = strv_extend(&s->wants, m); } else r = strv_extend(is_before ? &s->before : &s->after, m); - if (r < 0) return log_oom(); } - if (!isempty(state_)) - log_warning("[%s:%u] Trailing garbage in %*s, ignoring.", s->path, line, (int)(strchr(full_text, ':') - full_text), full_text); + return 0; } @@ -445,24 +471,22 @@ static int load_sysv(SysvStub *s) { _cleanup_free_ char *short_description = NULL, *long_description = NULL, *chkconfig_description = NULL; char *description; bool supports_reload = false; + char l[LINE_MAX]; assert(s); f = fopen(s->path, "re"); - if (!f) - return errno == ENOENT ? 0 : -errno; - - log_debug("Loading SysV script %s", s->path); + if (!f) { + if (errno == ENOENT) + return 0; - while (!feof(f)) { - char l[LINE_MAX], *t; + return log_error_errno(errno, "Failed to open %s: %m", s->path); + } - if (!fgets(l, sizeof(l), f)) { - if (feof(f)) - break; + log_debug("Loading SysV script %s", s->path); - return log_error_errno(errno, "Failed to read configuration file '%s': %m", s->path); - } + FOREACH_LINE(l, f, goto fail) { + char *t; line++; @@ -505,29 +529,25 @@ static int load_sysv(SysvStub *s) { if (startswith_no_case(t, "description:")) { - size_t k = strlen(t); - char *d; + size_t k; const char *j; - if (t[k-1] == '\\') { + k = strlen(t); + if (k > 0 && t[k-1] == '\\') { state = DESCRIPTION; t[k-1] = 0; } j = strstrip(t+12); - if (j && *j) { - d = strdup(j); - if (!d) - return -ENOMEM; - } else - d = NULL; + if (isempty(j)) + j = NULL; - free(chkconfig_description); - chkconfig_description = d; + r = free_and_strdup(&chkconfig_description, j); + if (r < 0) + return log_oom(); } else if (startswith_no_case(t, "pidfile:")) { - - char *fn; + const char *fn; state = NORMAL; @@ -537,12 +557,9 @@ static int load_sysv(SysvStub *s) { continue; } - fn = strdup(fn); - if (!fn) - return -ENOMEM; - - free(s->pid_file); - s->pid_file = fn; + r = free_and_strdup(&s->pid_file, fn); + if (r < 0) + return log_oom(); } } else if (state == DESCRIPTION) { @@ -550,25 +567,25 @@ static int load_sysv(SysvStub *s) { /* Try to parse Red Hat style description * continuation */ - size_t k = strlen(t); + size_t k; char *j; - if (t[k-1] == '\\') + k = strlen(t); + if (k > 0 && t[k-1] == '\\') t[k-1] = 0; else state = NORMAL; j = strstrip(t); - if (j && *j) { + if (!isempty(j)) { char *d = NULL; if (chkconfig_description) d = strjoin(chkconfig_description, " ", j, NULL); else d = strdup(j); - if (!d) - return -ENOMEM; + return log_oom(); free(chkconfig_description); chkconfig_description = d; @@ -582,6 +599,7 @@ static int load_sysv(SysvStub *s) { r = handle_provides(s, line, t, t + 9); if (r < 0) return r; + } else if (startswith_no_case(t, "Required-Start:") || startswith_no_case(t, "Should-Start:") || startswith_no_case(t, "X-Start-Before:") || @@ -593,55 +611,47 @@ static int load_sysv(SysvStub *s) { if (r < 0) return r; - } else if (startswith_no_case(t, "Description:")) { - char *d, *j; + const char *j; state = LSB_DESCRIPTION; j = strstrip(t+12); - if (j && *j) { - d = strdup(j); - if (!d) - return -ENOMEM; - } else - d = NULL; + if (isempty(j)) + j = NULL; - free(long_description); - long_description = d; + r = free_and_strdup(&long_description, j); + if (r < 0) + return log_oom(); } else if (startswith_no_case(t, "Short-Description:")) { - char *d, *j; + const char *j; state = LSB; j = strstrip(t+18); - if (j && *j) { - d = strdup(j); - if (!d) - return -ENOMEM; - } else - d = NULL; + if (isempty(j)) + j = NULL; - free(short_description); - short_description = d; + r = free_and_strdup(&short_description, j); + if (r < 0) + return log_oom(); } else if (state == LSB_DESCRIPTION) { if (startswith(l, "#\t") || startswith(l, "# ")) { - char *j; + const char *j; j = strstrip(t); - if (j && *j) { + if (!isempty(j)) { char *d = NULL; if (long_description) d = strjoin(long_description, " ", t, NULL); else d = strdup(j); - if (!d) - return -ENOMEM; + return log_oom(); free(long_description); long_description = d; @@ -672,12 +682,16 @@ static int load_sysv(SysvStub *s) { d = strappend(s->has_lsb ? "LSB: " : "SYSV: ", description); if (!d) - return -ENOMEM; + return log_oom(); s->description = d; } + s->loaded = true; return 0; + +fail: + return log_error_errno(errno, "Failed to read configuration file '%s': %m", s->path); } static int fix_order(SysvStub *s, Hashmap *all_services) { @@ -687,6 +701,9 @@ static int fix_order(SysvStub *s, Hashmap *all_services) { assert(s); + if (!s->loaded) + return 0; + if (s->sysv_start_priority < 0) return 0; @@ -694,6 +711,9 @@ static int fix_order(SysvStub *s, Hashmap *all_services) { if (s == other) continue; + if (!other->loaded) + continue; + if (other->sysv_start_priority < 0) continue; @@ -706,13 +726,12 @@ static int fix_order(SysvStub *s, Hashmap *all_services) { r = strv_extend(&s->after, other->name); if (r < 0) return log_oom(); - } - else if (other->sysv_start_priority > s->sysv_start_priority) { + + } else if (other->sysv_start_priority > s->sysv_start_priority) { r = strv_extend(&s->before, other->name); if (r < 0) return log_oom(); - } - else + } else continue; /* FIXME: Maybe we should compare the name here lexicographically? */ @@ -724,6 +743,9 @@ static int fix_order(SysvStub *s, Hashmap *all_services) { static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) { char **path; + assert(lp); + assert(all_services); + STRV_FOREACH(path, lp->sysvinit_path) { _cleanup_closedir_ DIR *d = NULL; struct dirent *de; @@ -731,21 +753,18 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) { d = opendir(*path); if (!d) { if (errno != ENOENT) - log_warning_errno(errno, "opendir(%s) failed: %m", *path); + log_warning_errno(errno, "Opening %s failed, ignoring: %m", *path); continue; } - while ((de = readdir(d))) { + FOREACH_DIRENT(de, d, log_error_errno(errno, "Failed to enumerate directory %s, ignoring: %m", *path)) { _cleanup_free_ char *fpath = NULL, *name = NULL; _cleanup_(free_sysvstubp) SysvStub *service = NULL; struct stat st; int r; - if (hidden_file(de->d_name)) - continue; - if (fstatat(dirfd(d), de->d_name, &st, 0) < 0) { - log_warning_errno(errno, "stat() failed on %s/%s: %m", *path, de->d_name); + log_warning_errno(errno, "stat() failed on %s/%s, ignoring: %m", *path, de->d_name); continue; } @@ -762,15 +781,15 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) { if (hashmap_contains(all_services, name)) continue; - fpath = strjoin(*path, "/", de->d_name, NULL); - if (!fpath) - return log_oom(); - if (unit_file_lookup_state(UNIT_FILE_SYSTEM, NULL, lp, name) >= 0) { log_debug("Native unit for %s already exists, skipping", name); continue; } + fpath = strjoin(*path, "/", de->d_name, NULL); + if (!fpath) + return log_oom(); + service = new0(SysvStub, 1); if (!service) return log_oom(); @@ -778,12 +797,12 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) { service->sysv_start_priority = -1; service->name = name; service->path = fpath; + name = fpath = NULL; r = hashmap_put(all_services, service->name, service); if (r < 0) return log_oom(); - name = fpath = NULL; service = NULL; } } @@ -792,43 +811,41 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) { } static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_services) { - char **p; - unsigned i; - _cleanup_closedir_ DIR *d = NULL; - _cleanup_free_ char *path = NULL, *fpath = NULL; - SysvStub *service; - Iterator j; Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {}; _cleanup_set_free_ Set *shutdown_services = NULL; - int r = 0; + SysvStub *service; + unsigned i; + Iterator j; + char **p; + int r; - STRV_FOREACH(p, lp->sysvrcnd_path) + assert(lp); + + STRV_FOREACH(p, lp->sysvrcnd_path) { for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) { + + _cleanup_closedir_ DIR *d = NULL; + _cleanup_free_ char *path = NULL; struct dirent *de; - free(path); path = strjoin(*p, "/", rcnd_table[i].path, NULL); - if (!path) - return -ENOMEM; - - safe_closedir(d); + if (!path) { + r = log_oom(); + goto finish; + } d = opendir(path); if (!d) { if (errno != ENOENT) - log_warning_errno(errno, "opendir(%s) failed: %m", path); + log_warning_errno(errno, "Opening %s failed, ignoring: %m", path); continue; } - while ((de = readdir(d))) { - _cleanup_free_ char *name = NULL; - + FOREACH_DIRENT(de, d, log_error_errno(errno, "Failed to enumerate directory %s, ignoring: %m", path)) { + _cleanup_free_ char *name = NULL, *fpath = NULL; int a, b; - if (hidden_file(de->d_name)) - continue; - if (de->d_name[0] != 'S' && de->d_name[0] != 'K') continue; @@ -841,10 +858,9 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic if (a < 0 || b < 0) continue; - free(fpath); fpath = strjoin(*p, "/", de->d_name, NULL); if (!fpath) { - r = -ENOMEM; + r = log_oom(); goto finish; } @@ -856,64 +872,77 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic service = hashmap_get(all_services, name); if (!service){ - log_debug("Ignoring %s symlink in %s, not generating %s.", - de->d_name, rcnd_table[i].path, name); + log_debug("Ignoring %s symlink in %s, not generating %s.", de->d_name, rcnd_table[i].path, name); continue; } if (de->d_name[0] == 'S') { - if (rcnd_table[i].type == RUNLEVEL_UP) { - service->sysv_start_priority = - MAX(a*10 + b, service->sysv_start_priority); - } + if (rcnd_table[i].type == RUNLEVEL_UP) + service->sysv_start_priority = MAX(a*10 + b, service->sysv_start_priority); r = set_ensure_allocated(&runlevel_services[i], NULL); - if (r < 0) + if (r < 0) { + log_oom(); goto finish; + } r = set_put(runlevel_services[i], service); - if (r < 0) + if (r < 0) { + log_oom(); goto finish; + } } else if (de->d_name[0] == 'K' && (rcnd_table[i].type == RUNLEVEL_DOWN)) { r = set_ensure_allocated(&shutdown_services, NULL); - if (r < 0) + if (r < 0) { + log_oom(); goto finish; + } r = set_put(shutdown_services, service); - if (r < 0) + if (r < 0) { + log_oom(); goto finish; + } } } } + } for (i = 0; i < ELEMENTSOF(rcnd_table); i ++) SET_FOREACH(service, runlevel_services[i], j) { r = strv_extend(&service->before, rcnd_table[i].target); - if (r < 0) - return log_oom(); + if (r < 0) { + log_oom(); + goto finish; + } r = strv_extend(&service->wanted_by, rcnd_table[i].target); - if (r < 0) - return log_oom(); + if (r < 0) { + log_oom(); + goto finish; + } } SET_FOREACH(service, shutdown_services, j) { r = strv_extend(&service->before, SPECIAL_SHUTDOWN_TARGET); - if (r < 0) - return log_oom(); + if (r < 0) { + log_oom(); + goto finish; + } r = strv_extend(&service->conflicts, SPECIAL_SHUTDOWN_TARGET); - if (r < 0) - return log_oom(); + if (r < 0) { + log_oom(); + goto finish; + } } r = 0; finish: - for (i = 0; i < ELEMENTSOF(rcnd_table); i++) set_free(runlevel_services[i]); @@ -921,11 +950,11 @@ finish: } int main(int argc, char *argv[]) { - int r, q; - _cleanup_lookup_paths_free_ LookupPaths lp = {}; _cleanup_(free_sysvstub_hashmapp) Hashmap *all_services = NULL; + _cleanup_lookup_paths_free_ LookupPaths lp = {}; SysvStub *service; Iterator j; + int r; if (argc > 1 && argc != 4) { log_error("This program takes three or no arguments."); @@ -943,43 +972,34 @@ int main(int argc, char *argv[]) { r = lookup_paths_init(&lp, MANAGER_SYSTEM, true, NULL, NULL, NULL, NULL); if (r < 0) { - log_error("Failed to find lookup paths."); - return EXIT_FAILURE; + log_error_errno(r, "Failed to find lookup paths: %m"); + goto finish; } all_services = hashmap_new(&string_hash_ops); if (!all_services) { - log_oom(); - return EXIT_FAILURE; + r = log_oom(); + goto finish; } r = enumerate_sysv(&lp, all_services); - if (r < 0) { - log_error("Failed to generate units for all init scripts."); - return EXIT_FAILURE; - } + if (r < 0) + goto finish; r = set_dependencies_from_rcnd(&lp, all_services); - if (r < 0) { - log_error("Failed to read runlevels from rcnd links."); - return EXIT_FAILURE; - } + if (r < 0) + goto finish; - HASHMAP_FOREACH(service, all_services, j) { - q = load_sysv(service); - if (q < 0) - continue; - } + HASHMAP_FOREACH(service, all_services, j) + (void) load_sysv(service); HASHMAP_FOREACH(service, all_services, j) { - q = fix_order(service, all_services); - if (q < 0) - continue; - - q = generate_unit_file(service); - if (q < 0) - continue; + (void) fix_order(service, all_services); + (void) generate_unit_file(service); } - return EXIT_SUCCESS; + r = 0; + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/src/test/test-execute.c b/src/test/test-execute.c index afbaa12e94..e2ec53ee51 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -22,6 +22,7 @@ #include <stdio.h> #include <sys/types.h> +#include "fileio.h" #include "fs-util.h" #include "macro.h" #include "manager.h" @@ -147,6 +148,26 @@ static void test_exec_environment(Manager *m) { test(m, "exec-environment-empty.service", 0, CLD_EXITED); } +static void test_exec_environmentfile(Manager *m) { + static const char e[] = + "VAR1='word1 word2'\n" + "VAR2=word3 \n" + "# comment1\n" + "\n" + "; comment2\n" + " ; # comment3\n" + "line without an equal\n" + "VAR3='$word 5 6'\n"; + int r; + + r = write_string_file("/tmp/test-exec_environmentfile.conf", e, WRITE_STRING_FILE_CREATE); + assert_se(r == 0); + + test(m, "exec-environmentfile.service", 0, CLD_EXITED); + + unlink("/tmp/test-exec_environmentfile.conf"); +} + static void test_exec_umask(Manager *m) { test(m, "exec-umask-default.service", 0, CLD_EXITED); test(m, "exec-umask-0177.service", 0, CLD_EXITED); @@ -178,6 +199,30 @@ static void test_exec_capabilityboundingset(Manager *m) { test(m, "exec-capabilityboundingset-invert.service", 0, CLD_EXITED); } +static void test_exec_privatenetwork(Manager *m) { + int r; + + r = find_binary("ip", NULL); + if (r < 0) { + log_error_errno(r, "Skipping test_exec_privatenetwork, could not find ip binary: %m"); + return; + } + + test(m, "exec-privatenetwork-yes.service", 0, CLD_EXITED); +} + +static void test_exec_oomscoreadjust(Manager *m) { + test(m, "exec-oomscoreadjust-positive.service", 0, CLD_EXITED); + test(m, "exec-oomscoreadjust-negative.service", 0, CLD_EXITED); +} + +static void test_exec_ioschedulingclass(Manager *m) { + test(m, "exec-ioschedulingclass-none.service", 0, CLD_EXITED); + test(m, "exec-ioschedulingclass-idle.service", 0, CLD_EXITED); + test(m, "exec-ioschedulingclass-realtime.service", 0, CLD_EXITED); + test(m, "exec-ioschedulingclass-best-effort.service", 0, CLD_EXITED); +} + int main(int argc, char *argv[]) { test_function_t tests[] = { test_exec_workingdirectory, @@ -185,14 +230,18 @@ int main(int argc, char *argv[]) { test_exec_ignoresigpipe, test_exec_privatetmp, test_exec_privatedevices, + test_exec_privatenetwork, test_exec_systemcallfilter, test_exec_systemcallerrornumber, test_exec_user, test_exec_group, test_exec_environment, + test_exec_environmentfile, test_exec_umask, test_exec_runtimedirectory, test_exec_capabilityboundingset, + test_exec_oomscoreadjust, + test_exec_ioschedulingclass, NULL, }; test_function_t *test = NULL; @@ -209,7 +258,7 @@ int main(int argc, char *argv[]) { } assert_se(setenv("XDG_RUNTIME_DIR", "/tmp/", 1) == 0); - assert_se(set_unit_path(TEST_DIR) >= 0); + assert_se(set_unit_path(TEST_DIR "/test-execute/") >= 0); r = manager_new(MANAGER_USER, true, &m); if (IN_SET(r, -EPERM, -EACCES, -EADDRINUSE, -EHOSTDOWN, -ENOENT)) { diff --git a/src/test/test-path-lookup.c b/src/test/test-path-lookup.c index 92ffa65925..65cb894ff7 100644 --- a/src/test/test-path-lookup.c +++ b/src/test/test-path-lookup.c @@ -19,6 +19,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <stdlib.h> #include <sys/stat.h> #include "log.h" @@ -30,19 +31,27 @@ static void test_paths(ManagerRunningAs running_as, bool personal) { char template[] = "/tmp/test-path-lookup.XXXXXXX"; - _cleanup_lookup_paths_free_ LookupPaths lp = {}; - char *exists, *not; + _cleanup_lookup_paths_free_ LookupPaths lp_without_env = {}; + _cleanup_lookup_paths_free_ LookupPaths lp_with_env = {}; + char *exists, *not, *systemd_unit_path; assert_se(mkdtemp(template)); exists = strjoina(template, "/exists"); assert_se(mkdir(exists, 0755) == 0); not = strjoina(template, "/not"); - assert_se(lookup_paths_init(&lp, running_as, personal, NULL, exists, not, not) == 0); + assert_se(unsetenv("SYSTEMD_UNIT_PATH") == 0); + assert_se(lookup_paths_init(&lp_without_env, running_as, personal, NULL, exists, not, not) == 0); - assert_se(!strv_isempty(lp.unit_path)); - assert_se(strv_contains(lp.unit_path, exists)); - assert_se(strv_contains(lp.unit_path, not)); + assert_se(!strv_isempty(lp_without_env.unit_path)); + assert_se(strv_contains(lp_without_env.unit_path, exists)); + assert_se(strv_contains(lp_without_env.unit_path, not)); + + systemd_unit_path = strjoina(template, "/systemd-unit-path"); + assert_se(setenv("SYSTEMD_UNIT_PATH", systemd_unit_path, 1) == 0); + assert_se(lookup_paths_init(&lp_with_env, running_as, personal, NULL, exists, not, not) == 0); + assert_se(strv_length(lp_with_env.unit_path) == 1); + assert_se(streq(lp_with_env.unit_path[0], systemd_unit_path)); assert_se(rm_rf(template, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); } diff --git a/src/test/test-path.c b/src/test/test-path.c index ff0f044958..8302bdd283 100644 --- a/src/test/test-path.c +++ b/src/test/test-path.c @@ -258,7 +258,7 @@ int main(int argc, char *argv[]) { log_parse_environment(); log_open(); - assert_se(set_unit_path(TEST_DIR) >= 0); + assert_se(set_unit_path(TEST_DIR "/test-path/") >= 0); for (test = tests; test && *test; test++) { int r; diff --git a/src/test/test-string-util.c b/src/test/test-string-util.c new file mode 100644 index 0000000000..25444c794a --- /dev/null +++ b/src/test/test-string-util.c @@ -0,0 +1,61 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2015 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see <http://www.gnu.org/licenses/>. +***/ + +#include "string-util.h" + +static void test_string_erase(void) { + char *x; + + x = strdupa(""); + assert_se(streq(string_erase(x), "")); + + x = strdupa("1"); + assert_se(streq(string_erase(x), "x")); + + x = strdupa("12"); + assert_se(streq(string_erase(x), "xx")); + + x = strdupa("123"); + assert_se(streq(string_erase(x), "xxx")); + + x = strdupa("1234"); + assert_se(streq(string_erase(x), "xxxx")); + + x = strdupa("12345"); + assert_se(streq(string_erase(x), "xxxxx")); + + x = strdupa("123456"); + assert_se(streq(string_erase(x), "xxxxxx")); + + x = strdupa("1234567"); + assert_se(streq(string_erase(x), "xxxxxxx")); + + x = strdupa("12345678"); + assert_se(streq(string_erase(x), "xxxxxxxx")); + + x = strdupa("123456789"); + assert_se(streq(string_erase(x), "xxxxxxxxx")); +} + +int main(int argc, char *argv[]) { + test_string_erase(); + return 0; +} diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c index a2ca391e1a..f9107e0d0d 100644 --- a/src/test/test-unit-file.c +++ b/src/test/test-unit-file.c @@ -672,6 +672,13 @@ static void test_config_parse_bounding_set(void) { &capability_bounding_set_drop, NULL); assert_se(r >= 0); assert_se(capability_bounding_set_drop == (uint64_t) 0ULL); + + capability_bounding_set_drop = 0; + r = config_parse_bounding_set(NULL, "fake", 1, "section", 1, + "CapabilityBoundingSet", 0, " 'CAP_NET_RAW' WAT_CAP??? CAP_NET_ADMIN CAP'_trailing_garbage", + &capability_bounding_set_drop, NULL); + assert_se(r >= 0); + assert_se(capability_bounding_set_drop == ~(make_cap(CAP_NET_RAW) | make_cap(CAP_NET_ADMIN))); } int main(int argc, char *argv[]) { |