diff --git a/src/basic/capability-util.c b/src/basic/capability-util.c index 645cd322a7..a425580887 100644 --- a/src/basic/capability-util.c +++ b/src/basic/capability-util.c @@ -228,10 +228,10 @@ finish: } static int drop_from_file(const char *fn, uint64_t keep) { - int r, k; - uint32_t hi, lo; + _cleanup_free_ char *p = NULL; uint64_t current, after; - char *p; + uint32_t hi, lo; + int r, k; r = read_one_line_file(fn, &p); if (r < 0) @@ -241,8 +241,6 @@ static int drop_from_file(const char *fn, uint64_t keep) { assert_cc(sizeof(lo) == sizeof(unsigned)); k = sscanf(p, "%u %u", &lo, &hi); - free(p); - if (k != 2) return -EIO; @@ -255,13 +253,7 @@ static int drop_from_file(const char *fn, uint64_t keep) { lo = (unsigned) (after & 0xFFFFFFFFULL); hi = (unsigned) ((after >> 32ULL) & 0xFFFFFFFFULL); - if (asprintf(&p, "%u %u", lo, hi) < 0) - return -ENOMEM; - - r = write_string_file(fn, p, WRITE_STRING_FILE_CREATE); - free(p); - - return r; + return write_string_filef(fn, WRITE_STRING_FILE_CREATE, "%u %u", lo, hi); } int capability_bounding_set_drop_usermode(uint64_t keep) { diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index e54881ca3c..e1bea94c90 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -930,3 +930,27 @@ int fd_reopen(int fd, int flags) { return new_fd; } + +int read_nr_open(void) { + _cleanup_free_ char *nr_open = NULL; + int r; + + /* Returns the kernel's current fd limit, either by reading it of /proc/sys if that works, or using the + * hard-coded default compiled-in value of current kernels (1M) if not. This call will never fail. */ + + r = read_one_line_file("/proc/sys/fs/nr_open", &nr_open); + if (r < 0) + log_debug_errno(r, "Failed to read /proc/sys/fs/nr_open, ignoring: %m"); + else { + int v; + + r = safe_atoi(nr_open, &v); + if (r < 0) + log_debug_errno(r, "Failed to parse /proc/sys/fs/nr_open value '%s', ignoring: %m", nr_open); + else + return v; + } + + /* If we fail, fallback to the hard-coded kernel limit of 1024 * 1024. */ + return 1024 * 1024; +} diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h index 2ae4e67ed0..b94944972c 100644 --- a/src/basic/fd-util.h +++ b/src/basic/fd-util.h @@ -108,3 +108,5 @@ static inline int make_null_stdio(void) { }) int fd_reopen(int fd, int flags); + +int read_nr_open(void); diff --git a/src/basic/fileio.c b/src/basic/fileio.c index c12b4ac7f5..5837163fed 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -206,6 +206,25 @@ fail: return 0; } +int write_string_filef( + const char *fn, + WriteStringFileFlags flags, + const char *format, ...) { + + _cleanup_free_ char *p = NULL; + va_list ap; + int r; + + va_start(ap, format); + r = vasprintf(&p, format, ap); + va_end(ap); + + if (r < 0) + return -ENOMEM; + + return write_string_file(fn, p, flags); +} + int read_one_line_file(const char *fn, char **line) { _cleanup_fclose_ FILE *f = NULL; int r; diff --git a/src/basic/fileio.h b/src/basic/fileio.h index addd9bade4..744d319abe 100644 --- a/src/basic/fileio.h +++ b/src/basic/fileio.h @@ -39,6 +39,8 @@ static inline int write_string_file(const char *fn, const char *line, WriteStrin return write_string_file_ts(fn, line, flags, NULL); } +int write_string_filef(const char *fn, WriteStringFileFlags flags, const char *format, ...) _printf_(3, 4); + int read_one_line_file(const char *fn, char **line); int read_full_file(const char *fn, char **contents, size_t *size); int read_full_stream(FILE *f, char **contents, size_t *size); diff --git a/src/core/main.c b/src/core/main.c index 8c04d41c0c..1e75ad7203 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -311,6 +311,7 @@ static int parse_confirm_spawn(const char *value, char **console) { s = strjoin("/dev/", value); if (!s) return -ENOMEM; + *console = s; return 0; } @@ -731,6 +732,10 @@ static void set_manager_defaults(Manager *m) { assert(m); + /* Propagates the various default unit property settings into the manager object, i.e. properties that do not + * affect the manager itself, but are just what newly allocated units will have set if they haven't set + * anything else. (Also see set_manager_settings() for the settings that affect the manager's own behaviour) */ + m->default_timer_accuracy_usec = arg_default_timer_accuracy_usec; m->default_std_output = arg_default_std_output; m->default_std_error = arg_default_std_error; @@ -755,6 +760,9 @@ static void set_manager_settings(Manager *m) { assert(m); + /* Propagates the various manager settings into the manager object, i.e. properties that effect the manager + * itself (as opposed to just being inherited into newly allocated units, see set_manager_defaults() above). */ + m->confirm_spawn = arg_confirm_spawn; m->service_watchdogs = arg_service_watchdogs; m->runtime_watchdog = arg_runtime_watchdog; @@ -1136,10 +1144,7 @@ static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds, bool switching } static int bump_rlimit_nofile(struct rlimit *saved_rlimit) { - struct rlimit nl; - int r; - int min_max; - _cleanup_free_ char *nr_open = NULL; + int r, nr; assert(saved_rlimit); @@ -1160,17 +1165,9 @@ static int bump_rlimit_nofile(struct rlimit *saved_rlimit) { arg_default_rlimit[RLIMIT_NOFILE] = rl; } - /* Get current RLIMIT_NOFILE maximum compiled into the kernel. */ - r = read_one_line_file("/proc/sys/fs/nr_open", &nr_open); - if (r >= 0) - r = safe_atoi(nr_open, &min_max); - /* If we fail, fallback to the hard-coded kernel limit of 1024 * 1024. */ - if (r < 0) - min_max = 1024 * 1024; - - /* Bump up the resource limit for ourselves substantially */ - nl.rlim_cur = nl.rlim_max = min_max; - r = setrlimit_closest(RLIMIT_NOFILE, &nl); + /* Bump up the resource limit for ourselves substantially, all the way to the maximum the kernel allows */ + nr = read_nr_open(); + r = setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(nr)); if (r < 0) return log_warning_errno(r, "Setting RLIMIT_NOFILE failed, ignoring: %m"); @@ -1270,10 +1267,9 @@ static int bump_unix_max_dgram_qlen(void) { 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. */ + /* 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) @@ -1281,16 +1277,12 @@ static int bump_unix_max_dgram_qlen(void) { r = safe_atolu(qlen, &v); if (r < 0) - return log_warning_errno(r, "Failed to parse AF_UNIX datagram queue length, ignoring: %m"); + return log_warning_errno(r, "Failed to parse AF_UNIX datagram queue length '%s', ignoring: %m", qlen); 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); + r = write_string_filef("/proc/sys/net/unix/max_dgram_qlen", 0, "%lu", DEFAULT_UNIX_MAX_DGRAM_QLEN); 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"); @@ -1934,12 +1926,9 @@ static int do_queue_default_job( } static void free_arguments(void) { - size_t j; /* Frees all arg_* variables, with the exception of arg_serialization */ - - for (j = 0; j < ELEMENTSOF(arg_default_rlimit); j++) - arg_default_rlimit[j] = mfree(arg_default_rlimit[j]); + rlimit_free_all(arg_default_rlimit); arg_default_unit = mfree(arg_default_unit); arg_confirm_spawn = mfree(arg_confirm_spawn); @@ -2136,7 +2125,6 @@ static bool early_skip_setup_check(int argc, char *argv[]) { * anyway, even if in that case we also do deserialization. */ for (i = 1; i < argc; i++) { - if (streq(argv[i], "--switched-root")) return false; /* If we switched root, don't skip the setup. */ else if (streq(argv[i], "--deserialize")) @@ -2188,8 +2176,8 @@ int main(int argc, char *argv[]) { /* Disable the umask logic */ umask(0); - /* Make sure that at least initially we do not ever log to journald/syslogd, because it might not be activated - * yet (even though the log socket for it exists). */ + /* Make sure that at least initially we do not ever log to journald/syslogd, because it might not be + * activated yet (even though the log socket for it exists). */ log_set_prohibit_ipc(true); /* Always reopen /dev/console when running as PID 1 or one of its pre-execve() children. This is @@ -2197,62 +2185,70 @@ int main(int argc, char *argv[]) { * child process right before execve()'ing the actual binary, at a point in time where socket * activation stderr/stdout area already set up. */ log_set_always_reopen_console(true); - } - if (getpid_cached() == 1 && detect_container() <= 0) { + if (detect_container() <= 0) { - /* Running outside of a container as PID 1 */ - arg_system = true; - log_set_target(LOG_TARGET_KMSG); - log_open(); + /* Running outside of a container as PID 1 */ + arg_system = true; + log_set_target(LOG_TARGET_KMSG); + log_open(); - if (in_initrd()) - initrd_timestamp = userspace_timestamp; + if (in_initrd()) + initrd_timestamp = userspace_timestamp; - if (!skip_setup) { - r = mount_setup_early(); - if (r < 0) { - error_message = "Failed to mount early API filesystems"; + if (!skip_setup) { + r = mount_setup_early(); + if (r < 0) { + error_message = "Failed to mount early API filesystems"; + goto finish; + } + + r = initialize_security( + &loaded_policy, + &security_start_timestamp, + &security_finish_timestamp, + &error_message); + if (r < 0) + goto finish; + } + + if (mac_selinux_init() < 0) { + error_message = "Failed to initialize SELinux policy"; goto finish; } - r = initialize_security( - &loaded_policy, - &security_start_timestamp, - &security_finish_timestamp, - &error_message); - if (r < 0) - goto finish; + if (!skip_setup) + initialize_clock(); + + /* Set the default for later on, but don't actually open the logs like this for now. Note that + * if we are transitioning from the initrd there might still be journal fd open, and we + * shouldn't attempt opening that before we parsed /proc/cmdline which might redirect output + * elsewhere. */ + log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); + + } else { + /* Running inside a container, as PID 1 */ + arg_system = true; + log_set_target(LOG_TARGET_CONSOLE); + log_open(); + + /* For later on, see above... */ + log_set_target(LOG_TARGET_JOURNAL); + + /* clear the kernel timestamp, + * because we are in a container */ + kernel_timestamp = DUAL_TIMESTAMP_NULL; } - if (mac_selinux_init() < 0) { - error_message = "Failed to initialize SELinux policy"; + initialize_coredump(skip_setup); + + r = fixup_environment(); + if (r < 0) { + log_emergency_errno(r, "Failed to fix up PID 1 environment: %m"); + error_message = "Failed to fix up PID1 environment"; goto finish; } - if (!skip_setup) - initialize_clock(); - - /* Set the default for later on, but don't actually - * open the logs like this for now. Note that if we - * are transitioning from the initrd there might still - * be journal fd open, and we shouldn't attempt - * opening that before we parsed /proc/cmdline which - * might redirect output elsewhere. */ - log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); - - } else if (getpid_cached() == 1) { - /* Running inside a container, as PID 1 */ - arg_system = true; - log_set_target(LOG_TARGET_CONSOLE); - log_open(); - - /* For later on, see above... */ - log_set_target(LOG_TARGET_JOURNAL); - - /* clear the kernel timestamp, - * because we are in a container */ - kernel_timestamp = DUAL_TIMESTAMP_NULL; } else { /* Running as user instance */ arg_system = false; @@ -2264,24 +2260,14 @@ int main(int argc, char *argv[]) { kernel_timestamp = DUAL_TIMESTAMP_NULL; } - initialize_coredump(skip_setup); - - r = fixup_environment(); - if (r < 0) { - log_emergency_errno(r, "Failed to fix up PID 1 environment: %m"); - error_message = "Failed to fix up PID1 environment"; - goto finish; - } - if (arg_system) { - - /* Try to figure out if we can use colors with the console. No - * need to do that for user instances since they never log - * into the console. */ + /* Try to figure out if we can use colors with the console. No need to do that for user instances since + * they never log into the console. */ log_show_color(colors_enabled()); + r = make_null_stdio(); if (r < 0) - log_warning_errno(r, "Failed to redirect standard streams to /dev/null: %m"); + log_warning_errno(r, "Failed to redirect standard streams to /dev/null, ignoring: %m"); } /* Mount /proc, /sys and friends, so that /proc/cmdline and @@ -2429,10 +2415,10 @@ int main(int argc, char *argv[]) { finish: pager_close(); - if (m) + if (m) { arg_shutdown_watchdog = m->shutdown_watchdog; - - m = manager_free(m); + m = manager_free(m); + } free_arguments(); mac_selinux_finish(); @@ -2465,7 +2451,6 @@ finish: if (shutdown_verb) { r = become_shutdown(shutdown_verb, retval); - log_error_errno(r, "Failed to execute shutdown binary, %s: %m", getpid_cached() == 1 ? "freezing" : "quitting"); error_message = "Failed to execute shutdown binary"; } diff --git a/src/test/test-fd-util.c b/src/test/test-fd-util.c index fc3c463385..b1de8aca5c 100644 --- a/src/test/test-fd-util.c +++ b/src/test/test-fd-util.c @@ -315,7 +315,14 @@ static void test_fd_duplicate_data_fd(void) { assert_se(read(fd2, &j, sizeof(j)) == 0); } +static void test_read_nr_open(void) { + log_info("nr-open: %i", read_nr_open()); +} + int main(int argc, char *argv[]) { + + log_set_max_level(LOG_DEBUG); + test_close_many(); test_close_nointr(); test_same_fd(); @@ -324,6 +331,7 @@ int main(int argc, char *argv[]) { test_fd_move_above_stdio(); test_rearrange_stdio(); test_fd_duplicate_data_fd(); + test_read_nr_open(); return 0; }