diff --git a/Makefile.am b/Makefile.am index 8b442dc243..c7b8a1b8f7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1216,6 +1216,7 @@ src_libsystemd_nm_la_SOURCES = \ src/systemd/sd-adapt/raw-clone.h \ src/systemd/sd-adapt/sd-daemon.h \ src/systemd/sd-adapt/stat-util.h \ + src/systemd/sd-adapt/terminal-util.h \ src/systemd/sd-adapt/udev-util.h \ src/systemd/sd-adapt/udev.h \ src/systemd/sd-adapt/user-util.h \ diff --git a/src/systemd/sd-adapt/terminal-util.h b/src/systemd/sd-adapt/terminal-util.h new file mode 100644 index 0000000000..637892c2d6 --- /dev/null +++ b/src/systemd/sd-adapt/terminal-util.h @@ -0,0 +1,3 @@ +#pragma once + +/* dummy header */ diff --git a/src/systemd/src/basic/async.h b/src/systemd/src/basic/async.h index 7eac54d8b2..01c975bb30 100644 --- a/src/systemd/src/basic/async.h +++ b/src/systemd/src/basic/async.h @@ -22,5 +22,5 @@ int asynchronous_job(void* (*func)(void *p), void *arg); -int asynchronous_sync(void); +int asynchronous_sync(pid_t *ret_pid); int asynchronous_close(int fd); diff --git a/src/systemd/src/basic/fd-util.c b/src/systemd/src/basic/fd-util.c index 528bde994f..82944908e8 100644 --- a/src/systemd/src/basic/fd-util.c +++ b/src/systemd/src/basic/fd-util.c @@ -195,9 +195,9 @@ int fd_cloexec(int fd, bool cloexec) { #if 0 /* NM_IGNORED */ void stdio_unset_cloexec(void) { - fd_cloexec(STDIN_FILENO, false); - fd_cloexec(STDOUT_FILENO, false); - fd_cloexec(STDERR_FILENO, false); + (void) fd_cloexec(STDIN_FILENO, false); + (void) fd_cloexec(STDOUT_FILENO, false); + (void) fd_cloexec(STDERR_FILENO, false); } _pure_ static bool fd_in_set(int fd, const int fdset[], unsigned n_fdset) { @@ -230,20 +230,21 @@ int close_all_fds(const int except[], unsigned n_except) { assert_se(getrlimit(RLIMIT_NOFILE, &rl) >= 0); for (fd = 3; fd < (int) rl.rlim_max; fd ++) { + int q; if (fd_in_set(fd, except, n_except)) continue; - if (close_nointr(fd) < 0) - if (errno != EBADF && r == 0) - r = -errno; + q = close_nointr(fd); + if (q < 0 && q != -EBADF && r >= 0) + r = q; } return r; } FOREACH_DIRENT(de, d, return -errno) { - int fd = -1; + int fd = -1, q; if (safe_atoi(de->d_name, &fd) < 0) /* Let's better ignore this, just in case */ @@ -258,11 +259,9 @@ int close_all_fds(const int except[], unsigned n_except) { if (fd_in_set(fd, except, n_except)) continue; - if (close_nointr(fd) < 0) { - /* Valgrind has its own FD and doesn't want to have it closed */ - if (errno != EBADF && r == 0) - r = -errno; - } + q = close_nointr(fd); + if (q < 0 && q != -EBADF && r >= 0) /* Valgrind has its own FD and doesn't want to have it closed */ + r = q; } return r; @@ -432,7 +431,7 @@ int move_fd(int from, int to, int cloexec) { int acquire_data_fd(const void *data, size_t size, unsigned flags) { - char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; + char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; _cleanup_close_pair_ int pipefds[2] = { -1, -1 }; char pattern[] = "/dev/shm/data-fd-XXXXXX"; _cleanup_close_ int fd = -1; diff --git a/src/systemd/src/basic/fileio.c b/src/systemd/src/basic/fileio.c index 9a0c4d9a87..a23d82a4b2 100644 --- a/src/systemd/src/basic/fileio.c +++ b/src/systemd/src/basic/fileio.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -67,9 +68,15 @@ int write_string_stream_ts( assert(f); assert(line); - fputs(line, f); + if (ferror(f)) + return -EIO; + + if (fputs(line, f) == EOF) + return -errno; + if (!(flags & WRITE_STRING_FILE_AVOID_NEWLINE) && !endswith(line, "\n")) - fputc('\n', f); + if (fputc('\n', f) == EOF) + return -errno; if (ts) { struct timespec twice[2] = {*ts, *ts}; @@ -101,6 +108,7 @@ static int write_string_file_atomic( if (r < 0) return r; + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); (void) fchmod_umask(fileno(f), 0644); r = write_string_stream_ts(f, line, flags, ts); @@ -170,6 +178,8 @@ int write_string_file_ts( } } + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + if (flags & WRITE_STRING_FILE_DISABLE_BUFFER) setvbuf(f, NULL, _IONBF, 0); @@ -206,10 +216,11 @@ int read_one_line_file(const char *fn, char **line) { if (!f) return -errno; + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + r = read_line(f, LONG_LINE_MAX, line); return r < 0 ? r : 0; } -#endif /* NM_IGNORED */ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) { _cleanup_fclose_ FILE *f = NULL; @@ -232,6 +243,8 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) { if (!f) return -errno; + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + /* We try to read one byte more than we need, so that we know whether we hit eof */ errno = 0; k = fread(buf, 1, l + accept_extra_nl + 1, f); @@ -247,6 +260,7 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) { return 1; } +#endif /* NM_IGNORED */ int read_full_stream(FILE *f, char **contents, size_t *size) { size_t n, l; @@ -327,6 +341,8 @@ int read_full_file(const char *fn, char **contents, size_t *size) { if (!f) return -errno; + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + return read_full_stream(f, contents, size); } @@ -884,7 +900,8 @@ int write_env_file(const char *fname, char **l) { if (r < 0) return r; - fchmod_umask(fileno(f), 0644); + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + (void) fchmod_umask(fileno(f), 0644); STRV_FOREACH(i, l) write_env_var(f, *i); @@ -1472,7 +1489,7 @@ int link_tmpfile(int fd, const char *path, const char *target) { if (rename_noreplace(AT_FDCWD, path, AT_FDCWD, target) < 0) return -errno; } else { - char proc_fd_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; + char proc_fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; xsprintf(proc_fd_path, "/proc/self/fd/%i", fd); diff --git a/src/systemd/src/basic/fs-util.c b/src/systemd/src/basic/fs-util.c index b9e2fb0bcc..3c949059a8 100644 --- a/src/systemd/src/basic/fs-util.c +++ b/src/systemd/src/basic/fs-util.c @@ -587,7 +587,7 @@ int tmp_dir(const char **ret) { } int inotify_add_watch_fd(int fd, int what, uint32_t mask) { - char path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; + char path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; int r; /* This is like inotify_add_watch(), except that the file to watch is not referenced by a path, but by an fd */ @@ -832,7 +832,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, } int access_fd(int fd, int mode) { - char p[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; + char p[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1]; int r; /* Like access() but operates on an already open fd */ diff --git a/src/systemd/src/basic/log.h b/src/systemd/src/basic/log.h index 9046631963..c954bc557c 100644 --- a/src/systemd/src/basic/log.h +++ b/src/systemd/src/basic/log.h @@ -341,3 +341,5 @@ int log_syntax_internal( "String is not UTF-8 clean, ignoring assignment: %s", strna(_p)); \ } \ }) + +#define DEBUG_LOGGING _unlikely_(log_get_max_level() >= LOG_DEBUG) diff --git a/src/systemd/src/basic/process-util.c b/src/systemd/src/basic/process-util.c index 9f12453758..2f1f397c74 100644 --- a/src/systemd/src/basic/process-util.c +++ b/src/systemd/src/basic/process-util.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -59,6 +60,7 @@ #include "stat-util.h" #include "string-table.h" #include "string-util.h" +#include "terminal-util.h" #include "user-util.h" #include "util.h" @@ -135,6 +137,8 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char * return -errno; } + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + if (max_length == 1) { /* If there's only room for one byte, return the empty string */ @@ -298,10 +302,17 @@ int rename_process(const char name[]) { if (isempty(name)) return -EINVAL; /* let's not confuse users unnecessarily with an empty name */ + if (!is_main_thread()) + return -EPERM; /* Let's not allow setting the process name from other threads than the main one, as we + * cache things without locking, and we make assumptions that PR_SET_NAME sets the + * process name that isn't correct on any other threads */ + l = strlen(name); - /* First step, change the comm field. */ - (void) prctl(PR_SET_NAME, name); + /* First step, change the comm field. The main thread's comm is identical to the process comm. This means we + * can use PR_SET_NAME, which sets the thread name for the calling thread. */ + if (prctl(PR_SET_NAME, name) < 0) + log_debug_errno(errno, "PR_SET_NAME failed: %m"); if (l > 15) /* Linux process names can be 15 chars at max */ truncated = true; @@ -411,6 +422,8 @@ int is_kernel_thread(pid_t pid) { return -errno; } + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + count = fread(&c, 1, 1, f); eof = feof(f); fclose(f); @@ -492,6 +505,8 @@ static int get_process_id(pid_t pid, const char *field, uid_t *uid) { return -errno; } + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + FOREACH_LINE(line, f, return -errno) { char *l; @@ -570,6 +585,8 @@ int get_process_environ(pid_t pid, char **env) { return -errno; } + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + while ((c = fgetc(f)) != EOF) { if (!GREEDY_REALLOC(outcome, allocated, sz + 5)) return -ENOMEM; @@ -704,6 +721,67 @@ int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_cod return -EPROTO; } +/* + * Return values: + * < 0 : wait_for_terminate_with_timeout() failed to get the state of the + * process, the process timed out, the process was terminated by a + * signal, or failed for an unknown reason. + * >=0 : The process terminated normally with no failures. + * + * Success is indicated by a return value of zero, a timeout is indicated + * by ETIMEDOUT, and all other child failure states are indicated by error + * is indicated by a non-zero value. + */ +int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) { + sigset_t mask; + int r; + usec_t until; + + assert_se(sigemptyset(&mask) == 0); + assert_se(sigaddset(&mask, SIGCHLD) == 0); + + /* Drop into a sigtimewait-based timeout. Waiting for the + * pid to exit. */ + until = now(CLOCK_MONOTONIC) + timeout; + for (;;) { + usec_t n; + siginfo_t status = {}; + struct timespec ts; + + n = now(CLOCK_MONOTONIC); + if (n >= until) + break; + + r = sigtimedwait(&mask, NULL, timespec_store(&ts, until - n)) < 0 ? -errno : 0; + /* Assuming we woke due to the child exiting. */ + if (waitid(P_PID, pid, &status, WEXITED|WNOHANG) == 0) { + if (status.si_pid == pid) { + /* This is the correct child.*/ + if (status.si_code == CLD_EXITED) + return (status.si_status == 0) ? 0 : -EPROTO; + else + return -EPROTO; + } + } + /* Not the child, check for errors and proceed appropriately */ + if (r < 0) { + switch (r) { + case -EAGAIN: + /* Timed out, child is likely hung. */ + return -ETIMEDOUT; + case -EINTR: + /* Received a different signal and should retry */ + continue; + default: + /* Return any unexpected errors */ + return r; + } + } + } + + return -EPROTO; +} + void sigkill_wait(pid_t pid) { assert(pid > 1); @@ -754,6 +832,8 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value) { return -errno; } + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + l = strlen(field); r = 0; @@ -1062,6 +1142,222 @@ pid_t getpid_cached(void) { } #if 0 /* NM_IGNORED */ +int must_be_root(void) { + + if (geteuid() == 0) + return 0; + + log_error("Need to be root."); + return -EPERM; +} + +int safe_fork_full( + const char *name, + const int except_fds[], + size_t n_except_fds, + ForkFlags flags, + pid_t *ret_pid) { + + pid_t original_pid, pid; + sigset_t saved_ss; + bool block_signals; + int r; + + /* A wrapper around fork(), that does a couple of important initializations in addition to mere forking. Always + * returns the child's PID in *ret_pid. Returns == 0 in the child, and > 0 in the parent. */ + + original_pid = getpid_cached(); + + block_signals = flags & (FORK_RESET_SIGNALS|FORK_DEATHSIG); + + if (block_signals) { + sigset_t ss; + + /* We temporarily block all signals, so that the new child has them blocked initially. This way, we can be sure + * that SIGTERMs are not lost we might send to the child. */ + if (sigfillset(&ss) < 0) + return log_debug_errno(errno, "Failed to reset signal set: %m"); + + if (sigprocmask(SIG_SETMASK, &ss, &saved_ss) < 0) + return log_debug_errno(errno, "Failed to reset signal mask: %m"); + } + + pid = fork(); + if (pid < 0) { + r = -errno; + + if (block_signals) /* undo what we did above */ + (void) sigprocmask(SIG_SETMASK, &saved_ss, NULL); + + return log_debug_errno(r, "Failed to fork: %m"); + } + if (pid > 0) { + /* We are in the parent process */ + + if (block_signals) /* undo what we did above */ + (void) sigprocmask(SIG_SETMASK, &saved_ss, NULL); + + log_debug("Sucessfully forked off '%s' as PID " PID_FMT ".", strna(name), pid); + + if (ret_pid) + *ret_pid = pid; + + return 1; + } + + /* We are in the child process */ + + if (flags & FORK_REOPEN_LOG) { + /* Close the logs if requested, before we log anything. And make sure we reopen it if needed. */ + log_close(); + log_set_open_when_needed(true); + } + + if (name) { + r = rename_process(name); + if (r < 0) + log_debug_errno(r, "Failed to rename process, ignoring: %m"); + } + + if (flags & FORK_DEATHSIG) + if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) { + log_debug_errno(errno, "Failed to set death signal: %m"); + _exit(EXIT_FAILURE); + } + + if (flags & FORK_RESET_SIGNALS) { + r = reset_all_signal_handlers(); + if (r < 0) { + log_debug_errno(r, "Failed to reset signal handlers: %m"); + _exit(EXIT_FAILURE); + } + + /* This implicitly undoes the signal mask stuff we did before the fork()ing above */ + r = reset_signal_mask(); + if (r < 0) { + log_debug_errno(r, "Failed to reset signal mask: %m"); + _exit(EXIT_FAILURE); + } + } else if (block_signals) { /* undo what we did above */ + if (sigprocmask(SIG_SETMASK, &saved_ss, NULL) < 0) { + log_debug_errno(errno, "Failed to restore signal mask: %m"); + _exit(EXIT_FAILURE); + } + } + + if (flags & FORK_DEATHSIG) { + /* Let's see if the parent PID is still the one we started from? If not, then the parent + * already died by the time we set PR_SET_PDEATHSIG, hence let's emulate the effect */ + + if (getppid() != original_pid) { + log_debug("Parent died early, raising SIGTERM."); + (void) raise(SIGTERM); + _exit(EXIT_FAILURE); + } + } + + if (flags & FORK_CLOSE_ALL_FDS) { + /* Close the logs here in case it got reopened above, as close_all_fds() would close them for us */ + log_close(); + + r = close_all_fds(except_fds, n_except_fds); + if (r < 0) { + log_debug_errno(r, "Failed to close all file descriptors: %m"); + _exit(EXIT_FAILURE); + } + } + + /* When we were asked to reopen the logs, do so again now */ + if (flags & FORK_REOPEN_LOG) { + log_open(); + log_set_open_when_needed(false); + } + + if (flags & FORK_NULL_STDIO) { + r = make_null_stdio(); + if (r < 0) { + log_debug_errno(r, "Failed to connect stdin/stdout to /dev/null: %m"); + _exit(EXIT_FAILURE); + } + } + + if (ret_pid) + *ret_pid = getpid_cached(); + + return 0; +} + +int fork_agent(const char *name, const int except[], unsigned n_except, pid_t *ret_pid, const char *path, ...) { + bool stdout_is_tty, stderr_is_tty; + unsigned n, i; + va_list ap; + char **l; + int r; + + assert(path); + + /* Spawns a temporary TTY agent, making sure it goes away when we go away */ + + r = safe_fork_full(name, except, n_except, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS, ret_pid); + if (r < 0) + return r; + if (r > 0) + return 0; + + /* In the child: */ + + stdout_is_tty = isatty(STDOUT_FILENO); + stderr_is_tty = isatty(STDERR_FILENO); + + if (!stdout_is_tty || !stderr_is_tty) { + int fd; + + /* Detach from stdout/stderr. and reopen + * /dev/tty for them. This is important to + * ensure that when systemctl is started via + * popen() or a similar call that expects to + * read EOF we actually do generate EOF and + * not delay this indefinitely by because we + * keep an unused copy of stdin around. */ + fd = open("/dev/tty", O_WRONLY); + if (fd < 0) { + log_error_errno(errno, "Failed to open /dev/tty: %m"); + _exit(EXIT_FAILURE); + } + + if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) { + log_error_errno(errno, "Failed to dup2 /dev/tty: %m"); + _exit(EXIT_FAILURE); + } + + if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) { + log_error_errno(errno, "Failed to dup2 /dev/tty: %m"); + _exit(EXIT_FAILURE); + } + + if (fd > STDERR_FILENO) + close(fd); + } + + /* Count arguments */ + va_start(ap, path); + for (n = 0; va_arg(ap, char*); n++) + ; + va_end(ap); + + /* Allocate strv */ + l = alloca(sizeof(char *) * (n + 1)); + + /* Fill in arguments */ + va_start(ap, path); + for (i = 0; i <= n; i++) + l[i] = va_arg(ap, char*); + va_end(ap); + + execv(path, l); + _exit(EXIT_FAILURE); +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", diff --git a/src/systemd/src/basic/process-util.h b/src/systemd/src/basic/process-util.h index 92521dba15..545a4c400f 100644 --- a/src/systemd/src/basic/process-util.h +++ b/src/systemd/src/basic/process-util.h @@ -33,6 +33,7 @@ #include "format-util.h" #include "ioprio.h" #include "macro.h" +#include "time-util.h" #define procfs_file_alloca(pid, field) \ ({ \ @@ -41,7 +42,7 @@ if (_pid_ == 0) { \ _r_ = ("/proc/self/" field); \ } else { \ - _r_ = alloca(strlen("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \ + _r_ = alloca(STRLEN("/proc/") + DECIMAL_STR_MAX(pid_t) + 1 + sizeof(field)); \ sprintf((char*) _r_, "/proc/"PID_FMT"/" field, _pid_); \ } \ _r_; \ @@ -61,6 +62,7 @@ int get_process_ppid(pid_t pid, pid_t *ppid); int wait_for_terminate(pid_t pid, siginfo_t *status); int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code); +int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout); void sigkill_wait(pid_t pid); void sigkill_waitp(pid_t *pid); @@ -142,3 +144,19 @@ int ioprio_parse_priority(const char *s, int *ret); pid_t getpid_cached(void); int must_be_root(void); + +typedef enum ForkFlags { + FORK_RESET_SIGNALS = 1U << 0, + FORK_CLOSE_ALL_FDS = 1U << 1, + FORK_DEATHSIG = 1U << 2, + FORK_NULL_STDIO = 1U << 3, + FORK_REOPEN_LOG = 1U << 4, +} ForkFlags; + +int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid); + +static inline int safe_fork(const char *name, ForkFlags flags, pid_t *ret_pid) { + return safe_fork_full(name, NULL, 0, flags, ret_pid); +} + +int fork_agent(const char *name, const int except[], unsigned n_except, pid_t *pid, const char *path, ...); diff --git a/src/systemd/src/basic/socket-util.c b/src/systemd/src/basic/socket-util.c index 2ef38ae282..cdea5c643e 100644 --- a/src/systemd/src/basic/socket-util.c +++ b/src/systemd/src/basic/socket-util.c @@ -58,6 +58,17 @@ # define IDN_FLAGS 0 #endif +static const char* const socket_address_type_table[] = { + [SOCK_STREAM] = "Stream", + [SOCK_DGRAM] = "Datagram", + [SOCK_RAW] = "Raw", + [SOCK_RDM] = "ReliableDatagram", + [SOCK_SEQPACKET] = "SequentialPacket", + [SOCK_DCCP] = "DatagramCongestionControl", +}; + +DEFINE_STRING_TABLE_LOOKUP(socket_address_type, int); + int socket_address_parse(SocketAddress *a, const char *s) { char *e, *n; unsigned u; @@ -125,7 +136,7 @@ int socket_address_parse(SocketAddress *a, const char *s) { } else if (startswith(s, "vsock:")) { /* AF_VSOCK socket in vsock:cid:port notation */ - const char *cid_start = s + strlen("vsock:"); + const char *cid_start = s + STRLEN("vsock:"); e = strchr(cid_start, ':'); if (!e) @@ -531,22 +542,25 @@ bool socket_address_matches_fd(const SocketAddress *a, int fd) { return socket_address_equal(a, &b); } -int sockaddr_port(const struct sockaddr *_sa, unsigned *port) { +int sockaddr_port(const struct sockaddr *_sa, unsigned *ret_port) { union sockaddr_union *sa = (union sockaddr_union*) _sa; + /* Note, this returns the port as 'unsigned' rather than 'uint16_t', as AF_VSOCK knows larger ports */ + assert(sa); switch (sa->sa.sa_family) { + case AF_INET: - *port = be16toh(sa->in.sin_port); + *ret_port = be16toh(sa->in.sin_port); return 0; case AF_INET6: - *port = be16toh(sa->in6.sin6_port); + *ret_port = be16toh(sa->in6.sin6_port); return 0; case AF_VSOCK: - *port = sa->vm.svm_port; + *ret_port = sa->vm.svm_port; return 0; default: diff --git a/src/systemd/src/basic/socket-util.h b/src/systemd/src/basic/socket-util.h index 883f10a7e6..3d9518ab86 100644 --- a/src/systemd/src/basic/socket-util.h +++ b/src/systemd/src/basic/socket-util.h @@ -74,6 +74,9 @@ typedef enum SocketAddressBindIPv6Only { #define socket_address_family(a) ((a)->sockaddr.sa.sa_family) +const char* socket_address_type_to_string(int t) _const_; +int socket_address_type_from_string(const char *s) _pure_; + int socket_address_parse(SocketAddress *a, const char *s); int socket_address_parse_and_warn(SocketAddress *a, const char *s); int socket_address_parse_netlink(SocketAddress *a, const char *s); diff --git a/src/systemd/src/basic/string-util.c b/src/systemd/src/basic/string-util.c index 307c5d2ac8..f30d5cfa0d 100644 --- a/src/systemd/src/basic/string-util.c +++ b/src/systemd/src/basic/string-util.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -675,10 +676,10 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) { if (!f) return NULL; - /* Note we use the _unlocked() stdio variants on f for performance - * reasons. It's safe to do so since we created f here and it - * doesn't leave our scope. - */ + /* Note we turn off internal locking on f for performance reasons. It's safe to do so since we created f here + * and it doesn't leave our scope. */ + + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); for (i = *ibuf; i < *ibuf + isz + 1; i++) { @@ -690,21 +691,21 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) { else if (*i == '\x1B') state = STATE_ESCAPE; else if (*i == '\t') - fputs_unlocked(" ", f); + fputs(" ", f); else - fputc_unlocked(*i, f); + fputc(*i, f); break; case STATE_ESCAPE: if (i >= *ibuf + isz) { /* EOT */ - fputc_unlocked('\x1B', f); + fputc('\x1B', f); break; } else if (*i == '[') { state = STATE_BRACKET; begin = i + 1; } else { - fputc_unlocked('\x1B', f); - fputc_unlocked(*i, f); + fputc('\x1B', f); + fputc(*i, f); state = STATE_OTHER; } @@ -714,8 +715,8 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) { if (i >= *ibuf + isz || /* EOT */ (!(*i >= '0' && *i <= '9') && !IN_SET(*i, ';', 'm'))) { - fputc_unlocked('\x1B', f); - fputc_unlocked('[', f); + fputc('\x1B', f); + fputc('[', f); state = STATE_OTHER; i = begin-1; } else if (*i == 'm') diff --git a/src/systemd/src/basic/time-util.c b/src/systemd/src/basic/time-util.c index 706267e721..1c9954e630 100644 --- a/src/systemd/src/basic/time-util.c +++ b/src/systemd/src/basic/time-util.c @@ -891,8 +891,8 @@ typedef struct ParseTimestampResult { int parse_timestamp(const char *t, usec_t *usec) { char *last_space, *tz = NULL; ParseTimestampResult *shared, tmp; - int r; pid_t pid; + int r; last_space = strrchr(t, ' '); if (last_space != NULL && timezone_is_valid(last_space + 1)) @@ -905,15 +905,12 @@ int parse_timestamp(const char *t, usec_t *usec) { if (shared == MAP_FAILED) return negative_errno(); - pid = fork(); - - if (pid == -1) { - int fork_errno = errno; + r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG, &pid); + if (r < 0) { (void) munmap(shared, sizeof *shared); - return -fork_errno; + return r; } - - if (pid == 0) { + if (r == 0) { bool with_tz = true; if (setenv("TZ", tz, 1) != 0) { diff --git a/src/systemd/src/basic/util.c b/src/systemd/src/basic/util.c index 9f12f52b29..1cd042e41b 100644 --- a/src/systemd/src/basic/util.c +++ b/src/systemd/src/basic/util.c @@ -110,7 +110,7 @@ int socket_from_display(const char *display, char **path) { k = strspn(display+1, "0123456789"); - f = new(char, strlen("/tmp/.X11-unix/X") + k + 1); + f = new(char, STRLEN("/tmp/.X11-unix/X") + k + 1); if (!f) return -ENOMEM; @@ -123,45 +123,6 @@ int socket_from_display(const char *display, char **path) { return 0; } -int block_get_whole_disk(dev_t d, dev_t *ret) { - char p[SYS_BLOCK_PATH_MAX("/partition")]; - _cleanup_free_ char *s = NULL; - int r; - unsigned n, m; - - assert(ret); - - /* If it has a queue this is good enough for us */ - xsprintf_sys_block_path(p, "/queue", d); - if (access(p, F_OK) >= 0) { - *ret = d; - return 0; - } - - /* If it is a partition find the originating device */ - xsprintf_sys_block_path(p, "/partition", d); - if (access(p, F_OK) < 0) - return -ENOENT; - - /* Get parent dev_t */ - xsprintf_sys_block_path(p, "/../dev", d); - r = read_one_line_file(p, &s); - if (r < 0) - return r; - - r = sscanf(s, "%u:%u", &m, &n); - if (r != 2) - return -EINVAL; - - /* Only return this if it is really good enough for us. */ - xsprintf_sys_block_path(p, "/queue", makedev(m, n)); - if (access(p, F_OK) < 0) - return -ENOENT; - - *ret = makedev(m, n); - return 0; -} - bool kexec_loaded(void) { _cleanup_free_ char *s = NULL; @@ -189,112 +150,6 @@ int prot_from_flags(int flags) { } } -int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...) { - bool stdout_is_tty, stderr_is_tty; - pid_t parent_pid, agent_pid; - sigset_t ss, saved_ss; - unsigned n, i; - va_list ap; - char **l; - - assert(pid); - assert(path); - - /* Spawns a temporary TTY agent, making sure it goes away when - * we go away */ - - parent_pid = getpid_cached(); - - /* First we temporarily block all signals, so that the new - * child has them blocked initially. This way, we can be sure - * that SIGTERMs are not lost we might send to the agent. */ - assert_se(sigfillset(&ss) >= 0); - assert_se(sigprocmask(SIG_SETMASK, &ss, &saved_ss) >= 0); - - agent_pid = fork(); - if (agent_pid < 0) { - assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0); - return -errno; - } - - if (agent_pid != 0) { - assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0); - *pid = agent_pid; - return 0; - } - - /* In the child: - * - * Make sure the agent goes away when the parent dies */ - if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) - _exit(EXIT_FAILURE); - - /* Make sure we actually can kill the agent, if we need to, in - * case somebody invoked us from a shell script that trapped - * SIGTERM or so... */ - (void) reset_all_signal_handlers(); - (void) reset_signal_mask(); - - /* Check whether our parent died before we were able - * to set the death signal and unblock the signals */ - if (getppid() != parent_pid) - _exit(EXIT_SUCCESS); - - /* Don't leak fds to the agent */ - close_all_fds(except, n_except); - - stdout_is_tty = isatty(STDOUT_FILENO); - stderr_is_tty = isatty(STDERR_FILENO); - - if (!stdout_is_tty || !stderr_is_tty) { - int fd; - - /* Detach from stdout/stderr. and reopen - * /dev/tty for them. This is important to - * ensure that when systemctl is started via - * popen() or a similar call that expects to - * read EOF we actually do generate EOF and - * not delay this indefinitely by because we - * keep an unused copy of stdin around. */ - fd = open("/dev/tty", O_WRONLY); - if (fd < 0) { - log_error_errno(errno, "Failed to open /dev/tty: %m"); - _exit(EXIT_FAILURE); - } - - if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) { - log_error_errno(errno, "Failed to dup2 /dev/tty: %m"); - _exit(EXIT_FAILURE); - } - - if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) { - log_error_errno(errno, "Failed to dup2 /dev/tty: %m"); - _exit(EXIT_FAILURE); - } - - if (fd > STDERR_FILENO) - close(fd); - } - - /* Count arguments */ - va_start(ap, path); - for (n = 0; va_arg(ap, char*); n++) - ; - va_end(ap); - - /* Allocate strv */ - l = alloca(sizeof(char *) * (n + 1)); - - /* Fill in arguments */ - va_start(ap, path); - for (i = 0; i <= n; i++) - l[i] = va_arg(ap, char*); - va_end(ap); - - execv(path, l); - _exit(EXIT_FAILURE); -} - bool in_initrd(void) { struct statfs s; @@ -704,132 +559,65 @@ int version(void) { return 0; } -int get_block_device(const char *path, dev_t *dev) { - struct stat st; - struct statfs sfs; - - assert(path); - assert(dev); - - /* Get's the block device directly backing a file system. If - * the block device is encrypted, returns the device mapper - * block device. */ - - if (lstat(path, &st)) - return -errno; - - if (major(st.st_dev) != 0) { - *dev = st.st_dev; - return 1; - } - - if (statfs(path, &sfs) < 0) - return -errno; - - if (F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC)) - return btrfs_get_block_device(path, dev); - - return 0; +/* This is a direct translation of str_verscmp from boot.c */ +static bool is_digit(int c) { + return c >= '0' && c <= '9'; } -int get_block_device_harder(const char *path, dev_t *dev) { - _cleanup_closedir_ DIR *d = NULL; - _cleanup_free_ char *t = NULL; - char p[SYS_BLOCK_PATH_MAX("/slaves")]; - struct dirent *de, *found = NULL; - const char *q; - unsigned maj, min; - dev_t dt; - int r; +static int c_order(int c) { + if (c == 0 || is_digit(c)) + return 0; - assert(path); - assert(dev); + if ((c >= 'a') && (c <= 'z')) + return c; - /* Gets the backing block device for a file system, and - * handles LUKS encrypted file systems, looking for its - * immediate parent, if there is one. */ + return c + 0x10000; +} - r = get_block_device(path, &dt); - if (r <= 0) - return r; +int str_verscmp(const char *s1, const char *s2) { + const char *os1, *os2; - xsprintf_sys_block_path(p, "/slaves", dt); - d = opendir(p); - if (!d) { - if (errno == ENOENT) - goto fallback; + assert(s1); + assert(s2); - return -errno; - } + os1 = s1; + os2 = s2; - FOREACH_DIRENT_ALL(de, d, return -errno) { + while (*s1 || *s2) { + int first; - if (dot_or_dot_dot(de->d_name)) - continue; + while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) { + int order; - if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN)) - continue; - - if (found) { - _cleanup_free_ char *u = NULL, *v = NULL, *a = NULL, *b = NULL; - - /* We found a device backed by multiple other devices. We don't really support automatic - * discovery on such setups, with the exception of dm-verity partitions. In this case there are - * two backing devices: the data partition and the hash partition. We are fine with such - * setups, however, only if both partitions are on the same physical device. Hence, let's - * verify this. */ - - u = strjoin(p, "/", de->d_name, "/../dev"); - if (!u) - return -ENOMEM; - - v = strjoin(p, "/", found->d_name, "/../dev"); - if (!v) - return -ENOMEM; - - r = read_one_line_file(u, &a); - if (r < 0) { - log_debug_errno(r, "Failed to read %s: %m", u); - goto fallback; - } - - r = read_one_line_file(v, &b); - if (r < 0) { - log_debug_errno(r, "Failed to read %s: %m", v); - goto fallback; - } - - /* Check if the parent device is the same. If not, then the two backing devices are on - * different physical devices, and we don't support that. */ - if (!streq(a, b)) - goto fallback; + order = c_order(*s1) - c_order(*s2); + if (order != 0) + return order; + s1++; + s2++; } - found = de; + while (*s1 == '0') + s1++; + while (*s2 == '0') + s2++; + + first = 0; + while (is_digit(*s1) && is_digit(*s2)) { + if (first == 0) + first = *s1 - *s2; + s1++; + s2++; + } + + if (is_digit(*s1)) + return 1; + if (is_digit(*s2)) + return -1; + + if (first != 0) + return first; } - if (!found) - goto fallback; - - q = strjoina(p, "/", found->d_name, "/dev"); - - r = read_one_line_file(q, &t); - if (r == -ENOENT) - goto fallback; - if (r < 0) - return r; - - if (sscanf(t, "%u:%u", &maj, &min) != 2) - return -EINVAL; - - if (maj == 0) - goto fallback; - - *dev = makedev(maj, min); - return 1; - -fallback: - *dev = dt; - return 1; + return strcmp(os1, os2); } #endif /* NM_IGNORED */ diff --git a/src/systemd/src/basic/util.h b/src/systemd/src/basic/util.h index a79907de3e..20181ab917 100644 --- a/src/systemd/src/basic/util.h +++ b/src/systemd/src/basic/util.h @@ -71,8 +71,6 @@ bool plymouth_running(void); bool display_is_local(const char *display) _pure_; int socket_from_display(const char *display, char **path); -int block_get_whole_disk(dev_t d, dev_t *ret); - #define NULSTR_FOREACH(i, l) \ for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1) @@ -86,8 +84,6 @@ bool kexec_loaded(void); int prot_from_flags(int flags) _const_; -int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...); - bool in_initrd(void); void in_initrd_force(bool value); @@ -194,5 +190,4 @@ int update_reboot_parameter_and_warn(const char *param); int version(void); -int get_block_device(const char *path, dev_t *dev); -int get_block_device_harder(const char *path, dev_t *dev); +int str_verscmp(const char *s1, const char *s2); diff --git a/src/systemd/src/libsystemd-network/network-internal.c b/src/systemd/src/libsystemd-network/network-internal.c index ddc836b55b..c7894cc205 100644 --- a/src/systemd/src/libsystemd-network/network-internal.c +++ b/src/systemd/src/libsystemd-network/network-internal.c @@ -119,7 +119,8 @@ bool net_match_config(const struct ether_addr *match_mac, char * const *match_names, Condition *match_host, Condition *match_virt, - Condition *match_kernel, + Condition *match_kernel_cmdline, + Condition *match_kernel_version, Condition *match_arch, const struct ether_addr *dev_mac, const char *dev_path, @@ -134,7 +135,10 @@ bool net_match_config(const struct ether_addr *match_mac, if (match_virt && condition_test(match_virt) <= 0) return false; - if (match_kernel && condition_test(match_kernel) <= 0) + if (match_kernel_cmdline && condition_test(match_kernel_cmdline) <= 0) + return false; + + if (match_kernel_version && condition_test(match_kernel_version) <= 0) return false; if (match_arch && condition_test(match_arch) <= 0) diff --git a/src/systemd/src/libsystemd-network/network-internal.h b/src/systemd/src/libsystemd-network/network-internal.h index a54adac602..4e69f1a598 100644 --- a/src/systemd/src/libsystemd-network/network-internal.h +++ b/src/systemd/src/libsystemd-network/network-internal.h @@ -37,7 +37,8 @@ bool net_match_config(const struct ether_addr *match_mac, char * const *match_name, Condition *match_host, Condition *match_virt, - Condition *match_kernel, + Condition *match_kernel_cmdline, + Condition *match_kernel_version, Condition *match_arch, const struct ether_addr *dev_mac, const char *dev_path, diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c index d1be3a7f5f..4bed074fba 100644 --- a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -878,7 +879,8 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { if (r < 0) goto fail; - fchmod(fileno(f), 0644); + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + (void) fchmod(fileno(f), 0644); fprintf(f, "# This is private data. Do not parse.\n"); @@ -925,16 +927,16 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { r = sd_dhcp_lease_get_dns(lease, &addresses); if (r > 0) { - fputs_unlocked("DNS=", f); + fputs("DNS=", f); serialize_in_addrs(f, addresses, r); - fputs_unlocked("\n", f); + fputs("\n", f); } r = sd_dhcp_lease_get_ntp(lease, &addresses); if (r > 0) { - fputs_unlocked("NTP=", f); + fputs("NTP=", f); serialize_in_addrs(f, addresses, r); - fputs_unlocked("\n", f); + fputs("\n", f); } r = sd_dhcp_lease_get_domainname(lease, &string); @@ -943,9 +945,9 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { r = sd_dhcp_lease_get_search_domains(lease, &search_domains); if (r > 0) { - fputs_unlocked("DOMAIN_SEARCH_LIST=", f); + fputs("DOMAIN_SEARCH_LIST=", f); fputstrv(f, search_domains, NULL, NULL); - fputs_unlocked("\n", f); + fputs("\n", f); } r = sd_dhcp_lease_get_hostname(lease, &string);