diff options
Diffstat (limited to 'src/shared')
56 files changed, 2581 insertions, 2307 deletions
diff --git a/src/shared/acl-util.c b/src/shared/acl-util.c index bd8c988751..35f2e1b67d 100644 --- a/src/shared/acl-util.c +++ b/src/shared/acl-util.c @@ -23,8 +23,11 @@ #include <stdbool.h> #include "acl-util.h" -#include "util.h" +#include "alloc-util.h" +#include "string-util.h" #include "strv.h" +#include "user-util.h" +#include "util.h" int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *entry) { acl_entry_t i; diff --git a/src/shared/acl-util.h b/src/shared/acl-util.h index cf612e8722..256a6a5900 100644 --- a/src/shared/acl-util.h +++ b/src/shared/acl-util.h @@ -23,9 +23,9 @@ #ifdef HAVE_ACL +#include <acl/libacl.h> #include <stdbool.h> #include <sys/acl.h> -#include <acl/libacl.h> #include "macro.h" diff --git a/src/shared/acpi-fpdt.c b/src/shared/acpi-fpdt.c index 64e50401b9..30e03c0652 100644 --- a/src/shared/acpi-fpdt.c +++ b/src/shared/acpi-fpdt.c @@ -19,16 +19,18 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdio.h> +#include <fcntl.h> #include <stdint.h> +#include <stdio.h> #include <string.h> #include <unistd.h> -#include <fcntl.h> -#include <util.h> -#include <fileio.h> -#include <time-util.h> -#include <acpi-fpdt.h> +#include "acpi-fpdt.h" +#include "alloc-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "time-util.h" +#include "util.h" struct acpi_table_header { char signature[4]; diff --git a/src/shared/apparmor-util.c b/src/shared/apparmor-util.c index c2bbd330bd..f6ac43adfe 100644 --- a/src/shared/apparmor-util.c +++ b/src/shared/apparmor-util.c @@ -19,10 +19,11 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ - -#include "util.h" -#include "fileio.h" +#include "alloc-util.h" #include "apparmor-util.h" +#include "fileio.h" +#include "parse-util.h" +#include "util.h" bool mac_apparmor_use(void) { static int cached_use = -1; diff --git a/src/shared/architecture.c b/src/shared/architecture.c index 8e72e7a36a..73937bd5a7 100644 --- a/src/shared/architecture.c +++ b/src/shared/architecture.c @@ -22,6 +22,8 @@ #include <sys/utsname.h> #include "architecture.h" +#include "string-table.h" +#include "string-util.h" int uname_architecture(void) { diff --git a/src/shared/architecture.h b/src/shared/architecture.h index f5bbf65a90..61d067cad7 100644 --- a/src/shared/architecture.h +++ b/src/shared/architecture.h @@ -78,9 +78,11 @@ int uname_architecture(void); #if defined(__x86_64__) # define native_architecture() ARCHITECTURE_X86_64 # define LIB_ARCH_TUPLE "x86_64-linux-gnu" +# define PROC_CPUINFO_MODEL "model name" #elif defined(__i386__) # define native_architecture() ARCHITECTURE_X86 # define LIB_ARCH_TUPLE "i386-linux-gnu" +# define PROC_CPUINFO_MODEL "model name" #elif defined(__powerpc64__) # if __BYTE_ORDER == __BIG_ENDIAN # define native_architecture() ARCHITECTURE_PPC64 @@ -89,6 +91,7 @@ int uname_architecture(void); # define native_architecture() ARCHITECTURE_PPC64_LE # define LIB_ARCH_TUPLE "powerpc64le-linux-gnu" # endif +# define PROC_CPUINFO_MODEL "cpu" #elif defined(__powerpc__) # if __BYTE_ORDER == __BIG_ENDIAN # define native_architecture() ARCHITECTURE_PPC @@ -97,15 +100,18 @@ int uname_architecture(void); # define native_architecture() ARCHITECTURE_PPC_LE # error "Missing LIB_ARCH_TUPLE for PPCLE" # endif +# define PROC_CPUINFO_MODEL "cpu" #elif defined(__ia64__) # define native_architecture() ARCHITECTURE_IA64 # define LIB_ARCH_TUPLE "ia64-linux-gnu" #elif defined(__hppa64__) # define native_architecture() ARCHITECTURE_PARISC64 # error "Missing LIB_ARCH_TUPLE for HPPA64" +# define PROC_CPUINFO_MODEL "cpu" #elif defined(__hppa__) # define native_architecture() ARCHITECTURE_PARISC # define LIB_ARCH_TUPLE "hppa‑linux‑gnu" +# define PROC_CPUINFO_MODEL "cpu" #elif defined(__s390x__) # define native_architecture() ARCHITECTURE_S390X # define LIB_ARCH_TUPLE "s390x-linux-gnu" @@ -115,9 +121,11 @@ int uname_architecture(void); #elif defined(__sparc64__) # define native_architecture() ARCHITECTURE_SPARC64 # define LIB_ARCH_TUPLE "sparc64-linux-gnu" +# define PROC_CPUINFO_MODEL "cpu" #elif defined(__sparc__) # define native_architecture() ARCHITECTURE_SPARC # define LIB_ARCH_TUPLE "sparc-linux-gnu" +# define PROC_CPUINFO_MODEL "cpu" #elif defined(__mips64__) # if __BYTE_ORDER == __BIG_ENDIAN # define native_architecture() ARCHITECTURE_MIPS64 @@ -126,6 +134,7 @@ int uname_architecture(void); # define native_architecture() ARCHITECTURE_MIPS64_LE # error "Missing LIB_ARCH_TUPLE for MIPS64_LE" # endif +# define PROC_CPUINFO_MODEL "cpu model" #elif defined(__mips__) # if __BYTE_ORDER == __BIG_ENDIAN # define native_architecture() ARCHITECTURE_MIPS @@ -134,6 +143,7 @@ int uname_architecture(void); # define native_architecture() ARCHITECTURE_MIPS_LE # define LIB_ARCH_TUPLE "mipsel-linux-gnu" # endif +# define PROC_CPUINFO_MODEL "cpu model" #elif defined(__alpha__) # define native_architecture() ARCHITECTURE_ALPHA # define LIB_ARCH_TUPLE "alpha-linux-gnu" @@ -169,6 +179,7 @@ int uname_architecture(void); # define LIB_ARCH_TUPLE "arm-linux-gnu" # endif # endif +# define PROC_CPUINFO_MODEL "model name" #elif defined(__sh64__) # define native_architecture() ARCHITECTURE_SH64 # error "Missing LIB_ARCH_TUPLE for SH64" @@ -188,5 +199,10 @@ int uname_architecture(void); # error "Please register your architecture here!" #endif +#ifndef PROC_CPUINFO_MODEL +#warning "PROC_CPUINFO_MODEL not defined for your architecture" +#define PROC_CPUINFO_MODEL "model name" +#endif + const char *architecture_to_string(int a) _const_; int architecture_from_string(const char *s) _pure_; diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c index f5cff6fc56..fbe2b6fecb 100644 --- a/src/shared/ask-password-api.c +++ b/src/shared/ask-password-api.c @@ -18,27 +18,167 @@ You should have received a copy of the GNU Lesser General Public License along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdbool.h> -#include <termios.h> -#include <unistd.h> -#include <poll.h> -#include <sys/inotify.h> + #include <errno.h> #include <fcntl.h> -#include <sys/socket.h> -#include <string.h> -#include <sys/un.h> +#include <poll.h> +#include <stdbool.h> #include <stddef.h> +#include <string.h> +#include <sys/inotify.h> #include <sys/signalfd.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <termios.h> +#include <unistd.h> -#include "util.h" +#include "alloc-util.h" +#include "ask-password-api.h" +#include "fd-util.h" +#include "fileio.h" #include "formats-util.h" +#include "io-util.h" +#include "missing.h" #include "mkdir.h" -#include "strv.h" #include "random-util.h" -#include "terminal-util.h" #include "signal-util.h" -#include "ask-password-api.h" +#include "socket-util.h" +#include "string-util.h" +#include "strv.h" +#include "terminal-util.h" +#include "umask-util.h" +#include "util.h" + +#define KEYRING_TIMEOUT_USEC ((5 * USEC_PER_MINUTE) / 2) + +static int lookup_key(const char *keyname, key_serial_t *ret) { + key_serial_t serial; + + assert(keyname); + assert(ret); + + serial = request_key("user", keyname, NULL, 0); + if (serial == -1) + return -errno; + + *ret = serial; + return 0; +} + +static int retrieve_key(key_serial_t serial, char ***ret) { + _cleanup_free_ char *p = NULL; + long m = 100, n; + char **l; + + assert(ret); + + for (;;) { + p = new(char, m); + if (!p) + return -ENOMEM; + + n = keyctl(KEYCTL_READ, (unsigned long) serial, (unsigned long) p, (unsigned long) m, 0); + if (n < 0) + return -errno; + + if (n < m) + break; + + memory_erase(p, n); + free(p); + m *= 2; + } + + l = strv_parse_nulstr(p, n); + if (!l) + return -ENOMEM; + + memory_erase(p, n); + + *ret = l; + return 0; +} + +static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **passwords) { + _cleanup_strv_free_erase_ char **l = NULL; + _cleanup_free_ char *p = NULL; + key_serial_t serial; + size_t n; + int r; + + assert(keyname); + assert(passwords); + + if (!(flags & ASK_PASSWORD_PUSH_CACHE)) + return 0; + + r = lookup_key(keyname, &serial); + if (r >= 0) { + r = retrieve_key(serial, &l); + if (r < 0) + return r; + } else if (r != -ENOKEY) + return r; + + r = strv_extend_strv(&l, passwords, true); + if (r <= 0) + return r; + + r = strv_make_nulstr(l, &p, &n); + if (r < 0) + return r; + + /* Truncate trailing NUL */ + assert(n > 0); + assert(p[n-1] == 0); + + serial = add_key("user", keyname, p, n-1, KEY_SPEC_USER_KEYRING); + memory_erase(p, n); + if (serial == -1) + return -errno; + + if (keyctl(KEYCTL_SET_TIMEOUT, + (unsigned long) serial, + (unsigned long) DIV_ROUND_UP(KEYRING_TIMEOUT_USEC, USEC_PER_SEC), 0, 0) < 0) + log_debug_errno(errno, "Failed to adjust timeout: %m"); + + log_debug("Added key to keyring as %" PRIi32 ".", serial); + + return 1; +} + +static int add_to_keyring_and_log(const char *keyname, AskPasswordFlags flags, char **passwords) { + int r; + + assert(keyname); + assert(passwords); + + r = add_to_keyring(keyname, flags, passwords); + if (r < 0) + return log_debug_errno(r, "Failed to add password to keyring: %m"); + + return 0; +} + +int ask_password_keyring(const char *keyname, AskPasswordFlags flags, char ***ret) { + + key_serial_t serial; + int r; + + assert(keyname); + assert(ret); + + if (!(flags & ASK_PASSWORD_ACCEPT_CACHED)) + return -EUNATCH; + + r = lookup_key(keyname, &serial); + if (r == -ENOSYS) /* when retrieving the distinction doesn't matter */ + return -ENOKEY; + if (r < 0) + return r; + + return retrieve_key(serial, ret); +} static void backspace_chars(int ttyfd, size_t p) { @@ -54,10 +194,11 @@ static void backspace_chars(int ttyfd, size_t p) { int ask_password_tty( const char *message, + const char *keyname, usec_t until, - bool echo, + AskPasswordFlags flags, const char *flag_file, - char **_passphrase) { + char **ret) { struct termios old_termios, new_termios; char passphrase[LINE_MAX], *x; @@ -66,15 +207,19 @@ int ask_password_tty( _cleanup_close_ int ttyfd = -1, notify = -1; struct pollfd pollfd[2]; bool reset_tty = false; - bool silent_mode = false; bool dirty = false; enum { POLL_TTY, POLL_INOTIFY }; - assert(message); - assert(_passphrase); + assert(ret); + + if (flags & ASK_PASSWORD_NO_TTY) + return -EUNATCH; + + if (!message) + message = "Password:"; if (flag_file) { notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK); @@ -97,10 +242,10 @@ int ask_password_tty( goto finish; } - loop_write(ttyfd, ANSI_HIGHLIGHT_ON, sizeof(ANSI_HIGHLIGHT_ON)-1, false); + loop_write(ttyfd, ANSI_HIGHLIGHT, strlen(ANSI_HIGHLIGHT), false); loop_write(ttyfd, message, strlen(message), false); loop_write(ttyfd, " ", 1, false); - loop_write(ttyfd, ANSI_HIGHLIGHT_OFF, sizeof(ANSI_HIGHLIGHT_OFF)-1, false); + loop_write(ttyfd, ANSI_NORMAL, strlen(ANSI_NORMAL), false); new_termios = old_termios; new_termios.c_lflag &= ~(ICANON|ECHO); @@ -145,7 +290,7 @@ int ask_password_tty( goto finish; } - k = poll(pollfd, notify > 0 ? 2 : 1, sleep_for); + k = poll(pollfd, notify >= 0 ? 2 : 1, sleep_for); if (k < 0) { if (errno == EINTR) continue; @@ -157,7 +302,7 @@ int ask_password_tty( goto finish; } - if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0) + if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0) flush_fd(notify); if (pollfd[POLL_TTY].revents == 0) @@ -178,7 +323,7 @@ int ask_password_tty( break; else if (c == 21) { /* C-u */ - if (!silent_mode) + if (!(flags & ASK_PASSWORD_SILENT)) backspace_chars(ttyfd, p); p = 0; @@ -186,28 +331,28 @@ int ask_password_tty( if (p > 0) { - if (!silent_mode) + if (!(flags & ASK_PASSWORD_SILENT)) backspace_chars(ttyfd, 1); p--; - } else if (!dirty && !silent_mode) { + } else if (!dirty && !(flags & ASK_PASSWORD_SILENT)) { - silent_mode = true; + flags |= ASK_PASSWORD_SILENT; /* There are two ways to enter silent * mode. Either by pressing backspace - * as first key (and only as first key), - * or ... */ + * as first key (and only as first + * key), or ... */ if (ttyfd >= 0) loop_write(ttyfd, "(no echo) ", 10, false); } else if (ttyfd >= 0) loop_write(ttyfd, "\a", 1, false); - } else if (c == '\t' && !silent_mode) { + } else if (c == '\t' && !(flags & ASK_PASSWORD_SILENT)) { backspace_chars(ttyfd, p); - silent_mode = true; + flags |= ASK_PASSWORD_SILENT; /* ... or by pressing TAB at any time. */ @@ -221,20 +366,26 @@ int ask_password_tty( passphrase[p++] = c; - if (!silent_mode && ttyfd >= 0) - loop_write(ttyfd, echo ? &c : "*", 1, false); + if (!(flags & ASK_PASSWORD_SILENT) && ttyfd >= 0) + loop_write(ttyfd, (flags & ASK_PASSWORD_ECHO) ? &c : "*", 1, false); dirty = true; } + + c = 'x'; } x = strndup(passphrase, p); + memory_erase(passphrase, p); if (!x) { r = -ENOMEM; goto finish; } - *_passphrase = x; + if (keyname) + (void) add_to_keyring_and_log(keyname, flags, STRV_MAKE(x)); + + *ret = x; r = 0; finish: @@ -247,52 +398,38 @@ finish: } static int create_socket(char **name) { - int fd; - union { - struct sockaddr sa; - struct sockaddr_un un; - } sa = { + union sockaddr_union sa = { .un.sun_family = AF_UNIX, }; - int one = 1; - int r = 0; + _cleanup_close_ int fd = -1; + static const int one = 1; char *c; + int r; assert(name); fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); if (fd < 0) - return log_error_errno(errno, "socket() failed: %m"); + return -errno; snprintf(sa.un.sun_path, sizeof(sa.un.sun_path)-1, "/run/systemd/ask-password/sck.%" PRIx64, random_u64()); RUN_WITH_UMASK(0177) { - r = bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)); - } - - if (r < 0) { - r = -errno; - log_error_errno(errno, "bind(%s) failed: %m", sa.un.sun_path); - goto fail; + if (bind(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) + return -errno; } - if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) { - r = -errno; - log_error_errno(errno, "SO_PASSCRED failed: %m"); - goto fail; - } + if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) + return -errno; c = strdup(sa.un.sun_path); - if (!c) { - r = log_oom(); - goto fail; - } + if (!c) + return -ENOMEM; *name = c; - return fd; -fail: - safe_close(fd); + r = fd; + fd = -1; return r; } @@ -301,10 +438,10 @@ int ask_password_agent( const char *message, const char *icon, const char *id, + const char *keyname, usec_t until, - bool echo, - bool accept_cached, - char ***_passphrases) { + AskPasswordFlags flags, + char ***ret) { enum { FD_SOCKET, @@ -312,35 +449,38 @@ int ask_password_agent( _FD_MAX }; + _cleanup_close_ int socket_fd = -1, signal_fd = -1, fd = -1; char temp[] = "/run/systemd/ask-password/tmp.XXXXXX"; char final[sizeof(temp)] = ""; - _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *socket_name = NULL; - _cleanup_close_ int socket_fd = -1, signal_fd = -1, fd = -1; - sigset_t mask, oldmask; + _cleanup_strv_free_ char **l = NULL; + _cleanup_fclose_ FILE *f = NULL; struct pollfd pollfd[_FD_MAX]; + sigset_t mask, oldmask; int r; - assert(_passphrases); + assert(ret); + + if (flags & ASK_PASSWORD_NO_AGENT) + return -EUNATCH; assert_se(sigemptyset(&mask) >= 0); assert_se(sigset_add_many(&mask, SIGINT, SIGTERM, -1) >= 0); assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) >= 0); - mkdir_p_label("/run/systemd/ask-password", 0755); + (void) mkdir_p_label("/run/systemd/ask-password", 0755); fd = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC); if (fd < 0) { - r = log_error_errno(errno, - "Failed to create password file: %m"); + r = fd; goto finish; } - fchmod(fd, 0644); + (void) fchmod(fd, 0644); f = fdopen(fd, "w"); if (!f) { - r = log_error_errno(errno, "Failed to allocate FILE: %m"); + r = -errno; goto finish; } @@ -348,7 +488,7 @@ int ask_password_agent( signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC); if (signal_fd < 0) { - r = log_error_errno(errno, "signalfd(): %m"); + r = -errno; goto finish; } @@ -367,8 +507,8 @@ int ask_password_agent( "NotAfter="USEC_FMT"\n", getpid(), socket_name, - accept_cached ? 1 : 0, - echo ? 1 : 0, + (flags & ASK_PASSWORD_ACCEPT_CACHED) ? 1 : 0, + (flags & ASK_PASSWORD_ECHO) ? 1 : 0, until); if (message) @@ -381,10 +521,8 @@ int ask_password_agent( fprintf(f, "Id=%s\n", id); r = fflush_and_check(f); - if (r < 0) { - log_error_errno(r, "Failed to write query file: %m"); + if (r < 0) goto finish; - } memcpy(final, temp, sizeof(temp)); @@ -393,7 +531,7 @@ int ask_password_agent( final[sizeof(final)-9] = 'k'; if (rename(temp, final) < 0) { - r = log_error_errno(errno, "Failed to rename query file: %m"); + r = -errno; goto finish; } @@ -419,7 +557,6 @@ int ask_password_agent( t = now(CLOCK_MONOTONIC); if (until > 0 && until <= t) { - log_notice("Timed out"); r = -ETIME; goto finish; } @@ -429,12 +566,11 @@ int ask_password_agent( if (errno == EINTR) continue; - r = log_error_errno(errno, "poll() failed: %m"); + r = -errno; goto finish; } if (k <= 0) { - log_notice("Timed out"); r = -ETIME; goto finish; } @@ -445,7 +581,6 @@ int ask_password_agent( } if (pollfd[FD_SOCKET].revents != POLLIN) { - log_error("Unexpected poll() event."); r = -EIO; goto finish; } @@ -467,14 +602,14 @@ int ask_password_agent( errno == EINTR) continue; - r = log_error_errno(errno, "recvmsg() failed: %m"); + r = -errno; goto finish; } cmsg_close_all(&msghdr); if (n <= 0) { - log_error("Message too short"); + log_debug("Message too short"); continue; } @@ -482,84 +617,104 @@ int ask_password_agent( control.cmsghdr.cmsg_level != SOL_SOCKET || control.cmsghdr.cmsg_type != SCM_CREDENTIALS || control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) { - log_warning("Received message without credentials. Ignoring."); + log_debug("Received message without credentials. Ignoring."); continue; } ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr); if (ucred->uid != 0) { - log_warning("Got request from unprivileged user. Ignoring."); + log_debug("Got request from unprivileged user. Ignoring."); continue; } if (passphrase[0] == '+') { - char **l; - + /* An empty message refers to the empty password */ if (n == 1) l = strv_new("", NULL); else l = strv_parse_nulstr(passphrase+1, n-1); - /* An empty message refers to the empty password */ - + memory_erase(passphrase, n); if (!l) { r = -ENOMEM; goto finish; } if (strv_length(l) <= 0) { - strv_free(l); - log_error("Invalid packet"); + l = strv_free(l); + log_debug("Invalid packet"); continue; } - *_passphrases = l; + break; + } - } else if (passphrase[0] == '-') { + if (passphrase[0] == '-') { r = -ECANCELED; goto finish; - } else { - log_error("Invalid packet"); - continue; } - break; + log_debug("Invalid packet"); } + if (keyname) + (void) add_to_keyring_and_log(keyname, flags, l); + + *ret = l; + l = NULL; r = 0; finish: if (socket_name) - unlink(socket_name); + (void) unlink(socket_name); - unlink(temp); + (void) unlink(temp); if (final[0]) - unlink(final); + (void) unlink(final); assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) == 0); - return r; } -int ask_password_auto(const char *message, const char *icon, const char *id, - usec_t until, bool accept_cached, char ***_passphrases) { - assert(message); - assert(_passphrases); +int ask_password_auto( + const char *message, + const char *icon, + const char *id, + const char *keyname, + usec_t until, + AskPasswordFlags flags, + char ***ret) { - if (isatty(STDIN_FILENO)) { - int r; - char *s = NULL, **l = NULL; + int r; - r = ask_password_tty(message, until, false, NULL, &s); - if (r < 0) + assert(ret); + + if ((flags & ASK_PASSWORD_ACCEPT_CACHED) && keyname) { + r = ask_password_keyring(keyname, flags, ret); + if (r != -ENOKEY) return r; + } - r = strv_consume(&l, s); + if (!(flags & ASK_PASSWORD_NO_TTY) && isatty(STDIN_FILENO)) { + char *s = NULL, **l = NULL; + + r = ask_password_tty(message, keyname, until, flags, NULL, &s); if (r < 0) return r; - *_passphrases = l; - return r; - } else - return ask_password_agent(message, icon, id, until, false, accept_cached, _passphrases); + r = strv_push(&l, s); + if (r < 0) { + string_erase(s); + free(s); + return -ENOMEM; + } + + *ret = l; + return 0; + } + + if (!(flags & ASK_PASSWORD_NO_AGENT)) + return ask_password_agent(message, icon, id, keyname, until, flags, ret); + + return -EUNATCH; } diff --git a/src/shared/ask-password-api.h b/src/shared/ask-password-api.h index 0954e072be..913cad9f8a 100644 --- a/src/shared/ask-password-api.h +++ b/src/shared/ask-password-api.h @@ -21,11 +21,20 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ - -int ask_password_tty(const char *message, usec_t until, bool echo, const char *flag_file, char **_passphrase); - -int ask_password_agent(const char *message, const char *icon, const char *id, - usec_t until, bool echo, bool accept_cached, char ***_passphrases); - -int ask_password_auto(const char *message, const char *icon, const char *id, - usec_t until, bool accept_cached, char ***_passphrases); +#include <stdbool.h> + +#include "time-util.h" + +typedef enum AskPasswordFlags { + ASK_PASSWORD_ACCEPT_CACHED = 1, + ASK_PASSWORD_PUSH_CACHE = 2, + ASK_PASSWORD_ECHO = 4, /* show the password literally while reading, instead of "*" */ + ASK_PASSWORD_SILENT = 8, /* do no show any password at all while reading */ + ASK_PASSWORD_NO_TTY = 16, + ASK_PASSWORD_NO_AGENT = 32, +} AskPasswordFlags; + +int ask_password_tty(const char *message, const char *keyname, usec_t until, AskPasswordFlags flags, const char *flag_file, char **ret); +int ask_password_agent(const char *message, const char *icon, const char *id, const char *keyname, usec_t until, AskPasswordFlags flag, char ***ret); +int ask_password_keyring(const char *keyname, AskPasswordFlags flags, char ***ret); +int ask_password_auto(const char *message, const char *icon, const char *id, const char *keyname, usec_t until, AskPasswordFlags flag, char ***ret); diff --git a/src/shared/base-filesystem.c b/src/shared/base-filesystem.c index ab6fc171b0..e605490c32 100644 --- a/src/shared/base-filesystem.c +++ b/src/shared/base-filesystem.c @@ -20,13 +20,18 @@ ***/ #include <errno.h> -#include <sys/stat.h> #include <stdlib.h> +#include <sys/stat.h> #include <unistd.h> +#include "alloc-util.h" #include "base-filesystem.h" +#include "fd-util.h" #include "log.h" #include "macro.h" +#include "string-util.h" +#include "umask-util.h" +#include "user-util.h" #include "util.h" typedef struct BaseFilesystem { @@ -34,12 +39,13 @@ typedef struct BaseFilesystem { mode_t mode; const char *target; const char *exists; + bool ignore_failure; } BaseFilesystem; static const BaseFilesystem table[] = { { "bin", 0, "usr/bin\0", NULL }, { "lib", 0, "usr/lib\0", NULL }, - { "root", 0755, NULL, NULL }, + { "root", 0755, NULL, NULL, true }, { "sbin", 0, "usr/sbin\0", NULL }, { "usr", 0755, NULL, NULL }, { "var", 0755, NULL, NULL }, @@ -104,8 +110,13 @@ int base_filesystem_create(const char *root, uid_t uid, gid_t gid) { RUN_WITH_UMASK(0000) r = mkdirat(fd, table[i].dir, table[i].mode); - if (r < 0 && errno != EEXIST) - return log_error_errno(errno, "Failed to create directory at %s/%s: %m", root, table[i].dir); + if (r < 0 && errno != EEXIST) { + log_full_errno(table[i].ignore_failure ? LOG_DEBUG : LOG_ERR, errno, + "Failed to create directory at %s/%s: %m", root, table[i].dir); + + if (!table[i].ignore_failure) + return -errno; + } if (uid != UID_INVALID || gid != UID_INVALID) { if (fchownat(fd, table[i].dir, uid, gid, AT_SYMLINK_NOFOLLOW) < 0) diff --git a/src/shared/boot-timestamps.c b/src/shared/boot-timestamps.c index ecbe1aaa0f..879aca9374 100644 --- a/src/shared/boot-timestamps.c +++ b/src/shared/boot-timestamps.c @@ -20,8 +20,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "boot-timestamps.h" #include "acpi-fpdt.h" +#include "boot-timestamps.h" #include "efivars.h" int boot_timestamps(const dual_timestamp *n, dual_timestamp *firmware, dual_timestamp *loader) { diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 36c44227c5..38281045b8 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -21,24 +21,38 @@ #include <sys/socket.h> +#include "sd-bus.h" #include "sd-daemon.h" #include "sd-event.h" -#include "util.h" -#include "strv.h" -#include "macro.h" -#include "def.h" -#include "path-util.h" -#include "missing.h" -#include "set.h" -#include "signal-util.h" -#include "unit-name.h" -#include "sd-bus.h" +#include "alloc-util.h" #include "bus-error.h" +#include "bus-internal.h" #include "bus-label.h" #include "bus-message.h" #include "bus-util.h" -#include "bus-internal.h" +#include "cgroup-util.h" +#include "def.h" +#include "env-util.h" +#include "escape.h" +#include "fd-util.h" +#include "macro.h" +#include "missing.h" +#include "parse-util.h" +#include "path-util.h" +#include "proc-cmdline.h" +#include "process-util.h" +#include "rlimit-util.h" +#include "set.h" +#include "signal-util.h" +#include "stdio-util.h" +#include "string-util.h" +#include "strv.h" +#include "syslog-util.h" +#include "unit-name.h" +#include "user-util.h" +#include "utf8.h" +#include "util.h" static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { sd_event *e = userdata; @@ -572,14 +586,14 @@ int bus_check_peercred(sd_bus *c) { return 1; } -int bus_open_system_systemd(sd_bus **_bus) { +int bus_connect_system_systemd(sd_bus **_bus) { _cleanup_bus_unref_ sd_bus *bus = NULL; int r; assert(_bus); if (geteuid() != 0) - return sd_bus_open_system(_bus); + return sd_bus_default_system(_bus); /* If we are root and kdbus is not available, then let's talk * directly to the system instance, instead of going via the @@ -614,7 +628,7 @@ int bus_open_system_systemd(sd_bus **_bus) { r = sd_bus_start(bus); if (r < 0) - return sd_bus_open_system(_bus); + return sd_bus_default_system(_bus); r = bus_check_peercred(bus); if (r < 0) @@ -626,7 +640,7 @@ int bus_open_system_systemd(sd_bus **_bus) { return 0; } -int bus_open_user_systemd(sd_bus **_bus) { +int bus_connect_user_systemd(sd_bus **_bus) { _cleanup_bus_unref_ sd_bus *bus = NULL; _cleanup_free_ char *ee = NULL; const char *e; @@ -656,7 +670,7 @@ int bus_open_user_systemd(sd_bus **_bus) { e = secure_getenv("XDG_RUNTIME_DIR"); if (!e) - return sd_bus_open_user(_bus); + return sd_bus_default_user(_bus); ee = bus_address_escape(e); if (!ee) @@ -672,7 +686,7 @@ int bus_open_user_systemd(sd_bus **_bus) { r = sd_bus_start(bus); if (r < 0) - return sd_bus_open_user(_bus); + return sd_bus_default_user(_bus); r = bus_check_peercred(bus); if (r < 0) @@ -1207,7 +1221,7 @@ int bus_map_all_properties( return bus_message_map_all_properties(m, map, userdata); } -int bus_open_transport(BusTransport transport, const char *host, bool user, sd_bus **bus) { +int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **bus) { int r; assert(transport >= 0); @@ -1242,7 +1256,7 @@ int bus_open_transport(BusTransport transport, const char *host, bool user, sd_b return r; } -int bus_open_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus) { +int bus_connect_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus) { int r; assert(transport >= 0); @@ -1256,9 +1270,9 @@ int bus_open_transport_systemd(BusTransport transport, const char *host, bool us case BUS_TRANSPORT_LOCAL: if (user) - r = bus_open_user_systemd(bus); + r = bus_connect_user_systemd(bus); else - r = bus_open_system_systemd(bus); + r = bus_connect_system_systemd(bus); break; @@ -1414,6 +1428,37 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen return bus_log_create_error(r); return 0; + + } else if (streq(field, "EnvironmentFile")) { + + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "EnvironmentFiles"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "v", "a(sb)", 1, + eq[0] == '-' ? eq + 1 : eq, + eq[0] == '-'); + if (r < 0) + return bus_log_create_error(r); + + return 0; + + } else if (streq(field, "RandomSec")) { + usec_t t; + + r = parse_sec(eq, &t); + if (r < 0) + return log_error_errno(r, "Failed to parse RandomSec= parameter: %s", eq); + + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, "RandomUSec"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "v", "t", t); + if (r < 0) + return bus_log_create_error(r); + + return 0; } r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); @@ -1421,33 +1466,63 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen return bus_log_create_error(r); if (STR_IN_SET(field, - "CPUAccounting", "MemoryAccounting", "BlockIOAccounting", + "CPUAccounting", "MemoryAccounting", "BlockIOAccounting", "TasksAccounting", "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies", - "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit")) { + "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit", + "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges", + "SyslogLevelPrefix", "Delegate", "RemainAfterElapse")) { r = parse_boolean(eq); - if (r < 0) { - log_error("Failed to parse boolean assignment %s.", assignment); - return -EINVAL; - } + if (r < 0) + return log_error_errno(r, "Failed to parse boolean assignment %s.", assignment); r = sd_bus_message_append(m, "v", "b", r); } else if (streq(field, "MemoryLimit")) { - off_t bytes; + uint64_t bytes; - r = parse_size(eq, 1024, &bytes); + if (isempty(eq) || streq(eq, "infinity")) + bytes = (uint64_t) -1; + else { + r = parse_size(eq, 1024, &bytes); + if (r < 0) { + log_error("Failed to parse bytes specification %s", assignment); + return -EINVAL; + } + } + + r = sd_bus_message_append(m, "v", "t", bytes); + + } else if (streq(field, "TasksMax")) { + uint64_t n; + + if (isempty(eq) || streq(eq, "infinity")) + n = (uint64_t) -1; + else { + r = safe_atou64(eq, &n); + if (r < 0) { + log_error("Failed to parse maximum tasks specification %s", assignment); + return -EINVAL; + } + } + + r = sd_bus_message_append(m, "v", "t", n); + + } else if (STR_IN_SET(field, "CPUShares", "StartupCPUShares")) { + uint64_t u; + + r = cg_cpu_shares_parse(eq, &u); if (r < 0) { - log_error("Failed to parse bytes specification %s", assignment); + log_error("Failed to parse %s value %s.", field, eq); return -EINVAL; } - r = sd_bus_message_append(m, "v", "t", (uint64_t) bytes); + r = sd_bus_message_append(m, "v", "t", u); - } else if (STR_IN_SET(field, "CPUShares", "BlockIOWeight")) { + } else if (STR_IN_SET(field, "BlockIOWeight", "StartupBlockIOWeight")) { uint64_t u; - r = safe_atou64(eq, &u); + r = cg_cpu_shares_parse(eq, &u); if (r < 0) { log_error("Failed to parse %s value %s.", field, eq); return -EINVAL; @@ -1459,10 +1534,34 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen "User", "Group", "DevicePolicy", "KillMode", "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath", "StandardInput", "StandardOutput", "StandardError", - "Description", "Slice", "Type")) + "Description", "Slice", "Type", "WorkingDirectory", + "RootDirectory", "SyslogIdentifier", "ProtectSystem", + "ProtectHome")) r = sd_bus_message_append(m, "v", "s", eq); - else if (streq(field, "DeviceAllow")) { + else if (streq(field, "SyslogLevel")) { + int level; + + level = log_level_from_string(eq); + if (level < 0) { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "i", level); + + } else if (streq(field, "SyslogFacility")) { + int facility; + + facility = log_facility_unshifted_from_string(eq); + if (facility < 0) { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "i", facility); + + } else if (streq(field, "DeviceAllow")) { if (isempty(eq)) r = sd_bus_message_append(m, "v", "a(ss)", 0); @@ -1492,7 +1591,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen r = sd_bus_message_append(m, "v", "a(st)", 0); else { const char *path, *bandwidth, *e; - off_t bytes; + uint64_t bytes; e = strchr(eq, ' '); if (e) { @@ -1514,7 +1613,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen return -EINVAL; } - r = sd_bus_message_append(m, "v", "a(st)", 1, path, (uint64_t) bytes); + r = sd_bus_message_append(m, "v", "a(st)", 1, path, bytes); } } else if (streq(field, "BlockIODeviceWeight")) { @@ -1573,9 +1672,52 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen r = sd_bus_message_append(m, "v", "i", i); - } else if (streq(field, "Environment")) { + } else if (STR_IN_SET(field, "Environment", "PassEnvironment")) { + const char *p; - r = sd_bus_message_append(m, "v", "as", 1, eq); + 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|EXTRACT_CUNESCAPE); + if (r < 0) { + log_error("Failed to parse Environment value %s", eq); + return -EINVAL; + } + if (r == 0) + break; + + if (streq(field, "Environment")) { + if (!env_assignment_is_valid(word)) { + log_error("Invalid environment assignment: %s", word); + return -EINVAL; + } + } else { /* PassEnvironment */ + if (!env_name_is_valid(word)) { + log_error("Invalid environment variable name: %s", word); + return -EINVAL; + } + } + + 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 if (streq(field, "KillSignal")) { int sig; @@ -1598,6 +1740,113 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen } r = sd_bus_message_append(m, "v", "t", u); + } else if (streq(field, "TimerSlackNSec")) { + nsec_t n; + + r = parse_nsec(eq, &n); + if (r < 0) { + log_error("Failed to parse %s value %s", field, eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "t", n); + } else if (streq(field, "OOMScoreAdjust")) { + int oa; + + r = safe_atoi(eq, &oa); + if (r < 0) { + log_error("Failed to parse %s value %s", field, eq); + return -EINVAL; + } + + if (!oom_score_adjust_is_valid(oa)) { + log_error("OOM score adjust value out of range"); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "i", oa); + } else if (STR_IN_SET(field, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories")) { + 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; + int offset; + + r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); + if (r < 0) { + log_error("Failed to parse %s value %s", field, eq); + return -EINVAL; + } + if (r == 0) + break; + + if (!utf8_is_valid(word)) { + log_error("Failed to parse %s value %s", field, eq); + return -EINVAL; + } + + offset = word[0] == '-'; + if (!path_is_absolute(word + offset)) { + log_error("Failed to parse %s value %s", field, eq); + return -EINVAL; + } + + path_kill_slashes(word + offset); + + 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 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); @@ -2103,3 +2352,42 @@ bool is_kdbus_available(void) { return ioctl(fd, KDBUS_CMD_BUS_MAKE, &cmd) >= 0; } + +int bus_property_get_rlimit( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + struct rlimit *rl; + uint64_t u; + rlim_t x; + + assert(bus); + assert(reply); + assert(userdata); + + rl = *(struct rlimit**) userdata; + if (rl) + x = rl->rlim_max; + else { + struct rlimit buf = {}; + int z; + + z = rlimit_from_string(strstr(property, "Limit")); + assert(z >= 0); + + getrlimit(z, &buf); + x = buf.rlim_max; + } + + /* rlim_t might have different sizes, let's map + * RLIMIT_INFINITY to (uint64_t) -1, so that it is the same on + * all archs */ + u = x == RLIM_INFINITY ? (uint64_t) -1 : (uint64_t) x; + + return sd_bus_message_append(reply, "t", u); +} diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h index d2b2d701ce..3925c10fde 100644 --- a/src/shared/bus-util.h +++ b/src/shared/bus-util.h @@ -21,10 +21,12 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "sd-event.h" #include "sd-bus.h" +#include "sd-event.h" + #include "hashmap.h" #include "install.h" +#include "string-util.h" #include "time-util.h" typedef enum BusTransport { @@ -65,11 +67,11 @@ int bus_test_polkit(sd_bus_message *call, int capability, const char *action, co int bus_verify_polkit_async(sd_bus_message *call, int capability, const char *action, const char **details, bool interactive, uid_t good_user, Hashmap **registry, sd_bus_error *error); void bus_verify_polkit_async_registry_free(Hashmap *registry); -int bus_open_system_systemd(sd_bus **_bus); -int bus_open_user_systemd(sd_bus **_bus); +int bus_connect_system_systemd(sd_bus **_bus); +int bus_connect_user_systemd(sd_bus **_bus); -int bus_open_transport(BusTransport transport, const char *host, bool user, sd_bus **bus); -int bus_open_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus); +int bus_connect_transport(BusTransport transport, const char *host, bool user, sd_bus **bus); +int bus_connect_transport_systemd(BusTransport transport, const char *host, bool user, sd_bus **bus); int bus_print_property(const char *name, sd_bus_message *property, bool all); int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, char **filter, bool all); @@ -200,3 +202,5 @@ int bus_path_decode_unique(const char *path, const char *prefix, char **ret_send bool is_kdbus_wanted(void); bool is_kdbus_available(void); + +int bus_property_get_rlimit(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error); diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c index 31b4f6c684..129ffc7056 100644 --- a/src/shared/cgroup-show.c +++ b/src/shared/cgroup-show.c @@ -19,19 +19,23 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdio.h> -#include <string.h> #include <dirent.h> #include <errno.h> +#include <stdio.h> +#include <string.h> -#include "util.h" +#include "alloc-util.h" +#include "cgroup-show.h" +#include "cgroup-util.h" +#include "fd-util.h" #include "formats-util.h" -#include "process-util.h" +#include "locale-util.h" #include "macro.h" #include "path-util.h" -#include "cgroup-util.h" -#include "cgroup-show.h" +#include "process-util.h" +#include "string-util.h" #include "terminal-util.h" +#include "util.h" static int compare(const void *a, const void *b) { const pid_t *p = a, *q = b; diff --git a/src/shared/cgroup-show.h b/src/shared/cgroup-show.h index aa832454b5..5842bdd15e 100644 --- a/src/shared/cgroup-show.h +++ b/src/shared/cgroup-show.h @@ -23,6 +23,7 @@ #include <stdbool.h> #include <sys/types.h> + #include "logs-show.h" int show_cgroup_by_path(const char *path, const char *prefix, unsigned columns, bool kernel_threads, OutputFlags flags); diff --git a/src/shared/clean-ipc.c b/src/shared/clean-ipc.c index d1cdb151b2..71cc613704 100644 --- a/src/shared/clean-ipc.c +++ b/src/shared/clean-ipc.c @@ -19,19 +19,23 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <dirent.h> +#include <fcntl.h> +#include <mqueue.h> #include <sys/ipc.h> -#include <sys/shm.h> -#include <sys/sem.h> #include <sys/msg.h> +#include <sys/sem.h> +#include <sys/shm.h> #include <sys/stat.h> -#include <fcntl.h> -#include <dirent.h> -#include <mqueue.h> -#include "util.h" +#include "clean-ipc.h" +#include "dirent-util.h" +#include "fd-util.h" +#include "fileio.h" #include "formats-util.h" +#include "string-util.h" #include "strv.h" -#include "clean-ipc.h" +#include "util.h" static int clean_sysvipc_shm(uid_t delete_uid) { _cleanup_fclose_ FILE *f = NULL; @@ -44,8 +48,7 @@ static int clean_sysvipc_shm(uid_t delete_uid) { if (errno == ENOENT) return 0; - log_warning_errno(errno, "Failed to open /proc/sysvipc/shm: %m"); - return -errno; + return log_warning_errno(errno, "Failed to open /proc/sysvipc/shm: %m"); } FOREACH_LINE(line, f, goto fail) { @@ -87,8 +90,7 @@ static int clean_sysvipc_shm(uid_t delete_uid) { return ret; fail: - log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m"); - return -errno; + return log_warning_errno(errno, "Failed to read /proc/sysvipc/shm: %m"); } static int clean_sysvipc_sem(uid_t delete_uid) { @@ -102,8 +104,7 @@ static int clean_sysvipc_sem(uid_t delete_uid) { if (errno == ENOENT) return 0; - log_warning_errno(errno, "Failed to open /proc/sysvipc/sem: %m"); - return -errno; + return log_warning_errno(errno, "Failed to open /proc/sysvipc/sem: %m"); } FOREACH_LINE(line, f, goto fail) { @@ -140,8 +141,7 @@ static int clean_sysvipc_sem(uid_t delete_uid) { return ret; fail: - log_warning_errno(errno, "Failed to read /proc/sysvipc/sem: %m"); - return -errno; + return log_warning_errno(errno, "Failed to read /proc/sysvipc/sem: %m"); } static int clean_sysvipc_msg(uid_t delete_uid) { @@ -155,8 +155,7 @@ static int clean_sysvipc_msg(uid_t delete_uid) { if (errno == ENOENT) return 0; - log_warning_errno(errno, "Failed to open /proc/sysvipc/msg: %m"); - return -errno; + return log_warning_errno(errno, "Failed to open /proc/sysvipc/msg: %m"); } FOREACH_LINE(line, f, goto fail) { @@ -194,8 +193,7 @@ static int clean_sysvipc_msg(uid_t delete_uid) { return ret; fail: - log_warning_errno(errno, "Failed to read /proc/sysvipc/msg: %m"); - return -errno; + return log_warning_errno(errno, "Failed to read /proc/sysvipc/msg: %m"); } static int clean_posix_shm_internal(DIR *dir, uid_t uid) { @@ -273,8 +271,7 @@ static int clean_posix_shm(uid_t uid) { if (errno == ENOENT) return 0; - log_warning_errno(errno, "Failed to open /dev/shm: %m"); - return -errno; + return log_warning_errno(errno, "Failed to open /dev/shm: %m"); } return clean_posix_shm_internal(dir, uid); @@ -290,8 +287,7 @@ static int clean_posix_mq(uid_t uid) { if (errno == ENOENT) return 0; - log_warning_errno(errno, "Failed to open /dev/mqueue: %m"); - return -errno; + return log_warning_errno(errno, "Failed to open /dev/mqueue: %m"); } FOREACH_DIRENT(de, dir, goto fail) { @@ -330,8 +326,7 @@ static int clean_posix_mq(uid_t uid) { return ret; fail: - log_warning_errno(errno, "Failed to read /dev/mqueue: %m"); - return -errno; + return log_warning_errno(errno, "Failed to read /dev/mqueue: %m"); } int clean_ipc(uid_t uid) { diff --git a/src/shared/condition.c b/src/shared/condition.c index 1d7dd49e04..a69719116c 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -19,25 +19,36 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdlib.h> #include <errno.h> +#include <fnmatch.h> +#include <stdlib.h> #include <string.h> #include <unistd.h> -#include <fnmatch.h> #include "sd-id128.h" -#include "util.h" -#include "virt.h" -#include "path-util.h" -#include "architecture.h" -#include "smack-util.h" + +#include "alloc-util.h" #include "apparmor-util.h" -#include "ima-util.h" -#include "selinux-util.h" -#include "audit.h" +#include "architecture.h" +#include "audit-util.h" #include "cap-list.h" -#include "hostname-util.h" #include "condition.h" +#include "extract-word.h" +#include "fd-util.h" +#include "glob-util.h" +#include "hostname-util.h" +#include "ima-util.h" +#include "mount-util.h" +#include "parse-util.h" +#include "path-util.h" +#include "proc-cmdline.h" +#include "selinux-util.h" +#include "smack-util.h" +#include "stat-util.h" +#include "string-table.h" +#include "string-util.h" +#include "util.h" +#include "virt.h" Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) { Condition *c; diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c index 23512f0d35..486122b0fd 100644 --- a/src/shared/conf-parser.c +++ b/src/shared/conf-parser.c @@ -19,21 +19,29 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <string.h> -#include <stdio.h> #include <errno.h> +#include <stdio.h> #include <stdlib.h> +#include <string.h> #include "sd-messages.h" + +#include "alloc-util.h" #include "conf-files.h" -#include "util.h" -#include "macro.h" -#include "strv.h" +#include "conf-parser.h" +#include "fd-util.h" +#include "fs-util.h" #include "log.h" -#include "utf8.h" +#include "macro.h" +#include "parse-util.h" #include "path-util.h" +#include "process-util.h" #include "signal-util.h" -#include "conf-parser.h" +#include "string-util.h" +#include "strv.h" +#include "syslog-util.h" +#include "utf8.h" +#include "util.h" int config_item_table_lookup( const void *table, @@ -147,8 +155,7 @@ static int next_assignment(const char *unit, /* Warn about unknown non-extension fields. */ if (!relaxed && !startswith(lvalue, "X-")) - log_syntax(unit, LOG_WARNING, filename, line, EINVAL, - "Unknown lvalue '%s' in section '%s'", lvalue, section); + log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown lvalue '%s' in section '%s'", lvalue, section); return 0; } @@ -196,8 +203,7 @@ static int parse_line(const char* unit, * Support for them should be eventually removed. */ if (!allow_include) { - log_syntax(unit, LOG_ERR, filename, line, EBADMSG, - ".include not allowed here. Ignoring."); + log_syntax(unit, LOG_ERR, filename, line, 0, ".include not allowed here. Ignoring."); return 0; } @@ -216,8 +222,7 @@ static int parse_line(const char* unit, assert(k > 0); if (l[k-1] != ']') { - log_syntax(unit, LOG_ERR, filename, line, EBADMSG, - "Invalid section header '%s'", l); + log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid section header '%s'", l); return -EBADMSG; } @@ -228,8 +233,7 @@ static int parse_line(const char* unit, if (sections && !nulstr_contains(sections, n)) { if (!relaxed && !startswith(n, "X-")) - log_syntax(unit, LOG_WARNING, filename, line, EINVAL, - "Unknown section '%s'. Ignoring.", n); + log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown section '%s'. Ignoring.", n); free(n); *section = mfree(*section); @@ -248,16 +252,15 @@ static int parse_line(const char* unit, if (sections && !*section) { if (!relaxed && !*section_ignored) - log_syntax(unit, LOG_WARNING, filename, line, EINVAL, - "Assignment outside of section. Ignoring."); + log_syntax(unit, LOG_WARNING, filename, line, 0, "Assignment outside of section. Ignoring."); return 0; } e = strchr(l, '='); if (!e) { - log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Missing '='."); - return -EBADMSG; + log_syntax(unit, LOG_WARNING, filename, line, 0, "Missing '='."); + return -EINVAL; } *e = 0; @@ -420,16 +423,17 @@ int config_parse_many(const char *conf_file, } #define DEFINE_PARSER(type, vartype, conv_func) \ - int config_parse_##type(const char *unit, \ - const char *filename, \ - unsigned line, \ - const char *section, \ - unsigned section_line, \ - const char *lvalue, \ - int ltype, \ - const char *rvalue, \ - void *data, \ - void *userdata) { \ + int config_parse_##type( \ + const char *unit, \ + const char *filename, \ + unsigned line, \ + const char *section, \ + unsigned section_line, \ + const char *lvalue, \ + int ltype, \ + const char *rvalue, \ + void *data, \ + void *userdata) { \ \ vartype *i = data; \ int r; \ @@ -441,21 +445,23 @@ int config_parse_many(const char *conf_file, \ r = conv_func(rvalue, i); \ if (r < 0) \ - log_syntax(unit, LOG_ERR, filename, line, -r, \ + log_syntax(unit, LOG_ERR, filename, line, r, \ "Failed to parse %s value, ignoring: %s", \ #type, rvalue); \ \ return 0; \ - } - -DEFINE_PARSER(int, int, safe_atoi) -DEFINE_PARSER(long, long, safe_atoli) -DEFINE_PARSER(uint32, uint32_t, safe_atou32) -DEFINE_PARSER(uint64, uint64_t, safe_atou64) -DEFINE_PARSER(unsigned, unsigned, safe_atou) -DEFINE_PARSER(double, double, safe_atod) -DEFINE_PARSER(nsec, nsec_t, parse_nsec) -DEFINE_PARSER(sec, usec_t, parse_sec) + } \ + struct __useless_struct_to_allow_trailing_semicolon__ + +DEFINE_PARSER(int, int, safe_atoi); +DEFINE_PARSER(long, long, safe_atoli); +DEFINE_PARSER(uint32, uint32_t, safe_atou32); +DEFINE_PARSER(uint64, uint64_t, safe_atou64); +DEFINE_PARSER(unsigned, unsigned, safe_atou); +DEFINE_PARSER(double, double, safe_atod); +DEFINE_PARSER(nsec, nsec_t, parse_nsec); +DEFINE_PARSER(sec, usec_t, parse_sec); +DEFINE_PARSER(mode, mode_t, parse_mode); int config_parse_iec_size(const char* unit, const char *filename, @@ -469,7 +475,7 @@ int config_parse_iec_size(const char* unit, void *userdata) { size_t *sz = data; - off_t o; + uint64_t v; int r; assert(filename); @@ -477,13 +483,13 @@ int config_parse_iec_size(const char* unit, assert(rvalue); assert(data); - r = parse_size(rvalue, 1024, &o); - if (r < 0 || (off_t) (size_t) o != o) { - log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue); + r = parse_size(rvalue, 1024, &v); + if (r < 0 || (uint64_t) (size_t) v != v) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue); return 0; } - *sz = (size_t) o; + *sz = (size_t) v; return 0; } @@ -499,7 +505,7 @@ int config_parse_si_size(const char* unit, void *userdata) { size_t *sz = data; - off_t o; + uint64_t v; int r; assert(filename); @@ -507,17 +513,17 @@ int config_parse_si_size(const char* unit, assert(rvalue); assert(data); - r = parse_size(rvalue, 1000, &o); - if (r < 0 || (off_t) (size_t) o != o) { - log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue); + r = parse_size(rvalue, 1000, &v); + if (r < 0 || (uint64_t) (size_t) v != v) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue); return 0; } - *sz = (size_t) o; + *sz = (size_t) v; return 0; } -int config_parse_iec_off(const char* unit, +int config_parse_iec_uint64(const char* unit, const char *filename, unsigned line, const char *section, @@ -528,7 +534,7 @@ int config_parse_iec_off(const char* unit, void *data, void *userdata) { - off_t *bytes = data; + uint64_t *bytes = data; int r; assert(filename); @@ -536,11 +542,9 @@ int config_parse_iec_off(const char* unit, assert(rvalue); assert(data); - assert_cc(sizeof(off_t) == sizeof(uint64_t)); - r = parse_size(rvalue, 1024, bytes); if (r < 0) - log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to parse size value, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue); return 0; } @@ -566,8 +570,7 @@ int config_parse_bool(const char* unit, k = parse_boolean(rvalue); if (k < 0) { - log_syntax(unit, LOG_ERR, filename, line, -k, - "Failed to parse boolean value, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue); return 0; } @@ -628,7 +631,7 @@ int config_parse_string( assert(data); if (!utf8_is_valid(rvalue)) { - log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue); + log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue); return 0; } @@ -666,12 +669,12 @@ int config_parse_path( assert(data); if (!utf8_is_valid(rvalue)) { - log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue); + log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue); return 0; } if (!path_is_absolute(rvalue)) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Not an absolute path, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute path, ignoring: %s", rvalue); return 0; } @@ -699,9 +702,6 @@ int config_parse_strv(const char *unit, void *userdata) { char ***sv = data; - const char *word, *state; - size_t l; - int r; assert(filename); assert(lvalue); @@ -724,53 +724,28 @@ int config_parse_strv(const char *unit, return 0; } - FOREACH_WORD_QUOTED(word, l, rvalue, state) { - char *n; - - n = strndup(word, l); - if (!n) + for (;;) { + char *word = NULL; + int r; + r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES); + if (r == 0) + break; + if (r == -ENOMEM) return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue); + break; + } - if (!utf8_is_valid(n)) { - log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue); - free(n); + if (!utf8_is_valid(word)) { + log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue); + free(word); continue; } - - r = strv_consume(sv, n); + r = strv_consume(sv, word); if (r < 0) return log_oom(); } - if (!isempty(state)) - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Trailing garbage, ignoring."); - - return 0; -} - -int config_parse_mode( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - mode_t *m = data; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (parse_mode(rvalue, m) < 0) { - log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse mode value, ignoring: %s", rvalue); - return 0; - } return 0; } @@ -797,7 +772,7 @@ int config_parse_log_facility( x = log_facility_unshifted_from_string(rvalue); if (x < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse log facility, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log facility, ignoring: %s", rvalue); return 0; } @@ -828,7 +803,7 @@ int config_parse_log_level( x = log_level_from_string(rvalue); if (x < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse log level, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log level, ignoring: %s", rvalue); return 0; } @@ -857,7 +832,7 @@ int config_parse_signal( r = signal_from_string_try_harder(rvalue); if (r <= 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse signal name, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse signal name, ignoring: %s", rvalue); return 0; } @@ -886,7 +861,7 @@ int config_parse_personality( p = personality_from_string(rvalue); if (p == PERSONALITY_INVALID) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse personality, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue); return 0; } diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h index 34e3815782..2872b22d9d 100644 --- a/src/shared/conf-parser.h +++ b/src/shared/conf-parser.h @@ -21,8 +21,8 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdio.h> #include <stdbool.h> +#include <stdio.h> #include "macro.h" @@ -109,7 +109,7 @@ int config_parse_uint64(const char *unit, const char *filename, unsigned line, c int config_parse_double(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_iec_size(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_si_size(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -int config_parse_iec_off(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_iec_uint64(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_bool(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_tristate(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_string(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); @@ -123,13 +123,6 @@ int config_parse_log_level(const char *unit, const char *filename, unsigned line int config_parse_signal(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_personality(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); -#define log_invalid_utf8(unit, level, config_file, config_line, error, rvalue) \ - do { \ - _cleanup_free_ char *_p = utf8_escape_invalid(rvalue); \ - log_syntax(unit, level, config_file, config_line, error, \ - "String is not UTF-8 clean, ignoring assignment: %s", strna(_p)); \ - } while(false) - #define DEFINE_CONFIG_PARSE_ENUM(function,name,type,msg) \ int function(const char *unit, \ const char *filename, \ diff --git a/src/shared/dev-setup.c b/src/shared/dev-setup.c index 25ad918b85..ad3c17d5bd 100644 --- a/src/shared/dev-setup.c +++ b/src/shared/dev-setup.c @@ -23,10 +23,12 @@ #include <stdlib.h> #include <unistd.h> -#include "util.h" +#include "alloc-util.h" +#include "dev-setup.h" #include "label.h" #include "path-util.h" -#include "dev-setup.h" +#include "user-util.h" +#include "util.h" int dev_setup(const char *prefix, uid_t uid, gid_t gid) { static const char symlinks[] = diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index 6dc04d51e4..423ccca9cc 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -24,7 +24,11 @@ #include <stringprep.h> #endif +#include "alloc-util.h" #include "dns-domain.h" +#include "hexdecoct.h" +#include "parse-util.h" +#include "string-util.h" int dns_label_unescape(const char **name, char *dest, size_t sz) { const char *n; @@ -379,9 +383,8 @@ int dns_name_concat(const char *a, const char *b, char **_ret) { return 0; } -unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_SIZE]) { +void dns_name_hash_func(const void *s, struct siphash *state) { const char *p = s; - unsigned long ul = hash_key[0]; int r; assert(p); @@ -400,13 +403,17 @@ unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_ if (k > 0) r = k; + if (r == 0) + break; + label[r] = 0; ascii_strlower(label); - ul = ul * hash_key[1] + ul + string_hash_func(label, hash_key); + string_hash_func(label, state); } - return ul; + /* enforce that all names are terminated by the empty label */ + string_hash_func("", state); } int dns_name_compare_func(const void *a, const void *b) { @@ -708,3 +715,37 @@ int dns_name_single_label(const char *name) { return r == 0 && *name == 0; } + +/* Encode a domain name according to RFC 1035 Section 3.1 */ +int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len) { + uint8_t *label_length; + uint8_t *out; + int r; + + assert_return(buffer, -EINVAL); + assert_return(domain, -EINVAL); + assert_return(domain[0], -EINVAL); + + out = buffer; + + do { + /* reserve a byte for label length */ + if (len == 0) + return -ENOBUFS; + len--; + label_length = out; + out++; + + /* convert and copy a single label */ + r = dns_label_unescape(&domain, (char *) out, len); + if (r < 0) + return r; + + /* fill label length, move forward */ + *label_length = r; + out += r; + len -= r; + } while (r != 0); + + return out - buffer; +} diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h index 8e73d9c20f..b214897440 100644 --- a/src/shared/dns-domain.h +++ b/src/shared/dns-domain.h @@ -54,7 +54,7 @@ static inline int dns_name_is_valid(const char *s) { return 1; } -unsigned long dns_name_hash_func(const void *s, const uint8_t hash_key[HASH_KEY_SIZE]); +void dns_name_hash_func(const void *s, struct siphash *state); int dns_name_compare_func(const void *a, const void *b); extern const struct hash_ops dns_name_hash_ops; @@ -67,3 +67,5 @@ int dns_name_address(const char *p, int *family, union in_addr_union *a); int dns_name_root(const char *name); int dns_name_single_label(const char *name); + +int dns_name_to_wire_format(const char *domain, uint8_t *buffer, size_t len); diff --git a/src/shared/dropin.c b/src/shared/dropin.c index 963d05d32e..0d44401cc2 100644 --- a/src/shared/dropin.c +++ b/src/shared/dropin.c @@ -19,12 +19,17 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include "alloc-util.h" +#include "conf-files.h" #include "dropin.h" -#include "util.h" -#include "strv.h" -#include "mkdir.h" +#include "escape.h" +#include "fd-util.h" #include "fileio-label.h" -#include "conf-files.h" +#include "mkdir.h" +#include "path-util.h" +#include "string-util.h" +#include "strv.h" +#include "util.h" int drop_in_file(const char *dir, const char *unit, unsigned level, const char *name, char **_p, char **_q) { @@ -78,7 +83,7 @@ int write_drop_in(const char *dir, const char *unit, unsigned level, if (r < 0) return r; - mkdir_p(p, 0755); + (void) mkdir_p(p, 0755); return write_string_file_atomic_label(q, data); } @@ -132,8 +137,7 @@ static int iterate_dir( if (errno == ENOENT) return 0; - log_error_errno(errno, "Failed to open directory %s: %m", path); - return -errno; + return log_error_errno(errno, "Failed to open directory %s: %m", path); } for (;;) { diff --git a/src/shared/efivars.c b/src/shared/efivars.c index f087c2a566..89deeb9b55 100644 --- a/src/shared/efivars.c +++ b/src/shared/efivars.c @@ -19,14 +19,20 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <unistd.h> -#include <string.h> #include <fcntl.h> +#include <string.h> +#include <unistd.h> -#include "util.h" +#include "alloc-util.h" +#include "dirent-util.h" +#include "efivars.h" +#include "fd-util.h" +#include "io-util.h" +#include "parse-util.h" +#include "stdio-util.h" #include "utf8.h" +#include "util.h" #include "virt.h" -#include "efivars.h" #ifdef ENABLE_EFI diff --git a/src/shared/efivars.h b/src/shared/efivars.h index e953a12737..5cb4c3af4e 100644 --- a/src/shared/efivars.h +++ b/src/shared/efivars.h @@ -24,6 +24,7 @@ #include <stdbool.h> #include "sd-id128.h" + #include "time-util.h" #define EFI_VENDOR_LOADER SD_ID128_MAKE(4a,67,b0,82,0a,4c,41,cf,b6,c7,44,0b,29,bb,8c,4f) diff --git a/src/shared/firewall-util.c b/src/shared/firewall-util.c index effc6e8e70..5acfb0191b 100644 --- a/src/shared/firewall-util.c +++ b/src/shared/firewall-util.c @@ -19,16 +19,17 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/types.h> #include <arpa/inet.h> #include <net/if.h> +#include <sys/types.h> #include <linux/netfilter_ipv4/ip_tables.h> #include <linux/netfilter/nf_nat.h> #include <linux/netfilter/xt_addrtype.h> #include <libiptc/libiptc.h> -#include "util.h" +#include "alloc-util.h" #include "firewall-util.h" +#include "util.h" DEFINE_TRIVIAL_CLEANUP_FUNC(struct xtc_handle*, iptc_free); diff --git a/src/shared/formats-util.h b/src/shared/formats-util.h deleted file mode 100644 index ce516b117d..0000000000 --- a/src/shared/formats-util.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -/*** - This file is part of systemd. - - Copyright 2015 Ronny Chevalier - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <inttypes.h> - -#if SIZEOF_PID_T == 4 -# define PID_PRI PRIi32 -#elif SIZEOF_PID_T == 2 -# define PID_PRI PRIi16 -#else -# error Unknown pid_t size -#endif -#define PID_FMT "%" PID_PRI - -#if SIZEOF_UID_T == 4 -# define UID_FMT "%" PRIu32 -#elif SIZEOF_UID_T == 2 -# define UID_FMT "%" PRIu16 -#else -# error Unknown uid_t size -#endif - -#if SIZEOF_GID_T == 4 -# define GID_FMT "%" PRIu32 -#elif SIZEOF_GID_T == 2 -# define GID_FMT "%" PRIu16 -#else -# error Unknown gid_t size -#endif - -#if SIZEOF_TIME_T == 8 -# define PRI_TIME PRIi64 -#elif SIZEOF_TIME_T == 4 -# define PRI_TIME PRIu32 -#else -# error Unknown time_t size -#endif - -#if SIZEOF_RLIM_T == 8 -# define RLIM_FMT "%" PRIu64 -#elif SIZEOF_RLIM_T == 4 -# define RLIM_FMT "%" PRIu32 -#else -# error Unknown rlim_t size -#endif diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c index e231a0ff80..eb2845cddf 100644 --- a/src/shared/fstab-util.c +++ b/src/shared/fstab-util.c @@ -19,10 +19,31 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include "alloc-util.h" +#include "device-nodes.h" #include "fstab-util.h" +#include "mount-util.h" +#include "parse-util.h" +#include "path-util.h" +#include "string-util.h" #include "strv.h" #include "util.h" +bool fstab_is_mount_point(const char *mount) { + _cleanup_endmntent_ FILE *f = NULL; + struct mntent *m; + + f = setmntent("/etc/fstab", "r"); + if (!f) + return false; + + while ((m = getmntent(f))) + if (path_equal(m->mnt_dir, mount)) + return true; + + return false; +} + int fstab_filter_options(const char *opts, const char *names, const char **namefound, char **value, char **filtered) { const char *name, *n = NULL, *x; @@ -178,3 +199,60 @@ int fstab_find_pri(const char *options, int *ret) { *ret = (int) pri; return 1; } + +static char *unquote(const char *s, const char* quotes) { + size_t l; + assert(s); + + /* This is rather stupid, simply removes the heading and + * trailing quotes if there is one. Doesn't care about + * escaping or anything. + * + * DON'T USE THIS FOR NEW CODE ANYMORE!*/ + + l = strlen(s); + if (l < 2) + return strdup(s); + + if (strchr(quotes, s[0]) && s[l-1] == s[0]) + return strndup(s+1, l-2); + + return strdup(s); +} + +static char *tag_to_udev_node(const char *tagvalue, const char *by) { + _cleanup_free_ char *t = NULL, *u = NULL; + size_t enc_len; + + u = unquote(tagvalue, QUOTES); + if (!u) + return NULL; + + enc_len = strlen(u) * 4 + 1; + t = new(char, enc_len); + if (!t) + return NULL; + + if (encode_devnode_name(u, t, enc_len) < 0) + return NULL; + + return strjoin("/dev/disk/by-", by, "/", t, NULL); +} + +char *fstab_node_to_udev_node(const char *p) { + assert(p); + + if (startswith(p, "LABEL=")) + return tag_to_udev_node(p+6, "label"); + + if (startswith(p, "UUID=")) + return tag_to_udev_node(p+5, "uuid"); + + if (startswith(p, "PARTUUID=")) + return tag_to_udev_node(p+9, "partuuid"); + + if (startswith(p, "PARTLABEL=")) + return tag_to_udev_node(p+10, "partlabel"); + + return strdup(p); +} diff --git a/src/shared/fstab-util.h b/src/shared/fstab-util.h index 387c562a96..5ebea44019 100644 --- a/src/shared/fstab-util.h +++ b/src/shared/fstab-util.h @@ -23,10 +23,12 @@ #include <stdbool.h> #include <stddef.h> + #include "macro.h" -int fstab_filter_options(const char *opts, const char *names, - const char **namefound, char **value, char **filtered); +bool fstab_is_mount_point(const char *mount); + +int fstab_filter_options(const char *opts, const char *names, const char **namefound, char **value, char **filtered); int fstab_extract_values(const char *opts, const char *name, char ***values); @@ -48,3 +50,5 @@ static inline bool fstab_test_yes_no_option(const char *opts, const char *yes_no return opt == yes_no; } + +char *fstab_node_to_udev_node(const char *p); diff --git a/src/shared/generator.c b/src/shared/generator.c index e58bbea77c..9998c64416 100644 --- a/src/shared/generator.c +++ b/src/shared/generator.c @@ -21,21 +21,25 @@ #include <unistd.h> -#include "util.h" -#include "special.h" -#include "mkdir.h" -#include "unit-name.h" +#include "alloc-util.h" +#include "dropin.h" +#include "escape.h" +#include "fd-util.h" +#include "fileio.h" +#include "fstab-util.h" #include "generator.h" +#include "mkdir.h" +#include "mount-util.h" #include "path-util.h" -#include "fstab-util.h" -#include "fileio.h" -#include "dropin.h" +#include "special.h" +#include "string-util.h" +#include "unit-name.h" +#include "util.h" static int write_fsck_sysroot_service(const char *dir, const char *what) { - const char *unit; - _cleanup_free_ char *device = NULL; - _cleanup_free_ char *escaped; + _cleanup_free_ char *device = NULL, *escaped = NULL; _cleanup_fclose_ FILE *f = NULL; + const char *unit; int r; escaped = cescape(what); @@ -60,7 +64,7 @@ static int write_fsck_sysroot_service(const char *dir, const char *what) { "Description=File System Check on %2$s\n" "DefaultDependencies=no\n" "BindsTo=%3$s\n" - "After=%3$s\n" + "After=%3$s local-fs-pre.target\n" "Before=shutdown.target\n" "\n" "[Service]\n" @@ -101,16 +105,17 @@ int generator_write_fsck_deps( if (!isempty(fstype) && !streq(fstype, "auto")) { r = fsck_exists(fstype); - if (r == -ENOENT) { + if (r < 0) + log_warning_errno(r, "Checking was requested for %s, but couldn't detect if fsck.%s may be used, proceeding: %m", what, fstype); + else if (r == 0) { /* treat missing check as essentially OK */ - log_debug_errno(r, "Checking was requested for %s, but fsck.%s does not exist: %m", what, fstype); + log_debug("Checking was requested for %s, but fsck.%s does not exist.", what, fstype); return 0; - } else if (r < 0) - return log_warning_errno(r, "Checking was requested for %s, but fsck.%s cannot be used: %m", what, fstype); + } } if (path_equal(where, "/")) { - char *lnk; + const char *lnk; lnk = strjoina(dir, "/" SPECIAL_LOCAL_FS_TARGET ".wants/systemd-fsck-root.service"); @@ -137,7 +142,7 @@ int generator_write_fsck_deps( } fprintf(f, - "RequiresOverridable=%1$s\n" + "Requires=%1$s\n" "After=%1$s\n", fsck); } diff --git a/src/shared/import-util.c b/src/shared/import-util.c index 001a8a37e8..ddc8c00a2d 100644 --- a/src/shared/import-util.c +++ b/src/shared/import-util.c @@ -19,8 +19,13 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "util.h" +#include "alloc-util.h" +#include "btrfs-util.h" #include "import-util.h" +#include "path-util.h" +#include "string-table.h" +#include "string-util.h" +#include "util.h" int import_url_last_component(const char *url, char **ret) { const char *e, *p; @@ -201,3 +206,29 @@ bool dkr_id_is_valid(const char *id) { return true; } + +int import_assign_pool_quota_and_warn(const char *path) { + int r; + + r = btrfs_subvol_auto_qgroup("/var/lib/machines", 0, true); + if (r == -ENOTTY) { + log_debug_errno(r, "Failed to set up default quota hierarchy for /var/lib/machines, as directory is not on btrfs or not a subvolume. Ignoring."); + return 0; + } + if (r < 0) + return log_error_errno(r, "Failed to set up default quota hierarchy for /var/lib/machines: %m"); + if (r > 0) + log_info("Set up default quota hierarchy for /var/lib/machines."); + + r = btrfs_subvol_auto_qgroup(path, 0, true); + if (r == -ENOTTY) { + log_debug_errno(r, "Failed to set up quota hierarchy for %s, as directory is not on btrfs or not a subvolume. Ignoring.", path); + return 0; + } + if (r < 0) + return log_error_errno(r, "Failed to set up default quota hierarchy for %s: %m", path); + if (r > 0) + log_info("Set up default quota hierarchy for %s.", path); + + return 0; +} diff --git a/src/shared/import-util.h b/src/shared/import-util.h index 7bf7d4ca40..9120a5119f 100644 --- a/src/shared/import-util.h +++ b/src/shared/import-util.h @@ -47,3 +47,5 @@ bool dkr_id_is_valid(const char *id); bool dkr_ref_is_valid(const char *ref); bool dkr_digest_is_valid(const char *digest); #define dkr_tag_is_valid(tag) filename_is_valid(tag) + +int import_assign_pool_quota_and_warn(const char *path); diff --git a/src/shared/install-printf.c b/src/shared/install-printf.c index cbe984d2fb..74b909d34d 100644 --- a/src/shared/install-printf.c +++ b/src/shared/install-printf.c @@ -21,11 +21,13 @@ #include <stdlib.h> +#include "alloc-util.h" +#include "formats-util.h" +#include "install-printf.h" #include "specifier.h" #include "unit-name.h" +#include "user-util.h" #include "util.h" -#include "install-printf.h" -#include "formats-util.h" static int specifier_prefix_and_instance(char specifier, void *data, void *userdata, char **ret) { UnitFileInstallInfo *i = userdata; @@ -65,42 +67,28 @@ static int specifier_instance(char specifier, void *data, void *userdata, char * } static int specifier_user_name(char specifier, void *data, void *userdata, char **ret) { - UnitFileInstallInfo *i = userdata; - const char *username; - _cleanup_free_ char *tmp = NULL; - char *printed = NULL; - - assert(i); + char *t; - if (i->user) - username = i->user; - else - /* get USER env from env or our own uid */ - username = tmp = getusername_malloc(); - - switch (specifier) { - case 'u': - printed = strdup(username); - break; - case 'U': { - /* fish username from passwd */ - uid_t uid; - int r; - - r = get_user_creds(&username, &uid, NULL, NULL, NULL); - if (r < 0) - return r; - - if (asprintf(&printed, UID_FMT, uid) < 0) - return -ENOMEM; - break; - }} + /* If we are UID 0 (root), this will not result in NSS, + * otherwise it might. This is good, as we want to be able to + * run this in PID 1, where our user ID is 0, but where NSS + * lookups are not allowed. */ + t = getusername_malloc(); + if (!t) + return -ENOMEM; - *ret = printed; + *ret = t; return 0; } +static int specifier_user_id(char specifier, void *data, void *userdata, char **ret) { + + if (asprintf(ret, UID_FMT, getuid()) < 0) + return -ENOMEM; + + return 0; +} int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret) { @@ -112,8 +100,8 @@ int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret) * %p: the prefix (foo) * %i: the instance (bar) - * %U the UID of the configured user or running user - * %u the username of the configured user or running user + * %U the UID of the running user + * %u the username of running user * %m the machine ID of the running system * %H the host name of the running system * %b the boot ID of the running system @@ -126,7 +114,7 @@ int install_full_printf(UnitFileInstallInfo *i, const char *format, char **ret) { 'p', specifier_prefix, NULL }, { 'i', specifier_instance, NULL }, - { 'U', specifier_user_name, NULL }, + { 'U', specifier_user_id, NULL }, { 'u', specifier_user_name, NULL }, { 'm', specifier_machine_id, NULL }, diff --git a/src/shared/install.c b/src/shared/install.c index 3d2b5ae77f..17e03e59cd 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -21,40 +21,59 @@ #include <errno.h> #include <fcntl.h> -#include <unistd.h> -#include <string.h> #include <fnmatch.h> +#include <string.h> +#include <unistd.h> -#include "util.h" -#include "mkdir.h" +#include "alloc-util.h" +#include "conf-files.h" +#include "conf-parser.h" +#include "dirent-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "fs-util.h" #include "hashmap.h" -#include "set.h" -#include "path-util.h" +#include "install-printf.h" +#include "install.h" +#include "mkdir.h" #include "path-lookup.h" +#include "path-util.h" +#include "set.h" +#include "special.h" +#include "stat-util.h" +#include "string-table.h" +#include "string-util.h" #include "strv.h" #include "unit-name.h" -#include "install.h" -#include "conf-parser.h" -#include "conf-files.h" -#include "install-printf.h" -#include "special.h" +#include "util.h" + +#define UNIT_FILE_FOLLOW_SYMLINK_MAX 64 + +typedef enum SearchFlags { + SEARCH_LOAD = 1, + SEARCH_FOLLOW_CONFIG_SYMLINKS = 2, +} SearchFlags; typedef struct { - OrderedHashmap *will_install; - OrderedHashmap *have_installed; + OrderedHashmap *will_process; + OrderedHashmap *have_processed; } InstallContext; static int in_search_path(const char *path, char **search) { _cleanup_free_ char *parent = NULL; - int r; + char **i; assert(path); - r = path_get_parent(path, &parent); - if (r < 0) - return r; + parent = dirname_malloc(path); + if (!parent) + return -ENOMEM; - return strv_contains(search, parent); + STRV_FOREACH(i, search) + if (path_equal(parent, *i)) + return true; + + return false; } static int get_config_path(UnitFileScope scope, bool runtime, const char *root_dir, char **ret) { @@ -65,6 +84,9 @@ static int get_config_path(UnitFileScope scope, bool runtime, const char *root_d assert(scope < _UNIT_FILE_SCOPE_MAX); assert(ret); + /* This determines where we shall create or remove our + * installation ("configuration") symlinks */ + switch (scope) { case UNIT_FILE_SYSTEM: @@ -95,9 +117,10 @@ static int get_config_path(UnitFileScope scope, bool runtime, const char *root_d r = user_runtime_dir(&p); else r = user_config_home(&p); - - if (r <= 0) - return r < 0 ? r : -ENOENT; + if (r < 0) + return r; + if (r == 0) + return -ENOENT; break; @@ -112,6 +135,185 @@ static int get_config_path(UnitFileScope scope, bool runtime, const char *root_d return 0; } +static bool is_config_path(UnitFileScope scope, const char *path) { + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(path); + + /* Checks whether the specified path is intended for + * configuration or is outside of it */ + + switch (scope) { + + case UNIT_FILE_SYSTEM: + case UNIT_FILE_GLOBAL: + return path_startswith(path, "/etc") || + path_startswith(path, SYSTEM_CONFIG_UNIT_PATH) || + path_startswith(path, "/run"); + + + case UNIT_FILE_USER: { + _cleanup_free_ char *p = NULL; + + r = user_config_home(&p); + if (r < 0) + return r; + if (r > 0 && path_startswith(path, p)) + return true; + + p = mfree(p); + + r = user_runtime_dir(&p); + if (r < 0) + return r; + if (r > 0 && path_startswith(path, p)) + return true; + + return false; + } + + default: + assert_not_reached("Bad scope"); + } +} + + +static int verify_root_dir(UnitFileScope scope, const char **root_dir) { + int r; + + assert(root_dir); + + /* Verifies that the specified root directory to operate on + * makes sense. Reset it to NULL if it is the root directory + * or set to empty */ + + if (isempty(*root_dir) || path_equal(*root_dir, "/")) { + *root_dir = NULL; + return 0; + } + + if (scope != UNIT_FILE_SYSTEM) + return -EINVAL; + + r = is_dir(*root_dir, true); + if (r < 0) + return r; + if (r == 0) + return -ENOTDIR; + + return 0; +} + +int unit_file_changes_add( + UnitFileChange **changes, + unsigned *n_changes, + UnitFileChangeType type, + const char *path, + const char *source) { + + UnitFileChange *c; + unsigned i; + + assert(path); + assert(!changes == !n_changes); + + if (!changes) + return 0; + + c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange)); + if (!c) + return -ENOMEM; + + *changes = c; + i = *n_changes; + + c[i].type = type; + c[i].path = strdup(path); + if (!c[i].path) + return -ENOMEM; + + path_kill_slashes(c[i].path); + + if (source) { + c[i].source = strdup(source); + if (!c[i].source) { + free(c[i].path); + return -ENOMEM; + } + + path_kill_slashes(c[i].path); + } else + c[i].source = NULL; + + *n_changes = i+1; + return 0; +} + +void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) { + unsigned i; + + assert(changes || n_changes == 0); + + if (!changes) + return; + + for (i = 0; i < n_changes; i++) { + free(changes[i].path); + free(changes[i].source); + } + + free(changes); +} + +static int create_symlink( + const char *old_path, + const char *new_path, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + _cleanup_free_ char *dest = NULL; + int r; + + assert(old_path); + assert(new_path); + + /* Actually create a symlink, and remember that we did. Is + * smart enough to check if there's already a valid symlink in + * place. */ + + mkdir_parents_label(new_path, 0755); + + if (symlink(old_path, new_path) >= 0) { + unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path); + return 0; + } + + if (errno != EEXIST) + return -errno; + + r = readlink_malloc(new_path, &dest); + if (r < 0) + return r; + + if (path_equal(dest, old_path)) + return 0; + + if (!force) + return -EEXIST; + + r = symlink_atomic(old_path, new_path); + if (r < 0) + return r; + + unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL); + unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path); + + return 0; +} + static int mark_symlink_for_removal( Set **remove_symlinks_to, const char *p) { @@ -132,10 +334,12 @@ static int mark_symlink_for_removal( path_kill_slashes(n); r = set_consume(*remove_symlinks_to, n); + if (r == -EEXIST) + return 0; if (r < 0) - return r == -EEXIST ? 0 : r; + return r; - return 0; + return 1; } static int remove_marked_symlinks_fd( @@ -143,19 +347,19 @@ static int remove_marked_symlinks_fd( int fd, const char *path, const char *config_path, - bool *deleted, + bool *restart, UnitFileChange **changes, - unsigned *n_changes, - char** instance_whitelist) { + unsigned *n_changes) { _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; int r = 0; assert(remove_symlinks_to); assert(fd >= 0); assert(path); assert(config_path); - assert(deleted); + assert(restart); d = fdopendir(fd); if (!d) { @@ -165,27 +369,13 @@ static int remove_marked_symlinks_fd( rewinddir(d); - for (;;) { - struct dirent *de; - - errno = 0; - de = readdir(d); - if (!de && errno != 0) { - r = -errno; - break; - } - - if (!de) - break; - - if (hidden_file(de->d_name)) - continue; + FOREACH_DIRENT(de, d, return -errno) { dirent_ensure_type(d, de); if (de->d_type == DT_DIR) { - int nfd, q; _cleanup_free_ char *p = NULL; + int nfd, q; nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); if (nfd < 0) { @@ -204,42 +394,23 @@ static int remove_marked_symlinks_fd( } /* This will close nfd, regardless whether it succeeds or not */ - q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, deleted, changes, n_changes, instance_whitelist); + q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, restart, changes, n_changes); if (q < 0 && r == 0) r = q; } else if (de->d_type == DT_LNK) { _cleanup_free_ char *p = NULL, *dest = NULL; - int q; bool found; + int q; if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) continue; - if (unit_name_is_valid(de->d_name, UNIT_NAME_INSTANCE) && - instance_whitelist && - !strv_contains(instance_whitelist, de->d_name)) { - - _cleanup_free_ char *w = NULL; - - /* OK, the file is not listed directly - * in the whitelist, so let's check if - * the template of it might be - * listed. */ - - r = unit_name_template(de->d_name, &w); - if (r < 0) - return r; - - if (!strv_contains(instance_whitelist, w)) - continue; - } - p = path_make_absolute(de->d_name, path); if (!p) return -ENOMEM; - q = readlink_and_canonicalize(p, &dest); + q = readlink_malloc(p, &dest); if (q < 0) { if (q == -ENOENT) continue; @@ -249,9 +420,15 @@ static int remove_marked_symlinks_fd( continue; } + /* We remove all links pointing to a file or + * path that is marked, as well as all files + * sharing the same name as a file that is + * marked. */ + found = - set_get(remove_symlinks_to, dest) || - set_get(remove_symlinks_to, basename(dest)); + set_contains(remove_symlinks_to, dest) || + set_contains(remove_symlinks_to, basename(dest)) || + set_contains(remove_symlinks_to, de->d_name); if (!found) continue; @@ -263,18 +440,15 @@ static int remove_marked_symlinks_fd( } path_kill_slashes(p); - rmdir_parents(p, config_path); - unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, p, NULL); + (void) rmdir_parents(p, config_path); - if (!set_get(remove_symlinks_to, p)) { + unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, p, NULL); - q = mark_symlink_for_removal(&remove_symlinks_to, p); - if (q < 0) { - if (r == 0) - r = q; - } else - *deleted = true; - } + q = mark_symlink_for_removal(&remove_symlinks_to, p); + if (q < 0) + return q; + if (q > 0) + *restart = true; } } @@ -285,12 +459,11 @@ static int remove_marked_symlinks( Set *remove_symlinks_to, const char *config_path, UnitFileChange **changes, - unsigned *n_changes, - char** instance_whitelist) { + unsigned *n_changes) { _cleanup_close_ int fd = -1; + bool restart; int r = 0; - bool deleted; assert(config_path); @@ -303,32 +476,32 @@ static int remove_marked_symlinks( do { int q, cfd; - deleted = false; + restart = false; cfd = fcntl(fd, F_DUPFD_CLOEXEC, 3); - if (cfd < 0) { - r = -errno; - break; - } + if (cfd < 0) + return -errno; /* This takes possession of cfd and closes it */ - q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, &deleted, changes, n_changes, instance_whitelist); + q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, &restart, changes, n_changes); if (r == 0) r = q; - } while (deleted); + } while (restart); return r; } static int find_symlinks_fd( + const char *root_dir, const char *name, int fd, const char *path, const char *config_path, bool *same_name_link) { - int r = 0; _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + int r = 0; assert(name); assert(fd >= 0); @@ -342,25 +515,13 @@ static int find_symlinks_fd( return -errno; } - for (;;) { - struct dirent *de; - - errno = 0; - de = readdir(d); - if (!de && errno != 0) - return -errno; - - if (!de) - return r; - - if (hidden_file(de->d_name)) - continue; + FOREACH_DIRENT(de, d, return -errno) { dirent_ensure_type(d, de); if (de->d_type == DT_DIR) { - int nfd, q; _cleanup_free_ char *p = NULL; + int nfd, q; nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); if (nfd < 0) { @@ -379,7 +540,7 @@ static int find_symlinks_fd( } /* This will close nfd, regardless whether it succeeds or not */ - q = find_symlinks_fd(name, nfd, p, config_path, same_name_link); + q = find_symlinks_fd(root_dir, name, nfd, p, config_path, same_name_link); if (q > 0) return 1; if (r == 0) @@ -396,16 +557,27 @@ static int find_symlinks_fd( return -ENOMEM; /* Acquire symlink destination */ - q = readlink_and_canonicalize(p, &dest); + q = readlink_malloc(p, &dest); + if (q == -ENOENT) + continue; if (q < 0) { - if (q == -ENOENT) - continue; - if (r == 0) r = q; continue; } + /* Make absolute */ + if (!path_is_absolute(dest)) { + char *x; + + x = prefix_root(root_dir, dest); + if (!x) + return -ENOMEM; + + free(dest); + dest = x; + } + /* Check if the symlink itself matches what we * are looking for */ if (path_is_absolute(name)) @@ -438,9 +610,12 @@ static int find_symlinks_fd( return 1; } } + + return r; } static int find_symlinks( + const char *root_dir, const char *name, const char *config_path, bool *same_name_link) { @@ -459,7 +634,7 @@ static int find_symlinks( } /* This takes possession of fd and closes it */ - return find_symlinks_fd(name, fd, config_path, config_path, same_name_link); + return find_symlinks_fd(root_dir, name, fd, config_path, config_path, same_name_link); } static int find_symlinks_in_scope( @@ -468,350 +643,59 @@ static int find_symlinks_in_scope( const char *name, UnitFileState *state) { - int r; _cleanup_free_ char *normal_path = NULL, *runtime_path = NULL; bool same_name_link_runtime = false, same_name_link = false; + int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); assert(name); - /* First look in runtime config path */ - r = get_config_path(scope, true, root_dir, &normal_path); + /* First look in the normal config path */ + r = get_config_path(scope, false, root_dir, &normal_path); if (r < 0) return r; - r = find_symlinks(name, normal_path, &same_name_link_runtime); + r = find_symlinks(root_dir, name, normal_path, &same_name_link); if (r < 0) return r; - else if (r > 0) { - *state = UNIT_FILE_ENABLED_RUNTIME; + if (r > 0) { + *state = UNIT_FILE_ENABLED; return r; } - /* Then look in the normal config path */ - r = get_config_path(scope, false, root_dir, &runtime_path); + /* Then look in runtime config path */ + r = get_config_path(scope, true, root_dir, &runtime_path); if (r < 0) return r; - r = find_symlinks(name, runtime_path, &same_name_link); + r = find_symlinks(root_dir, name, runtime_path, &same_name_link_runtime); if (r < 0) return r; - else if (r > 0) { - *state = UNIT_FILE_ENABLED; + if (r > 0) { + *state = UNIT_FILE_ENABLED_RUNTIME; return r; } /* Hmm, we didn't find it, but maybe we found the same name * link? */ - if (same_name_link_runtime) { - *state = UNIT_FILE_LINKED_RUNTIME; - return 1; - } else if (same_name_link) { + if (same_name_link) { *state = UNIT_FILE_LINKED; return 1; } - - return 0; -} - -int unit_file_mask( - UnitFileScope scope, - bool runtime, - const char *root_dir, - char **files, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - char **i; - _cleanup_free_ char *prefix = NULL; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - r = get_config_path(scope, runtime, root_dir, &prefix); - if (r < 0) - return r; - - STRV_FOREACH(i, files) { - _cleanup_free_ char *path = NULL; - - if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) { - if (r == 0) - r = -EINVAL; - continue; - } - - path = path_make_absolute(*i, prefix); - if (!path) { - r = -ENOMEM; - break; - } - - if (symlink("/dev/null", path) >= 0) { - unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null"); - continue; - } - - if (errno == EEXIST) { - - if (null_or_empty_path(path) > 0) - continue; - - if (force) { - if (symlink_atomic("/dev/null", path) >= 0) { - unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); - unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null"); - continue; - } - } - - if (r == 0) - r = -EEXIST; - } else { - if (r == 0) - r = -errno; - } - } - - return r; -} - -int unit_file_unmask( - UnitFileScope scope, - bool runtime, - const char *root_dir, - char **files, - UnitFileChange **changes, - unsigned *n_changes) { - - char **i, *config_path = NULL; - int r, q; - Set *remove_symlinks_to = NULL; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - r = get_config_path(scope, runtime, root_dir, &config_path); - if (r < 0) - goto finish; - - STRV_FOREACH(i, files) { - _cleanup_free_ char *path = NULL; - - if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) { - if (r == 0) - r = -EINVAL; - continue; - } - - path = path_make_absolute(*i, config_path); - if (!path) { - r = -ENOMEM; - break; - } - - q = null_or_empty_path(path); - if (q > 0) { - if (unlink(path) < 0) - q = -errno; - else { - q = mark_symlink_for_removal(&remove_symlinks_to, path); - unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); - } - } - - if (q != -ENOENT && r == 0) - r = q; - } - - -finish: - q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files); - if (r == 0) - r = q; - - set_free_free(remove_symlinks_to); - free(config_path); - - return r; -} - -int unit_file_link( - UnitFileScope scope, - bool runtime, - const char *root_dir, - char **files, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_lookup_paths_free_ LookupPaths paths = {}; - char **i; - _cleanup_free_ char *config_path = NULL; - int r, q; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - r = lookup_paths_init_from_scope(&paths, scope, root_dir); - if (r < 0) - return r; - - r = get_config_path(scope, runtime, root_dir, &config_path); - if (r < 0) - return r; - - STRV_FOREACH(i, files) { - _cleanup_free_ char *path = NULL; - char *fn; - struct stat st; - - fn = basename(*i); - - if (!path_is_absolute(*i) || - !unit_name_is_valid(fn, UNIT_NAME_ANY)) { - if (r == 0) - r = -EINVAL; - continue; - } - - if (lstat(*i, &st) < 0) { - if (r == 0) - r = -errno; - continue; - } - - if (!S_ISREG(st.st_mode)) { - r = -ENOENT; - continue; - } - - q = in_search_path(*i, paths.unit_path); - if (q < 0) - return q; - - if (q > 0) - continue; - - path = path_make_absolute(fn, config_path); - if (!path) - return -ENOMEM; - - if (symlink(*i, path) >= 0) { - unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, *i); - continue; - } - - if (errno == EEXIST) { - _cleanup_free_ char *dest = NULL; - - q = readlink_and_make_absolute(path, &dest); - if (q < 0 && errno != ENOENT) { - if (r == 0) - r = q; - continue; - } - - if (q >= 0 && path_equal(dest, *i)) - continue; - - if (force) { - if (symlink_atomic(*i, path) >= 0) { - unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); - unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, path, *i); - continue; - } - } - - if (r == 0) - r = -EEXIST; - } else { - if (r == 0) - r = -errno; - } - } - - return r; -} - -void unit_file_list_free(Hashmap *h) { - UnitFileList *i; - - while ((i = hashmap_steal_first(h))) { - free(i->path); - free(i); + if (same_name_link_runtime) { + *state = UNIT_FILE_LINKED_RUNTIME; + return 1; } - hashmap_free(h); -} - -int unit_file_changes_add( - UnitFileChange **changes, - unsigned *n_changes, - UnitFileChangeType type, - const char *path, - const char *source) { - - UnitFileChange *c; - unsigned i; - - assert(path); - assert(!changes == !n_changes); - - if (!changes) - return 0; - - c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange)); - if (!c) - return -ENOMEM; - - *changes = c; - i = *n_changes; - - c[i].type = type; - c[i].path = strdup(path); - if (!c[i].path) - return -ENOMEM; - - path_kill_slashes(c[i].path); - - if (source) { - c[i].source = strdup(source); - if (!c[i].source) { - free(c[i].path); - return -ENOMEM; - } - - path_kill_slashes(c[i].path); - } else - c[i].source = NULL; - - *n_changes = i+1; return 0; } -void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) { - unsigned i; - - assert(changes || n_changes == 0); +static void install_info_free(UnitFileInstallInfo *i) { - if (!changes) + if (!i) return; - for (i = 0; i < n_changes; i++) { - free(changes[i].path); - free(changes[i].source); - } - - free(changes); -} - -static void install_info_free(UnitFileInstallInfo *i) { - assert(i); - free(i->name); free(i->path); strv_free(i->aliases); @@ -819,34 +703,45 @@ static void install_info_free(UnitFileInstallInfo *i) { strv_free(i->required_by); strv_free(i->also); free(i->default_instance); + free(i->symlink_target); free(i); } -static void install_info_hashmap_free(OrderedHashmap *m) { +static OrderedHashmap* install_info_hashmap_free(OrderedHashmap *m) { UnitFileInstallInfo *i; if (!m) - return; + return NULL; while ((i = ordered_hashmap_steal_first(m))) install_info_free(i); - ordered_hashmap_free(m); + return ordered_hashmap_free(m); } static void install_context_done(InstallContext *c) { assert(c); - install_info_hashmap_free(c->will_install); - install_info_hashmap_free(c->have_installed); + c->will_process = install_info_hashmap_free(c->will_process); + c->have_processed = install_info_hashmap_free(c->have_processed); +} - c->will_install = c->have_installed = NULL; +static UnitFileInstallInfo *install_info_find(InstallContext *c, const char *name) { + UnitFileInstallInfo *i; + + i = ordered_hashmap_get(c->have_processed, name); + if (i) + return i; + + return ordered_hashmap_get(c->will_process, name); } static int install_info_add( InstallContext *c, const char *name, - const char *path) { + const char *path, + UnitFileInstallInfo **ret) { + UnitFileInstallInfo *i = NULL; int r; @@ -859,17 +754,21 @@ static int install_info_add( if (!unit_name_is_valid(name, UNIT_NAME_ANY)) return -EINVAL; - if (ordered_hashmap_get(c->have_installed, name) || - ordered_hashmap_get(c->will_install, name)) + i = install_info_find(c, name); + if (i) { + if (ret) + *ret = i; return 0; + } - r = ordered_hashmap_ensure_allocated(&c->will_install, &string_hash_ops); + r = ordered_hashmap_ensure_allocated(&c->will_process, &string_hash_ops); if (r < 0) return r; i = new0(UnitFileInstallInfo, 1); if (!i) return -ENOMEM; + i->type = _UNIT_FILE_TYPE_INVALID; i->name = strdup(name); if (!i->name) { @@ -885,30 +784,32 @@ static int install_info_add( } } - r = ordered_hashmap_put(c->will_install, i->name, i); + r = ordered_hashmap_put(c->will_process, i->name, i); if (r < 0) goto fail; + if (ret) + *ret = i; + return 0; fail: - if (i) - install_info_free(i); - + install_info_free(i); return r; } static int install_info_add_auto( InstallContext *c, - const char *name_or_path) { + const char *name_or_path, + UnitFileInstallInfo **ret) { assert(c); assert(name_or_path); if (path_is_absolute(name_or_path)) - return install_info_add(c, NULL, name_or_path); + return install_info_add(c, NULL, name_or_path, ret); else - return install_info_add(c, name_or_path, NULL); + return install_info_add(c, name_or_path, NULL, ret); } static int config_parse_also( @@ -923,64 +824,33 @@ static int config_parse_also( void *data, void *userdata) { - size_t l; - const char *word, *state; - InstallContext *c = data; UnitFileInstallInfo *i = userdata; + InstallContext *c = data; + int r; assert(filename); assert(lvalue); assert(rvalue); - FOREACH_WORD_QUOTED(word, l, rvalue, state) { - _cleanup_free_ char *n; - int r; - - n = strndup(word, l); - if (!n) - return -ENOMEM; + for (;;) { + _cleanup_free_ char *word = NULL; - r = install_info_add(c, n, NULL); + r = extract_first_word(&rvalue, &word, NULL, 0); if (r < 0) return r; + if (r == 0) + break; - r = strv_extend(&i->also, n); + r = install_info_add(c, word, NULL, NULL); if (r < 0) return r; - } - if (!isempty(state)) - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Trailing garbage, ignoring."); - - return 0; -} -static int config_parse_user( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - UnitFileInstallInfo *i = data; - char *printed; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - r = install_full_printf(i, rvalue, &printed); - if (r < 0) - return r; + r = strv_push(&i->also, word); + if (r < 0) + return r; - free(i->user); - i->user = printed; + word = NULL; + } return 0; } @@ -1025,9 +895,7 @@ static int unit_file_load( UnitFileInstallInfo *info, const char *path, const char *root_dir, - bool allow_symlink, - bool load, - bool *also) { + SearchFlags flags) { const ConfigTableItem items[] = { { "Install", "Alias", config_parse_strv, 0, &info->aliases }, @@ -1035,34 +903,57 @@ static int unit_file_load( { "Install", "RequiredBy", config_parse_strv, 0, &info->required_by }, { "Install", "DefaultInstance", config_parse_default_instance, 0, info }, { "Install", "Also", config_parse_also, 0, c }, - { "Exec", "User", config_parse_user, 0, info }, {} }; _cleanup_fclose_ FILE *f = NULL; - int fd, r; + _cleanup_close_ int fd = -1; + struct stat st; + int r; assert(c); assert(info); assert(path); - if (!isempty(root_dir)) - path = strjoina(root_dir, "/", path); + path = prefix_roota(root_dir, path); - if (!load) { - r = access(path, F_OK) ? -errno : 0; - return r; + if (!(flags & SEARCH_LOAD)) { + r = lstat(path, &st); + if (r < 0) + return -errno; + + if (null_or_empty(&st)) + info->type = UNIT_FILE_TYPE_MASKED; + else if (S_ISREG(st.st_mode)) + info->type = UNIT_FILE_TYPE_REGULAR; + else if (S_ISLNK(st.st_mode)) + return -ELOOP; + else if (S_ISDIR(st.st_mode)) + return -EISDIR; + else + return -ENOTTY; + + return 0; } - fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|(allow_symlink ? 0 : O_NOFOLLOW)); + fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); if (fd < 0) return -errno; + if (fstat(fd, &st) < 0) + return -errno; + if (null_or_empty(&st)) { + info->type = UNIT_FILE_TYPE_MASKED; + return 0; + } + if (S_ISDIR(st.st_mode)) + return -EISDIR; + if (!S_ISREG(st.st_mode)) + return -ENOTTY; f = fdopen(fd, "re"); - if (!f) { - safe_close(fd); - return -ENOMEM; - } + if (!f) + return -errno; + fd = -1; r = config_parse(NULL, path, f, NULL, @@ -1071,8 +962,7 @@ static int unit_file_load( if (r < 0) return r; - if (also) - *also = !strv_isempty(info->also); + info->type = UNIT_FILE_TYPE_REGULAR; return (int) strv_length(info->aliases) + @@ -1080,14 +970,73 @@ static int unit_file_load( (int) strv_length(info->required_by); } +static int unit_file_load_or_readlink( + InstallContext *c, + UnitFileInstallInfo *info, + const char *path, + const char *root_dir, + SearchFlags flags) { + + _cleanup_free_ char *np = NULL; + int r; + + r = unit_file_load(c, info, path, root_dir, flags); + if (r != -ELOOP) + return r; + + /* This is a symlink, let's read it. */ + + r = readlink_and_make_absolute_root(root_dir, path, &np); + if (r < 0) + return r; + + if (path_equal(np, "/dev/null")) + info->type = UNIT_FILE_TYPE_MASKED; + else { + const char *bn; + UnitType a, b; + + bn = basename(np); + + if (unit_name_is_valid(info->name, UNIT_NAME_PLAIN)) { + + if (!unit_name_is_valid(bn, UNIT_NAME_PLAIN)) + return -EINVAL; + + } else if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) { + + if (!unit_name_is_valid(bn, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) + return -EINVAL; + + } else if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE)) { + + if (!unit_name_is_valid(bn, UNIT_NAME_TEMPLATE)) + return -EINVAL; + } else + return -EINVAL; + + /* Enforce that the symlink destination does not + * change the unit file type. */ + + a = unit_name_to_type(info->name); + b = unit_name_to_type(bn); + if (a < 0 || b < 0 || a != b) + return -EINVAL; + + info->type = UNIT_FILE_TYPE_SYMLINK; + info->symlink_target = np; + np = NULL; + } + + return 0; +} + static int unit_file_search( InstallContext *c, UnitFileInstallInfo *info, const LookupPaths *paths, const char *root_dir, - bool allow_symlink, - bool load, - bool *also) { + SearchFlags flags) { char **p; int r; @@ -1096,8 +1045,12 @@ static int unit_file_search( assert(info); assert(paths); + /* Was this unit already loaded? */ + if (info->type != _UNIT_FILE_TYPE_INVALID) + return 0; + if (info->path) - return unit_file_load(c, info, info->path, root_dir, allow_symlink, load, also); + return unit_file_load_or_readlink(c, info, info->path, root_dir, flags); assert(info->name); @@ -1108,14 +1061,15 @@ static int unit_file_search( if (!path) return -ENOMEM; - r = unit_file_load(c, info, path, root_dir, allow_symlink, load, also); - if (r >= 0) { + r = unit_file_load_or_readlink(c, info, path, root_dir, flags); + if (r < 0) { + if (r != -ENOENT) + return r; + } else { info->path = path; path = NULL; return r; } - if (r != -ENOENT && r != -ELOOP) - return r; } if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) { @@ -1137,92 +1091,149 @@ static int unit_file_search( if (!path) return -ENOMEM; - r = unit_file_load(c, info, path, root_dir, allow_symlink, load, also); - if (r >= 0) { + r = unit_file_load_or_readlink(c, info, path, root_dir, flags); + if (r < 0) { + if (r != -ENOENT) + return r; + } else { info->path = path; path = NULL; return r; } - if (r != -ENOENT && r != -ELOOP) - return r; } } return -ENOENT; } -static int unit_file_can_install( - const LookupPaths *paths, +static int install_info_follow( + InstallContext *c, + UnitFileInstallInfo *i, const char *root_dir, - const char *name, - bool allow_symlink, - bool *also) { + SearchFlags flags) { + + assert(c); + assert(i); + + if (i->type != UNIT_FILE_TYPE_SYMLINK) + return -EINVAL; + if (!i->symlink_target) + return -EINVAL; + + /* If the basename doesn't match, the caller should add a + * complete new entry for this. */ + + if (!streq(basename(i->symlink_target), i->name)) + return -EXDEV; + + free(i->path); + i->path = i->symlink_target; + i->symlink_target = NULL; + i->type = _UNIT_FILE_TYPE_INVALID; + + return unit_file_load_or_readlink(c, i, i->path, root_dir, flags); +} + +static int install_info_traverse( + UnitFileScope scope, + InstallContext *c, + const char *root_dir, + const LookupPaths *paths, + UnitFileInstallInfo *start, + SearchFlags flags, + UnitFileInstallInfo **ret) { - _cleanup_(install_context_done) InstallContext c = {}; UnitFileInstallInfo *i; + unsigned k = 0; int r; assert(paths); - assert(name); + assert(start); + assert(c); - r = install_info_add_auto(&c, name); + r = unit_file_search(c, start, paths, root_dir, flags); if (r < 0) return r; - assert_se(i = ordered_hashmap_first(c.will_install)); + i = start; + while (i->type == UNIT_FILE_TYPE_SYMLINK) { + /* Follow the symlink */ - r = unit_file_search(&c, i, paths, root_dir, allow_symlink, true, also); + if (++k > UNIT_FILE_FOLLOW_SYMLINK_MAX) + return -ELOOP; - if (r >= 0) - r = - (int) strv_length(i->aliases) + - (int) strv_length(i->wanted_by) + - (int) strv_length(i->required_by); + if (!(flags & SEARCH_FOLLOW_CONFIG_SYMLINKS) && is_config_path(scope, i->path)) + return -ELOOP; - return r; -} + r = install_info_follow(c, i, root_dir, flags); + if (r < 0) { + _cleanup_free_ char *buffer = NULL; + const char *bn; -static int create_symlink( - const char *old_path, - const char *new_path, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { + if (r != -EXDEV) + return r; - _cleanup_free_ char *dest = NULL; - int r; + /* Target has a different name, create a new + * install info object for that, and continue + * with that. */ - assert(old_path); - assert(new_path); + bn = basename(i->symlink_target); - mkdir_parents_label(new_path, 0755); + if (unit_name_is_valid(i->name, UNIT_NAME_INSTANCE) && + unit_name_is_valid(bn, UNIT_NAME_TEMPLATE)) { - if (symlink(old_path, new_path) >= 0) { - unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path); - return 0; + _cleanup_free_ char *instance = NULL; + + r = unit_name_to_instance(i->name, &instance); + if (r < 0) + return r; + + r = unit_name_replace_instance(bn, instance, &buffer); + if (r < 0) + return r; + + bn = buffer; + } + + r = install_info_add(c, bn, NULL, &i); + if (r < 0) + return r; + + r = unit_file_search(c, i, paths, root_dir, flags); + if (r < 0) + return r; + } + + /* Try again, with the new target we found. */ } - if (errno != EEXIST) - return -errno; + if (ret) + *ret = i; - r = readlink_and_make_absolute(new_path, &dest); - if (r < 0) - return r; + return 0; +} - if (path_equal(dest, old_path)) - return 0; +static int install_info_discover( + UnitFileScope scope, + InstallContext *c, + const char *root_dir, + const LookupPaths *paths, + const char *name, + SearchFlags flags, + UnitFileInstallInfo **ret) { - if (!force) - return -EEXIST; + UnitFileInstallInfo *i; + int r; - r = symlink_atomic(old_path, new_path); + assert(c); + assert(paths); + assert(name); + + r = install_info_add_auto(c, name, &i); if (r < 0) return r; - unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL); - unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path); - - return 0; + return install_info_traverse(scope, c, root_dir, paths, i, flags, ret); } static int install_info_symlink_alias( @@ -1357,6 +1368,9 @@ static int install_info_apply( assert(paths); assert(config_path); + if (i->type != UNIT_FILE_TYPE_REGULAR) + return 0; + r = install_info_symlink_alias(i, config_path, force, changes, n_changes); q = install_info_symlink_wants(i, config_path, i->wanted_by, ".wants/", force, changes, n_changes); @@ -1375,53 +1389,59 @@ static int install_info_apply( } static int install_context_apply( + UnitFileScope scope, InstallContext *c, const LookupPaths *paths, const char *config_path, const char *root_dir, bool force, + SearchFlags flags, UnitFileChange **changes, unsigned *n_changes) { UnitFileInstallInfo *i; - int r, q; + int r; assert(c); assert(paths); assert(config_path); - if (!ordered_hashmap_isempty(c->will_install)) { - r = ordered_hashmap_ensure_allocated(&c->have_installed, &string_hash_ops); - if (r < 0) - return r; + if (ordered_hashmap_isempty(c->will_process)) + return 0; - r = ordered_hashmap_reserve(c->have_installed, ordered_hashmap_size(c->will_install)); - if (r < 0) - return r; - } + r = ordered_hashmap_ensure_allocated(&c->have_processed, &string_hash_ops); + if (r < 0) + return r; r = 0; - while ((i = ordered_hashmap_first(c->will_install))) { - assert_se(ordered_hashmap_move_one(c->have_installed, c->will_install, i->name) == 0); + while ((i = ordered_hashmap_first(c->will_process))) { + int q; - q = unit_file_search(c, i, paths, root_dir, false, true, NULL); - if (q < 0) { - if (r >= 0) - r = q; + q = ordered_hashmap_move_one(c->have_processed, c->will_process, i->name); + if (q < 0) + return q; + r = install_info_traverse(scope, c, root_dir, paths, i, flags, NULL); + if (r < 0) return r; - } else if (r >= 0) - r += q; + + if (i->type != UNIT_FILE_TYPE_REGULAR) + continue; q = install_info_apply(i, paths, config_path, root_dir, force, changes, n_changes); - if (r >= 0 && q < 0) - r = q; + if (r >= 0) { + if (q < 0) + r = q; + else + r+= q; + } } return r; } static int install_context_mark_for_removal( + UnitFileScope scope, InstallContext *c, const LookupPaths *paths, Set **remove_symlinks_to, @@ -1429,7 +1449,7 @@ static int install_context_mark_for_removal( const char *root_dir) { UnitFileInstallInfo *i; - int r, q; + int r; assert(c); assert(paths); @@ -1437,87 +1457,182 @@ static int install_context_mark_for_removal( /* Marks all items for removal */ - if (!ordered_hashmap_isempty(c->will_install)) { - r = ordered_hashmap_ensure_allocated(&c->have_installed, &string_hash_ops); + if (ordered_hashmap_isempty(c->will_process)) + return 0; + + r = ordered_hashmap_ensure_allocated(&c->have_processed, &string_hash_ops); + if (r < 0) + return r; + + while ((i = ordered_hashmap_first(c->will_process))) { + + r = ordered_hashmap_move_one(c->have_processed, c->will_process, i->name); if (r < 0) return r; - r = ordered_hashmap_reserve(c->have_installed, ordered_hashmap_size(c->will_install)); + r = install_info_traverse(scope, c, root_dir, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL); if (r < 0) return r; - } - r = 0; - while ((i = ordered_hashmap_first(c->will_install))) { - assert_se(ordered_hashmap_move_one(c->have_installed, c->will_install, i->name) == 0); - - q = unit_file_search(c, i, paths, root_dir, false, true, NULL); - if (q == -ENOENT) { - /* do nothing */ - } else if (q < 0) { - if (r >= 0) - r = q; + if (i->type != UNIT_FILE_TYPE_REGULAR) + continue; + r = mark_symlink_for_removal(remove_symlinks_to, i->name); + if (r < 0) return r; - } else if (r >= 0) - r += q; - - if (unit_name_is_valid(i->name, UNIT_NAME_INSTANCE)) { - char *unit_file; - - if (i->path) { - unit_file = basename(i->path); - - if (unit_name_is_valid(unit_file, UNIT_NAME_INSTANCE)) - /* unit file named as instance exists, thus all symlinks - * pointing to it will be removed */ - q = mark_symlink_for_removal(remove_symlinks_to, i->name); - else - /* does not exist, thus we will mark for removal symlinks - * to template unit file */ - q = mark_symlink_for_removal(remove_symlinks_to, unit_file); - } else { - /* If i->path is not set, it means that we didn't actually find - * the unit file. But we can still remove symlinks to the - * nonexistent template. */ - r = unit_name_template(i->name, &unit_file); - if (r < 0) - return r; + } - q = mark_symlink_for_removal(remove_symlinks_to, unit_file); - free(unit_file); - } - } else - q = mark_symlink_for_removal(remove_symlinks_to, i->name); + return 0; +} + +int unit_file_mask( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + _cleanup_free_ char *prefix = NULL; + char **i; + int r; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; + + r = get_config_path(scope, runtime, root_dir, &prefix); + if (r < 0) + return r; + + STRV_FOREACH(i, files) { + _cleanup_free_ char *path = NULL; + int q; - if (r >= 0 && q < 0) + if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) { + if (r == 0) + r = -EINVAL; + continue; + } + + path = path_make_absolute(*i, prefix); + if (!path) + return -ENOMEM; + + q = create_symlink("/dev/null", path, force, changes, n_changes); + if (q < 0 && r >= 0) r = q; } return r; } -int unit_file_add_dependency( +int unit_file_unmask( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + UnitFileChange **changes, + unsigned *n_changes) { + + _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; + _cleanup_free_ char *config_path = NULL; + _cleanup_free_ char **todo = NULL; + size_t n_todo = 0, n_allocated = 0; + char **i; + int r, q; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; + + r = get_config_path(scope, runtime, root_dir, &config_path); + if (r < 0) + return r; + + STRV_FOREACH(i, files) { + _cleanup_free_ char *path = NULL; + + if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) + return -EINVAL; + + path = path_make_absolute(*i, config_path); + if (!path) + return -ENOMEM; + + r = null_or_empty_path(path); + if (r == -ENOENT) + continue; + if (r < 0) + return r; + if (r == 0) + continue; + + if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2)) + return -ENOMEM; + + todo[n_todo++] = *i; + } + + strv_uniq(todo); + + r = 0; + STRV_FOREACH(i, todo) { + _cleanup_free_ char *path = NULL; + + path = path_make_absolute(*i, config_path); + if (!path) + return -ENOMEM; + + if (unlink(path) < 0) { + if (errno != -ENOENT && r >= 0) + r = -errno; + } else { + q = mark_symlink_for_removal(&remove_symlinks_to, path); + if (q < 0) + return q; + + unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); + } + } + + q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); + if (r >= 0) + r = q; + + return r; +} + +int unit_file_link( UnitFileScope scope, bool runtime, const char *root_dir, char **files, - char *target, - UnitDependency dep, bool force, UnitFileChange **changes, unsigned *n_changes) { _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_(install_context_done) InstallContext c = {}; _cleanup_free_ char *config_path = NULL; + _cleanup_free_ char **todo = NULL; + size_t n_todo = 0, n_allocated = 0; char **i; - int r; - UnitFileInstallInfo *info; + int r, q; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; + r = lookup_paths_init_from_scope(&paths, scope, root_dir); if (r < 0) return r; @@ -1527,55 +1642,135 @@ int unit_file_add_dependency( return r; STRV_FOREACH(i, files) { - UnitFileState state; + _cleanup_free_ char *full = NULL; + struct stat st; + char *fn; - state = unit_file_get_state(scope, root_dir, *i); - if (state < 0) - return log_error_errno(state, "Failed to get unit file state for %s: %m", *i); + if (!path_is_absolute(*i)) + return -EINVAL; - if (state == UNIT_FILE_MASKED || state == UNIT_FILE_MASKED_RUNTIME) { - log_error("Failed to enable unit: Unit %s is masked", *i); - return -EOPNOTSUPP; - } + fn = basename(*i); + if (!unit_name_is_valid(fn, UNIT_NAME_ANY)) + return -EINVAL; - r = install_info_add_auto(&c, *i); - if (r < 0) - return r; + full = prefix_root(root_dir, *i); + if (!full) + return -ENOMEM; + + if (lstat(full, &st) < 0) + return -errno; + if (S_ISLNK(st.st_mode)) + return -ELOOP; + if (S_ISDIR(st.st_mode)) + return -EISDIR; + if (!S_ISREG(st.st_mode)) + return -ENOTTY; + + q = in_search_path(*i, paths.unit_path); + if (q < 0) + return q; + if (q > 0) + continue; + + if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2)) + return -ENOMEM; + + todo[n_todo++] = *i; } - if (!ordered_hashmap_isempty(c.will_install)) { - r = ordered_hashmap_ensure_allocated(&c.have_installed, &string_hash_ops); - if (r < 0) - return r; + strv_uniq(todo); - r = ordered_hashmap_reserve(c.have_installed, ordered_hashmap_size(c.will_install)); - if (r < 0) - return r; + r = 0; + STRV_FOREACH(i, todo) { + _cleanup_free_ char *path = NULL; + + path = path_make_absolute(basename(*i), config_path); + if (!path) + return -ENOMEM; + + q = create_symlink(*i, path, force, changes, n_changes); + if (q < 0 && r >= 0) + r = q; } - while ((info = ordered_hashmap_first(c.will_install))) { - assert_se(ordered_hashmap_move_one(c.have_installed, c.will_install, info->name) == 0); + return r; +} + +int unit_file_add_dependency( + UnitFileScope scope, + bool runtime, + const char *root_dir, + char **files, + const char *target, + UnitDependency dep, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + _cleanup_lookup_paths_free_ LookupPaths paths = {}; + _cleanup_(install_context_done) InstallContext c = {}; + _cleanup_free_ char *config_path = NULL; + UnitFileInstallInfo *i, *target_info; + char **f; + int r; - r = unit_file_search(&c, info, &paths, root_dir, false, false, NULL); + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(target); + + if (!IN_SET(dep, UNIT_WANTS, UNIT_REQUIRES)) + return -EINVAL; + + if (!unit_name_is_valid(target, UNIT_NAME_ANY)) + return -EINVAL; + + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; + + r = lookup_paths_init_from_scope(&paths, scope, root_dir); + if (r < 0) + return r; + + r = get_config_path(scope, runtime, root_dir, &config_path); + if (r < 0) + return r; + + r = install_info_discover(scope, &c, root_dir, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, &target_info); + if (r < 0) + return r; + if (target_info->type == UNIT_FILE_TYPE_MASKED) + return -ESHUTDOWN; + + assert(target_info->type == UNIT_FILE_TYPE_REGULAR); + + STRV_FOREACH(f, files) { + char ***l; + + r = install_info_discover(scope, &c, root_dir, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); if (r < 0) return r; + if (i->type == UNIT_FILE_TYPE_MASKED) + return -ESHUTDOWN; + + assert(i->type == UNIT_FILE_TYPE_REGULAR); + + /* We didn't actually load anything from the unit + * file, but instead just add in our new symlink to + * create. */ if (dep == UNIT_WANTS) - r = strv_extend(&info->wanted_by, target); - else if (dep == UNIT_REQUIRES) - r = strv_extend(&info->required_by, target); + l = &i->wanted_by; else - r = -EINVAL; - - if (r < 0) - return r; + l = &i->required_by; - r = install_info_apply(info, &paths, config_path, root_dir, force, changes, n_changes); - if (r < 0) - return r; + strv_free(*l); + *l = strv_new(target_info->name, NULL); + if (!*l) + return -ENOMEM; } - return 0; + return install_context_apply(scope, &c, &paths, config_path, root_dir, force, SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes); } int unit_file_enable( @@ -1589,13 +1784,18 @@ int unit_file_enable( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_(install_context_done) InstallContext c = {}; - char **i; _cleanup_free_ char *config_path = NULL; + UnitFileInstallInfo *i; + char **f; int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; + r = lookup_paths_init_from_scope(&paths, scope, root_dir); if (r < 0) return r; @@ -1604,29 +1804,22 @@ int unit_file_enable( if (r < 0) return r; - STRV_FOREACH(i, files) { - UnitFileState state; - - /* We only want to know if this unit is masked, so we ignore - * errors from unit_file_get_state, deferring other checks. - * This allows templated units to be enabled on the fly. */ - state = unit_file_get_state(scope, root_dir, *i); - if (state == UNIT_FILE_MASKED || state == UNIT_FILE_MASKED_RUNTIME) { - log_error("Failed to enable unit: Unit %s is masked", *i); - return -EOPNOTSUPP; - } - - r = install_info_add_auto(&c, *i); + STRV_FOREACH(f, files) { + r = install_info_discover(scope, &c, root_dir, &paths, *f, SEARCH_LOAD, &i); if (r < 0) return r; + if (i->type == UNIT_FILE_TYPE_MASKED) + return -ESHUTDOWN; + + assert(i->type == UNIT_FILE_TYPE_REGULAR); } /* This will return the number of symlink rules that were - supposed to be created, not the ones actually created. This is - useful to determine whether the passed files had any - installation data at all. */ + supposed to be created, not the ones actually created. This + is useful to determine whether the passed files had any + installation data at all. */ - return install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes); + return install_context_apply(scope, &c, &paths, config_path, root_dir, force, SEARCH_LOAD, changes, n_changes); } int unit_file_disable( @@ -1639,14 +1832,18 @@ int unit_file_disable( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_(install_context_done) InstallContext c = {}; - char **i; _cleanup_free_ char *config_path = NULL; _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - int r, q; + char **i; + int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; + r = lookup_paths_init_from_scope(&paths, scope, root_dir); if (r < 0) return r; @@ -1656,18 +1853,19 @@ int unit_file_disable( return r; STRV_FOREACH(i, files) { - r = install_info_add_auto(&c, *i); + if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) + return -EINVAL; + + r = install_info_add(&c, *i, NULL, NULL); if (r < 0) return r; } - r = install_context_mark_for_removal(&c, &paths, &remove_symlinks_to, config_path, root_dir); - - q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files); - if (r >= 0) - r = q; + r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path, root_dir); + if (r < 0) + return r; - return r; + return remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); } int unit_file_reenable( @@ -1678,21 +1876,30 @@ int unit_file_reenable( bool force, UnitFileChange **changes, unsigned *n_changes) { + + char **n; int r; + size_t l, i; + + /* First, we invoke the disable command with only the basename... */ + l = strv_length(files); + n = newa(char*, l+1); + for (i = 0; i < l; i++) + n[i] = basename(files[i]); + n[i] = NULL; - r = unit_file_disable(scope, runtime, root_dir, files, - changes, n_changes); + r = unit_file_disable(scope, runtime, root_dir, n, changes, n_changes); if (r < 0) return r; - return unit_file_enable(scope, runtime, root_dir, files, force, - changes, n_changes); + /* But the enable command with the full name */ + return unit_file_enable(scope, runtime, root_dir, files, force, changes, n_changes); } int unit_file_set_default( UnitFileScope scope, const char *root_dir, - const char *file, + const char *name, bool force, UnitFileChange **changes, unsigned *n_changes) { @@ -1700,42 +1907,40 @@ int unit_file_set_default( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_(install_context_done) InstallContext c = {}; _cleanup_free_ char *config_path = NULL; - char *path; + UnitFileInstallInfo *i; + const char *path; int r; - UnitFileInstallInfo *i = NULL; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(file); + assert(name); - if (unit_name_to_type(file) != UNIT_TARGET) + if (unit_name_to_type(name) != UNIT_TARGET) + return -EINVAL; + if (streq(name, SPECIAL_DEFAULT_TARGET)) return -EINVAL; - r = lookup_paths_init_from_scope(&paths, scope, root_dir); + r = verify_root_dir(scope, &root_dir); if (r < 0) return r; - r = get_config_path(scope, false, root_dir, &config_path); + r = lookup_paths_init_from_scope(&paths, scope, root_dir); if (r < 0) return r; - r = install_info_add_auto(&c, file); + r = get_config_path(scope, false, root_dir, &config_path); if (r < 0) return r; - assert_se(i = ordered_hashmap_first(c.will_install)); - - r = unit_file_search(&c, i, &paths, root_dir, false, true, NULL); + r = install_info_discover(scope, &c, root_dir, &paths, name, 0, &i); if (r < 0) return r; + if (i->type == UNIT_FILE_TYPE_MASKED) + return -ESHUTDOWN; path = strjoina(config_path, "/" SPECIAL_DEFAULT_TARGET); - r = create_symlink(i->path, path, force, changes, n_changes); - if (r < 0) - return r; - - return 0; + return create_symlink(i->path, path, force, changes, n_changes); } int unit_file_get_default( @@ -1744,126 +1949,101 @@ int unit_file_get_default( char **name) { _cleanup_lookup_paths_free_ LookupPaths paths = {}; - char **p; + _cleanup_(install_context_done) InstallContext c = {}; + UnitFileInstallInfo *i; + char *n; int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); assert(name); - r = lookup_paths_init_from_scope(&paths, scope, root_dir); + r = verify_root_dir(scope, &root_dir); if (r < 0) return r; - STRV_FOREACH(p, paths.unit_path) { - _cleanup_free_ char *path = NULL, *tmp = NULL; - char *n; - - path = path_join(root_dir, *p, SPECIAL_DEFAULT_TARGET); - if (!path) - return -ENOMEM; - - r = readlink_malloc(path, &tmp); - if (r == -ENOENT) - continue; - else if (r == -EINVAL) - /* not a symlink */ - n = strdup(SPECIAL_DEFAULT_TARGET); - else if (r < 0) - return r; - else - n = strdup(basename(tmp)); + r = lookup_paths_init_from_scope(&paths, scope, root_dir); + if (r < 0) + return r; - if (!n) - return -ENOMEM; + r = install_info_discover(scope, &c, root_dir, &paths, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + if (r < 0) + return r; + if (i->type == UNIT_FILE_TYPE_MASKED) + return -ESHUTDOWN; - *name = n; - return 0; - } + n = strdup(i->name); + if (!n) + return -ENOMEM; - return -ENOENT; + *name = n; + return 0; } -UnitFileState unit_file_lookup_state( +int unit_file_lookup_state( UnitFileScope scope, const char *root_dir, const LookupPaths *paths, - const char *name) { + const char *name, + UnitFileState *ret) { - UnitFileState state = _UNIT_FILE_STATE_INVALID; - char **i; - _cleanup_free_ char *path = NULL; - int r = 0; + _cleanup_(install_context_done) InstallContext c = {}; + UnitFileInstallInfo *i; + UnitFileState state; + int r; assert(paths); + assert(name); if (!unit_name_is_valid(name, UNIT_NAME_ANY)) return -EINVAL; - STRV_FOREACH(i, paths->unit_path) { - struct stat st; - char *partial; - bool also = false; + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; - free(path); - path = path_join(root_dir, *i, name); - if (!path) - return -ENOMEM; + r = install_info_discover(scope, &c, root_dir, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + if (r < 0) + return r; - if (root_dir) - partial = path + strlen(root_dir); - else - partial = path; - - /* - * Search for a unit file in our default paths, to - * be sure, that there are no broken symlinks. - */ - if (lstat(path, &st) < 0) { - r = -errno; - if (errno != ENOENT) - return r; + /* Shortcut things, if the caller just wants to know if this unit exists. */ + if (!ret) + return 0; - if (!unit_name_is_valid(name, UNIT_NAME_INSTANCE)) - continue; - } else { - if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) - return -ENOENT; + switch (i->type) { - r = null_or_empty_path(path); - if (r < 0 && r != -ENOENT) - return r; - else if (r > 0) { - state = path_startswith(*i, "/run") ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED; - return state; - } - } + case UNIT_FILE_TYPE_MASKED: + state = path_startswith(i->path, "/run") ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED; + break; - r = find_symlinks_in_scope(scope, root_dir, name, &state); + case UNIT_FILE_TYPE_REGULAR: + r = find_symlinks_in_scope(scope, root_dir, i->name, &state); if (r < 0) return r; - else if (r > 0) - return state; - - r = unit_file_can_install(paths, root_dir, partial, true, &also); - if (r < 0 && errno != ENOENT) - return r; - else if (r > 0) - return UNIT_FILE_DISABLED; - else if (r == 0) { - if (also) - return UNIT_FILE_INDIRECT; - return UNIT_FILE_STATIC; + if (r == 0) { + if (UNIT_FILE_INSTALL_INFO_HAS_RULES(i)) + state = UNIT_FILE_DISABLED; + else if (UNIT_FILE_INSTALL_INFO_HAS_ALSO(i)) + state = UNIT_FILE_INDIRECT; + else + state = UNIT_FILE_STATIC; } + + break; + + default: + assert_not_reached("Unexpect unit file type."); } - return r < 0 ? r : state; + *ret = state; + return 0; } -UnitFileState unit_file_get_state( +int unit_file_get_state( UnitFileScope scope, const char *root_dir, - const char *name) { + const char *name, + UnitFileState *ret) { _cleanup_lookup_paths_free_ LookupPaths paths = {}; int r; @@ -1872,14 +2052,15 @@ UnitFileState unit_file_get_state( assert(scope < _UNIT_FILE_SCOPE_MAX); assert(name); - if (root_dir && scope != UNIT_FILE_SYSTEM) - return -EINVAL; + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; r = lookup_paths_init_from_scope(&paths, scope, root_dir); if (r < 0) return r; - return unit_file_lookup_state(scope, root_dir, &paths, name); + return unit_file_lookup_state(scope, root_dir, &paths, name, ret); } int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) { @@ -1891,6 +2072,13 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char assert(scope < _UNIT_FILE_SCOPE_MAX); assert(name); + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; + + if (!unit_name_is_valid(name, UNIT_NAME_ANY)) + return -EINVAL; + if (scope == UNIT_FILE_SYSTEM) r = conf_files_list(&files, ".preset", root_dir, "/etc/systemd/system-preset", @@ -1907,13 +2095,14 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char "/usr/lib/systemd/user-preset", NULL); else - return 1; + return 1; /* Default is "enable" */ if (r < 0) return r; STRV_FOREACH(p, files) { _cleanup_fclose_ FILE *f; + char line[LINE_MAX]; f = fopen(*p, "re"); if (!f) { @@ -1923,39 +2112,38 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char return -errno; } - for (;;) { - char line[LINE_MAX], *l; - - if (!fgets(line, sizeof(line), f)) - break; + FOREACH_LINE(line, f, return -errno) { + const char *parameter; + char *l; l = strstrip(line); - if (!*l) - continue; - if (strchr(COMMENTS "\n", *l)) + if (isempty(l)) + continue; + if (strchr(COMMENTS, *l)) continue; - if (first_word(l, "enable")) { - l += 6; - l += strspn(l, WHITESPACE); - - if (fnmatch(l, name, FNM_NOESCAPE) == 0) { + parameter = first_word(l, "enable"); + if (parameter) { + if (fnmatch(parameter, name, FNM_NOESCAPE) == 0) { log_debug("Preset file says enable %s.", name); return 1; } - } else if (first_word(l, "disable")) { - l += 7; - l += strspn(l, WHITESPACE); + continue; + } - if (fnmatch(l, name, FNM_NOESCAPE) == 0) { + parameter = first_word(l, "disable"); + if (parameter) { + if (fnmatch(parameter, name, FNM_NOESCAPE) == 0) { log_debug("Preset file says disable %s.", name); return 0; } - } else - log_debug("Couldn't parse line '%s'", l); + continue; + } + + log_debug("Couldn't parse line '%s'", l); } } @@ -1964,6 +2152,86 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char return 1; } +static int execute_preset( + UnitFileScope scope, + InstallContext *plus, + InstallContext *minus, + const LookupPaths *paths, + const char *config_path, + const char *root_dir, + char **files, + UnitFilePresetMode mode, + bool force, + UnitFileChange **changes, + unsigned *n_changes) { + + int r; + + assert(plus); + assert(minus); + assert(paths); + assert(config_path); + + if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) { + _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; + + r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path, root_dir); + if (r < 0) + return r; + + r = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes); + } else + r = 0; + + if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) { + int q; + + /* Returns number of symlinks that where supposed to be installed. */ + q = install_context_apply(scope, plus, paths, config_path, root_dir, force, SEARCH_LOAD, changes, n_changes); + if (r >= 0) { + if (q < 0) + r = q; + else + r+= q; + } + } + + return r; +} + +static int preset_prepare_one( + UnitFileScope scope, + InstallContext *plus, + InstallContext *minus, + LookupPaths *paths, + const char *root_dir, + UnitFilePresetMode mode, + const char *name) { + + UnitFileInstallInfo *i; + int r; + + if (install_info_find(plus, name) || + install_info_find(minus, name)) + return 0; + + r = unit_file_query_preset(scope, root_dir, name); + if (r < 0) + return r; + + if (r > 0) { + r = install_info_discover(scope, plus, root_dir, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + if (r < 0) + return r; + + if (i->type == UNIT_FILE_TYPE_MASKED) + return -ESHUTDOWN; + } else + r = install_info_discover(scope, minus, root_dir, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i); + + return r; +} + int unit_file_preset( UnitFileScope scope, bool runtime, @@ -1978,12 +2246,16 @@ int unit_file_preset( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_free_ char *config_path = NULL; char **i; - int r, q; + int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); assert(mode < _UNIT_FILE_PRESET_MAX); + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; + r = lookup_paths_init_from_scope(&paths, scope, root_dir); if (r < 0) return r; @@ -1993,44 +2265,15 @@ int unit_file_preset( return r; STRV_FOREACH(i, files) { - if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) return -EINVAL; - r = unit_file_query_preset(scope, root_dir, *i); - if (r < 0) - return r; - - if (r && mode != UNIT_FILE_PRESET_DISABLE_ONLY) - r = install_info_add_auto(&plus, *i); - else if (!r && mode != UNIT_FILE_PRESET_ENABLE_ONLY) - r = install_info_add_auto(&minus, *i); - else - r = 0; + r = preset_prepare_one(scope, &plus, &minus, &paths, root_dir, mode, *i); if (r < 0) return r; } - r = 0; - - if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) { - _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - - r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir); - - q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files); - if (r == 0) - r = q; - } - - if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) { - /* Returns number of symlinks that where supposed to be installed. */ - q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes); - if (r == 0) - r = q; - } - - return r; + return execute_preset(scope, &plus, &minus, &paths, config_path, root_dir, files, mode, force, changes, n_changes); } int unit_file_preset_all( @@ -2046,12 +2289,16 @@ int unit_file_preset_all( _cleanup_lookup_paths_free_ LookupPaths paths = {}; _cleanup_free_ char *config_path = NULL; char **i; - int r, q; + int r; assert(scope >= 0); assert(scope < _UNIT_FILE_SCOPE_MAX); assert(mode < _UNIT_FILE_PRESET_MAX); + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; + r = lookup_paths_init_from_scope(&paths, scope, root_dir); if (r < 0) return r; @@ -2063,6 +2310,7 @@ int unit_file_preset_all( STRV_FOREACH(i, paths.unit_path) { _cleanup_closedir_ DIR *d = NULL; _cleanup_free_ char *units_dir; + struct dirent *de; units_dir = path_join(root_dir, *i, NULL); if (!units_dir) @@ -2076,62 +2324,23 @@ int unit_file_preset_all( return -errno; } - for (;;) { - struct dirent *de; - - errno = 0; - de = readdir(d); - if (!de && errno != 0) - return -errno; - - if (!de) - break; - - if (hidden_file(de->d_name)) - continue; + FOREACH_DIRENT(de, d, return -errno) { if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) continue; dirent_ensure_type(d, de); - if (de->d_type != DT_REG) + if (!IN_SET(de->d_type, DT_LNK, DT_REG)) continue; - r = unit_file_query_preset(scope, root_dir, de->d_name); - if (r < 0) - return r; - - if (r && mode != UNIT_FILE_PRESET_DISABLE_ONLY) - r = install_info_add_auto(&plus, de->d_name); - else if (!r && mode != UNIT_FILE_PRESET_ENABLE_ONLY) - r = install_info_add_auto(&minus, de->d_name); - else - r = 0; + r = preset_prepare_one(scope, &plus, &minus, &paths, root_dir, mode, de->d_name); if (r < 0) return r; } } - r = 0; - - if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) { - _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - - r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir); - - q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, NULL); - if (r == 0) - r = q; - } - - if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) { - q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes); - if (r == 0) - r = q; - } - - return r; + return execute_preset(scope, &plus, &minus, &paths, config_path, root_dir, NULL, mode, force, changes, n_changes); } static void unit_file_list_free_one(UnitFileList *f) { @@ -2142,6 +2351,15 @@ static void unit_file_list_free_one(UnitFileList *f) { free(f); } +Hashmap* unit_file_list_free(Hashmap *h) { + UnitFileList *i; + + while ((i = hashmap_steal_first(h))) + unit_file_list_free_one(i); + + return hashmap_free(h); +} + DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList*, unit_file_list_free_one); int unit_file_get_list( @@ -2157,14 +2375,9 @@ int unit_file_get_list( assert(scope < _UNIT_FILE_SCOPE_MAX); assert(h); - if (root_dir && scope != UNIT_FILE_SYSTEM) - return -EINVAL; - - if (root_dir) { - r = access(root_dir, F_OK); - if (r < 0) - return -errno; - } + r = verify_root_dir(scope, &root_dir); + if (r < 0) + return r; r = lookup_paths_init_from_scope(&paths, scope, root_dir); if (r < 0) @@ -2173,6 +2386,7 @@ int unit_file_get_list( STRV_FOREACH(i, paths.unit_path) { _cleanup_closedir_ DIR *d = NULL; _cleanup_free_ char *units_dir; + struct dirent *de; units_dir = path_join(root_dir, *i, NULL); if (!units_dir) @@ -2186,22 +2400,8 @@ int unit_file_get_list( return -errno; } - for (;;) { + FOREACH_DIRENT(de, d, return -errno) { _cleanup_(unit_file_list_free_onep) UnitFileList *f = NULL; - struct dirent *de; - _cleanup_free_ char *path = NULL; - bool also = false; - - errno = 0; - de = readdir(d); - if (!de && errno != 0) - return -errno; - - if (!de) - break; - - if (hidden_file(de->d_name)) - continue; if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) continue; @@ -2222,44 +2422,14 @@ int unit_file_get_list( if (!f->path) return -ENOMEM; - r = null_or_empty_path(f->path); - if (r < 0 && r != -ENOENT) - return r; - else if (r > 0) { - f->state = - path_startswith(*i, "/run") ? - UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED; - goto found; - } - - r = find_symlinks_in_scope(scope, root_dir, de->d_name, &f->state); + r = unit_file_lookup_state(scope, root_dir, &paths, basename(f->path), &f->state); if (r < 0) - return r; - else if (r > 0) { - f->state = UNIT_FILE_ENABLED; - goto found; - } - - path = path_make_absolute(de->d_name, *i); - if (!path) - return -ENOMEM; + f->state = UNIT_FILE_BAD; - r = unit_file_can_install(&paths, root_dir, path, true, &also); - if (r == -EINVAL || /* Invalid setting? */ - r == -EBADMSG || /* Invalid format? */ - r == -ENOENT /* Included file not found? */) - f->state = UNIT_FILE_INVALID; - else if (r < 0) - return r; - else if (r > 0) - f->state = UNIT_FILE_DISABLED; - else - f->state = also ? UNIT_FILE_INDIRECT : UNIT_FILE_STATIC; - - found: r = hashmap_put(h, basename(f->path), f); if (r < 0) return r; + f = NULL; /* prevent cleanup */ } } @@ -2277,7 +2447,7 @@ static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = { [UNIT_FILE_STATIC] = "static", [UNIT_FILE_DISABLED] = "disabled", [UNIT_FILE_INDIRECT] = "indirect", - [UNIT_FILE_INVALID] = "invalid", + [UNIT_FILE_BAD] = "bad", }; DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState); diff --git a/src/shared/install.h b/src/shared/install.h index a9d77dd91b..45a417df92 100644 --- a/src/shared/install.h +++ b/src/shared/install.h @@ -25,13 +25,15 @@ typedef enum UnitFileScope UnitFileScope; typedef enum UnitFileState UnitFileState; typedef enum UnitFilePresetMode UnitFilePresetMode; typedef enum UnitFileChangeType UnitFileChangeType; +typedef enum UnitFileType UnitFileType; typedef struct UnitFileChange UnitFileChange; typedef struct UnitFileList UnitFileList; typedef struct UnitFileInstallInfo UnitFileInstallInfo; #include "hashmap.h" -#include "unit-name.h" #include "path-lookup.h" +#include "strv.h" +#include "unit-name.h" enum UnitFileScope { UNIT_FILE_SYSTEM, @@ -51,7 +53,7 @@ enum UnitFileState { UNIT_FILE_STATIC, UNIT_FILE_DISABLED, UNIT_FILE_INDIRECT, - UNIT_FILE_INVALID, + UNIT_FILE_BAD, _UNIT_FILE_STATE_MAX, _UNIT_FILE_STATE_INVALID = -1 }; @@ -82,10 +84,17 @@ struct UnitFileList { UnitFileState state; }; +enum UnitFileType { + UNIT_FILE_TYPE_REGULAR, + UNIT_FILE_TYPE_SYMLINK, + UNIT_FILE_TYPE_MASKED, + _UNIT_FILE_TYPE_MAX, + _UNIT_FILE_TYPE_INVALID = -1, +}; + struct UnitFileInstallInfo { char *name; char *path; - char *user; char **aliases; char **wanted_by; @@ -93,8 +102,26 @@ struct UnitFileInstallInfo { char **also; char *default_instance; + + UnitFileType type; + + char *symlink_target; }; +static inline bool UNIT_FILE_INSTALL_INFO_HAS_RULES(UnitFileInstallInfo *i) { + assert(i); + + return !strv_isempty(i->aliases) || + !strv_isempty(i->wanted_by) || + !strv_isempty(i->required_by); +} + +static inline bool UNIT_FILE_INSTALL_INFO_HAS_ALSO(UnitFileInstallInfo *i) { + assert(i); + + return !strv_isempty(i->also); +} + int unit_file_enable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes); int unit_file_disable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFileChange **changes, unsigned *n_changes); int unit_file_reenable(UnitFileScope scope, bool runtime, const char *root_dir, char **files, bool force, UnitFileChange **changes, unsigned *n_changes); @@ -105,21 +132,14 @@ int unit_file_mask(UnitFileScope scope, bool runtime, const char *root_dir, char int unit_file_unmask(UnitFileScope scope, bool runtime, const char *root_dir, char **files, UnitFileChange **changes, unsigned *n_changes); int unit_file_set_default(UnitFileScope scope, const char *root_dir, const char *file, bool force, UnitFileChange **changes, unsigned *n_changes); int unit_file_get_default(UnitFileScope scope, const char *root_dir, char **name); -int unit_file_add_dependency(UnitFileScope scope, bool runtime, const char *root_dir, char **files, char *target, UnitDependency dep, bool force, UnitFileChange **changes, unsigned *n_changes); - -UnitFileState unit_file_lookup_state( - UnitFileScope scope, - const char *root_dir, - const LookupPaths *paths, - const char *name); -UnitFileState unit_file_get_state( - UnitFileScope scope, - const char *root_dir, - const char *filename); +int unit_file_add_dependency(UnitFileScope scope, bool runtime, const char *root_dir, char **files, const char *target, UnitDependency dep, bool force, UnitFileChange **changes, unsigned *n_changes); + +int unit_file_lookup_state(UnitFileScope scope, const char *root_dir,const LookupPaths *paths, const char *name, UnitFileState *ret); +int unit_file_get_state(UnitFileScope scope, const char *root_dir, const char *filename, UnitFileState *ret); int unit_file_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h); +Hashmap* unit_file_list_free(Hashmap *h); -void unit_file_list_free(Hashmap *h); int unit_file_changes_add(UnitFileChange **changes, unsigned *n_changes, UnitFileChangeType type, const char *path, const char *source); void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes); diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index 7790c1a3c8..0313b0946f 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -19,25 +19,30 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <time.h> #include <errno.h> -#include <sys/socket.h> -#include <string.h> #include <fcntl.h> +#include <string.h> +#include <sys/socket.h> +#include <time.h> -#include "logs-show.h" -#include "log.h" -#include "util.h" -#include "utf8.h" +#include "alloc-util.h" +#include "fd-util.h" +#include "formats-util.h" #include "hashmap.h" +#include "hostname-util.h" +#include "io-util.h" #include "journal-internal.h" -#include "formats-util.h" +#include "log.h" +#include "logs-show.h" +#include "parse-util.h" #include "process-util.h" +#include "string-table.h" +#include "string-util.h" #include "terminal-util.h" -#include "hostname-util.h" +#include "utf8.h" +#include "util.h" -/* up to three lines (each up to 100 characters), - or 300 characters, whichever is less */ +/* up to three lines (each up to 100 characters) or 300 characters, whichever is less */ #define PRINT_LINE_THRESHOLD 3 #define PRINT_CHAR_THRESHOLD 300 @@ -117,11 +122,11 @@ static bool print_multiline(FILE *f, unsigned prefix, unsigned n_columns, Output if (flags & OUTPUT_COLOR) { if (priority <= LOG_ERR) { - color_on = ANSI_HIGHLIGHT_RED_ON; - color_off = ANSI_HIGHLIGHT_OFF; + color_on = ANSI_HIGHLIGHT_RED; + color_off = ANSI_NORMAL; } else if (priority <= LOG_NOTICE) { - color_on = ANSI_HIGHLIGHT_ON; - color_off = ANSI_HIGHLIGHT_OFF; + color_on = ANSI_HIGHLIGHT; + color_off = ANSI_NORMAL; } } @@ -455,8 +460,8 @@ static int output_verbose( fieldlen = c - (const char*) data; if (flags & OUTPUT_COLOR && startswith(data, "MESSAGE=")) { - on = ANSI_HIGHLIGHT_ON; - off = ANSI_HIGHLIGHT_OFF; + on = ANSI_HIGHLIGHT; + off = ANSI_NORMAL; } if (flags & OUTPUT_SHOW_ALL || diff --git a/src/shared/logs-show.h b/src/shared/logs-show.h index 569e1faa55..98927bbc59 100644 --- a/src/shared/logs-show.h +++ b/src/shared/logs-show.h @@ -26,8 +26,8 @@ #include "sd-journal.h" -#include "util.h" #include "output-mode.h" +#include "util.h" int output_journal( FILE *f, diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c index 70220bdd14..2c1da0a40d 100644 --- a/src/shared/machine-image.c +++ b/src/shared/machine-image.c @@ -19,17 +19,26 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/statfs.h> -#include <linux/fs.h> #include <fcntl.h> +#include <linux/fs.h> +#include <sys/statfs.h> -#include "utf8.h" +#include "alloc-util.h" #include "btrfs-util.h" -#include "path-util.h" +#include "chattr-util.h" #include "copy.h" +#include "dirent-util.h" +#include "fd-util.h" +#include "fs-util.h" +#include "machine-image.h" #include "mkdir.h" +#include "path-util.h" #include "rm-rf.h" -#include "machine-image.h" +#include "string-table.h" +#include "string-util.h" +#include "strv.h" +#include "utf8.h" +#include "xattr-util.h" static const char image_search_path[] = "/var/lib/machines\0" @@ -47,6 +56,38 @@ Image *image_unref(Image *i) { return NULL; } +static char **image_settings_path(Image *image) { + _cleanup_strv_free_ char **l = NULL; + char **ret; + const char *fn, *s; + unsigned i = 0; + + assert(image); + + l = new0(char*, 4); + if (!l) + return NULL; + + fn = strjoina(image->name, ".nspawn"); + + FOREACH_STRING(s, "/etc/systemd/nspawn/", "/run/systemd/nspawn/") { + l[i] = strappend(s, fn); + if (!l[i]) + return NULL; + + i++; + } + + l[i] = file_in_same_dir(image->path, fn); + if (!l[i]) + return NULL; + + ret = l; + l = NULL; + + return ret; +} + static int image_new( ImageType t, const char *pretty, @@ -142,11 +183,10 @@ static int image_make( return r; if (r) { BtrfsSubvolInfo info; - BtrfsQuotaInfo quota; /* It's a btrfs subvolume */ - r = btrfs_subvol_get_info_fd(fd, &info); + r = btrfs_subvol_get_info_fd(fd, 0, &info); if (r < 0) return r; @@ -161,13 +201,17 @@ static int image_make( if (r < 0) return r; - r = btrfs_subvol_get_quota_fd(fd, "a); - if (r >= 0) { - (*ret)->usage = quota.referenced; - (*ret)->usage_exclusive = quota.exclusive; + if (btrfs_quota_scan_ongoing(fd) == 0) { + BtrfsQuotaInfo quota; - (*ret)->limit = quota.referenced_max; - (*ret)->limit_exclusive = quota.exclusive_max; + r = btrfs_subvol_get_subtree_quota_fd(fd, 0, "a); + if (r >= 0) { + (*ret)->usage = quota.referenced; + (*ret)->usage_exclusive = quota.exclusive; + + (*ret)->limit = quota.referenced_max; + (*ret)->limit_exclusive = quota.exclusive_max; + } } return 1; @@ -341,6 +385,8 @@ void image_hashmap_free(Hashmap *map) { int image_remove(Image *i) { _cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT; + _cleanup_strv_free_ char **settings = NULL; + char **j; int r; assert(i); @@ -349,6 +395,10 @@ int image_remove(Image *i) { path_startswith(i->path, "/usr")) return -EROFS; + settings = image_settings_path(i); + if (!settings) + return -ENOMEM; + /* Make sure we don't interfere with a running nspawn */ r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock); if (r < 0) @@ -357,28 +407,56 @@ int image_remove(Image *i) { switch (i->type) { case IMAGE_SUBVOLUME: - return btrfs_subvol_remove(i->path, true); + r = btrfs_subvol_remove(i->path, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA); + if (r < 0) + return r; + break; case IMAGE_DIRECTORY: /* Allow deletion of read-only directories */ (void) chattr_path(i->path, false, FS_IMMUTABLE_FL); - return rm_rf(i->path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); + r = rm_rf(i->path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME); + if (r < 0) + return r; + + break; case IMAGE_RAW: if (unlink(i->path) < 0) return -errno; - - return 0; + break; default: return -EOPNOTSUPP; } + + STRV_FOREACH(j, settings) { + if (unlink(*j) < 0 && errno != ENOENT) + log_debug_errno(errno, "Failed to unlink %s, ignoring: %m", *j); + } + + return 0; +} + +static int rename_settings_file(const char *path, const char *new_name) { + _cleanup_free_ char *rs = NULL; + const char *fn; + + fn = strjoina(new_name, ".nspawn"); + + rs = file_in_same_dir(path, fn); + if (!rs) + return -ENOMEM; + + return rename_noreplace(AT_FDCWD, path, AT_FDCWD, rs); } int image_rename(Image *i, const char *new_name) { _cleanup_release_lock_file_ LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT, name_lock = LOCK_FILE_INIT; _cleanup_free_ char *new_path = NULL, *nn = NULL; + _cleanup_strv_free_ char **settings = NULL; unsigned file_attr = 0; + char **j; int r; assert(i); @@ -390,6 +468,10 @@ int image_rename(Image *i, const char *new_name) { path_startswith(i->path, "/usr")) return -EROFS; + settings = image_settings_path(i); + if (!settings) + return -ENOMEM; + /* Make sure we don't interfere with a running nspawn */ r = image_path_lock(i->path, LOCK_EX|LOCK_NB, &global_lock, &local_lock); if (r < 0) @@ -458,12 +540,33 @@ int image_rename(Image *i, const char *new_name) { i->name = nn; nn = NULL; + STRV_FOREACH(j, settings) { + r = rename_settings_file(*j, new_name); + if (r < 0 && r != -ENOENT) + log_debug_errno(r, "Failed to rename settings file %s, ignoring: %m", *j); + } + return 0; } +static int clone_settings_file(const char *path, const char *new_name) { + _cleanup_free_ char *rs = NULL; + const char *fn; + + fn = strjoina(new_name, ".nspawn"); + + rs = file_in_same_dir(path, fn); + if (!rs) + return -ENOMEM; + + return copy_file_atomic(path, rs, 0664, false, 0); +} + int image_clone(Image *i, const char *new_name, bool read_only) { _cleanup_release_lock_file_ LockFile name_lock = LOCK_FILE_INIT; + _cleanup_strv_free_ char **settings = NULL; const char *new_path; + char **j; int r; assert(i); @@ -471,6 +574,10 @@ int image_clone(Image *i, const char *new_name, bool read_only) { if (!image_name_is_valid(new_name)) return -EINVAL; + settings = image_settings_path(i); + if (!settings) + return -ENOMEM; + /* Make sure nobody takes the new name, between the time we * checked it is currently unused in all search paths, and the * time we take possesion of it */ @@ -490,7 +597,12 @@ int image_clone(Image *i, const char *new_name, bool read_only) { case IMAGE_DIRECTORY: new_path = strjoina("/var/lib/machines/", new_name); - r = btrfs_subvol_snapshot(i->path, new_path, (read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | BTRFS_SNAPSHOT_FALLBACK_COPY | BTRFS_SNAPSHOT_RECURSIVE); + r = btrfs_subvol_snapshot(i->path, new_path, (read_only ? BTRFS_SNAPSHOT_READ_ONLY : 0) | BTRFS_SNAPSHOT_FALLBACK_COPY | BTRFS_SNAPSHOT_RECURSIVE | BTRFS_SNAPSHOT_QUOTA); + + /* Enable "subtree" quotas for the copy, if we didn't + * copy any quota from the source. */ + (void) btrfs_subvol_auto_qgroup(i->path, 0, true); + break; case IMAGE_RAW: @@ -506,6 +618,12 @@ int image_clone(Image *i, const char *new_name, bool read_only) { if (r < 0) return r; + STRV_FOREACH(j, settings) { + r = clone_settings_file(*j, new_name); + if (r < 0 && r != -ENOENT) + log_debug_errno(r, "Failed to clone settings %s, ignoring: %m", *j); + } + return 0; } @@ -526,6 +644,10 @@ int image_read_only(Image *i, bool b) { switch (i->type) { case IMAGE_SUBVOLUME: + + /* Note that we set the flag only on the top-level + * subvolume of the image. */ + r = btrfs_subvol_set_read_only(i->path, b); if (r < 0) return r; @@ -626,7 +748,14 @@ int image_set_limit(Image *i, uint64_t referenced_max) { if (i->type != IMAGE_SUBVOLUME) return -EOPNOTSUPP; - return btrfs_quota_limit(i->path, referenced_max); + /* We set the quota both for the subvolume as well as for the + * subtree. The latter is mostly for historical reasons, since + * we didn't use to have a concept of subtree quota, and hence + * only modified the subvolume quota. */ + + (void) btrfs_qgroup_set_limit(i->path, 0, referenced_max); + (void) btrfs_subvol_auto_qgroup(i->path, 0, true); + return btrfs_subvol_set_subtree_quota_limit(i->path, 0, referenced_max); } int image_name_lock(const char *name, int operation, LockFile *ret) { diff --git a/src/shared/machine-image.h b/src/shared/machine-image.h index f041600fbf..038db7453c 100644 --- a/src/shared/machine-image.h +++ b/src/shared/machine-image.h @@ -21,9 +21,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "time-util.h" -#include "lockfile-util.h" #include "hashmap.h" +#include "lockfile-util.h" +#include "time-util.h" typedef enum ImageType { IMAGE_DIRECTORY, diff --git a/src/shared/machine-pool.c b/src/shared/machine-pool.c index 8af78f47d5..4172a63fd0 100644 --- a/src/shared/machine-pool.c +++ b/src/shared/machine-pool.c @@ -19,19 +19,27 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <sys/mount.h> #include <sys/prctl.h> -#include <sys/vfs.h> #include <sys/statvfs.h> -#include <sys/mount.h> +#include <sys/vfs.h> -#include "util.h" -#include "process-util.h" +#include "alloc-util.h" +#include "btrfs-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "fs-util.h" #include "lockfile-util.h" +#include "machine-pool.h" #include "mkdir.h" -#include "btrfs-util.h" +#include "mount-util.h" +#include "parse-util.h" #include "path-util.h" +#include "process-util.h" #include "signal-util.h" -#include "machine-pool.h" +#include "stat-util.h" +#include "string-util.h" +#include "util.h" #define VAR_LIB_MACHINES_SIZE_START (1024UL*1024UL*500UL) #define VAR_LIB_MACHINES_FREE_MIN (1024UL*1024UL*750UL) @@ -170,7 +178,7 @@ int setup_machine_directory(uint64_t size, sd_bus_error *error) { }; _cleanup_close_ int fd = -1, control = -1, loop = -1; _cleanup_free_ char* loopdev = NULL; - char tmpdir[] = "/tmp/import-mount.XXXXXX", *mntdir = NULL; + char tmpdir[] = "/tmp/machine-pool.XXXXXX", *mntdir = NULL; bool tmpdir_made = false, mntdir_made = false, mntdir_mounted = false; char buf[FORMAT_BYTES_MAX]; int r, nr = -1; @@ -194,14 +202,35 @@ int setup_machine_directory(uint64_t size, sd_bus_error *error) { r = btrfs_quota_enable("/var/lib/machines", true); if (r < 0) - log_warning_errno(r, "Failed to enable quota, ignoring: %m"); + log_warning_errno(r, "Failed to enable quota for /var/lib/machines, ignoring: %m"); + + r = btrfs_subvol_auto_qgroup("/var/lib/machines", 0, true); + if (r < 0) + log_warning_errno(r, "Failed to set up default quota hierarchy for /var/lib/machines, ignoring: %m"); + + return 1; + } + if (path_is_mount_point("/var/lib/machines", AT_SYMLINK_FOLLOW) > 0) { + log_debug("/var/lib/machines is already a mount point, not creating loopback file for it."); return 0; } - if (path_is_mount_point("/var/lib/machines", AT_SYMLINK_FOLLOW) > 0 || - dir_is_empty("/var/lib/machines") == 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "/var/lib/machines is not a btrfs file system. Operation is not supported on legacy file systems."); + r = dir_is_populated("/var/lib/machines"); + if (r < 0 && r != -ENOENT) + return r; + if (r > 0) { + log_debug("/var/log/machines is already populated, not creating loopback file for it."); + return 0; + } + + r = mkfs_exists("btrfs"); + if (r == -ENOENT) { + log_debug("mkfs.btrfs is missing, cannot create loopback file for /var/lib/machines."); + return 0; + } + if (r < 0) + return r; fd = setup_machine_raw(size, error); if (fd < 0) @@ -266,6 +295,10 @@ int setup_machine_directory(uint64_t size, sd_bus_error *error) { if (r < 0) log_warning_errno(r, "Failed to enable quota, ignoring: %m"); + r = btrfs_subvol_auto_qgroup(mntdir, 0, true); + if (r < 0) + log_warning_errno(r, "Failed to set up default quota hierarchy, ignoring: %m"); + if (chmod(mntdir, 0700) < 0) { r = sd_bus_error_set_errnof(error, errno, "Failed to fix owner: %m"); goto fail; @@ -286,7 +319,7 @@ int setup_machine_directory(uint64_t size, sd_bus_error *error) { (void) rmdir(mntdir); (void) rmdir(tmpdir); - return 0; + return 1; fail: if (mntdir_mounted) @@ -345,7 +378,7 @@ int grow_machine_directory(void) { if (b.f_bavail > b.f_blocks / 3) return 0; - /* Calculate how much we are willing to add at maximum */ + /* Calculate how much we are willing to add at most */ max_add = ((uint64_t) a.f_bavail * (uint64_t) a.f_bsize) - VAR_LIB_MACHINES_FREE_MIN; /* Calculate the old size */ @@ -370,9 +403,11 @@ int grow_machine_directory(void) { if (r <= 0) return r; - r = btrfs_quota_limit("/var/lib/machines", new_size); - if (r < 0) - return r; + /* Also bump the quota, of both the subvolume leaf qgroup, as + * well as of any subtree quota group by the same id but a + * higher level, if it exists. */ + (void) btrfs_qgroup_set_limit("/var/lib/machines", 0, new_size); + (void) btrfs_subvol_set_subtree_quota_limit("/var/lib/machines", 0, new_size); log_info("Grew /var/lib/machines btrfs loopback file system to %s.", format_bytes(buf, sizeof(buf), new_size)); return 1; diff --git a/src/shared/nss-util.h b/src/shared/nss-util.h index 3657aa5d9c..a7b51a91da 100644 --- a/src/shared/nss-util.h +++ b/src/shared/nss-util.h @@ -21,11 +21,11 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <nss.h> +#include <grp.h> #include <netdb.h> -#include <resolv.h> +#include <nss.h> #include <pwd.h> -#include <grp.h> +#include <resolv.h> #define NSS_GETHOSTBYNAME_PROTOTYPES(module) \ diff --git a/src/shared/pager.c b/src/shared/pager.c index 479a9d5e8d..d149bc1722 100644 --- a/src/shared/pager.c +++ b/src/shared/pager.c @@ -21,24 +21,27 @@ #include <fcntl.h> #include <stdlib.h> -#include <unistd.h> #include <string.h> #include <sys/prctl.h> +#include <unistd.h> +#include "copy.h" +#include "fd-util.h" +#include "locale-util.h" +#include "macro.h" #include "pager.h" -#include "util.h" #include "process-util.h" -#include "macro.h" -#include "terminal-util.h" #include "signal-util.h" -#include "copy.h" +#include "string-util.h" +#include "terminal-util.h" +#include "util.h" static pid_t pager_pid = 0; noreturn static void pager_fallback(void) { int r; - r = copy_bytes(STDIN_FILENO, STDOUT_FILENO, (off_t) -1, false); + r = copy_bytes(STDIN_FILENO, STDOUT_FILENO, (uint64_t) -1, false); if (r < 0) { log_error_errno(r, "Internal pager failed: %m"); _exit(EXIT_FAILURE); @@ -48,24 +51,27 @@ noreturn static void pager_fallback(void) { } int pager_open(bool jump_to_end) { - int fd[2]; + _cleanup_close_pair_ int fd[2] = { -1, -1 }; const char *pager; pid_t parent_pid; - int r; if (pager_pid > 0) return 1; - if ((pager = getenv("SYSTEMD_PAGER")) || (pager = getenv("PAGER"))) - if (!*pager || streq(pager, "cat")) - return 0; - if (!on_tty()) return 0; + pager = getenv("SYSTEMD_PAGER"); + if (!pager) + pager = getenv("PAGER"); + + /* If the pager is explicitly turned off, honour it */ + if (pager && (pager[0] == 0 || streq(pager, "cat"))) + return 0; + /* Determine and cache number of columns before we spawn the * pager so that we get the value from the actual tty */ - columns(); + (void) columns(); if (pipe(fd) < 0) return log_error_errno(errno, "Failed to create pager pipe: %m"); @@ -73,11 +79,8 @@ int pager_open(bool jump_to_end) { parent_pid = getpid(); pager_pid = fork(); - if (pager_pid < 0) { - r = log_error_errno(errno, "Failed to fork pager: %m"); - safe_close_pair(fd); - return r; - } + if (pager_pid < 0) + return log_error_errno(errno, "Failed to fork pager: %m"); /* In the child start the pager */ if (pager_pid == 0) { @@ -86,7 +89,7 @@ int pager_open(bool jump_to_end) { (void) reset_all_signal_handlers(); (void) reset_signal_mask(); - dup2(fd[0], STDIN_FILENO); + (void) dup2(fd[0], STDIN_FILENO); safe_close_pair(fd); /* Initialize a good set of less options */ @@ -141,7 +144,6 @@ int pager_open(bool jump_to_end) { if (dup2(fd[1], STDERR_FILENO) < 0) return log_error_errno(errno, "Failed to duplicate pager pipe: %m"); - safe_close_pair(fd); return 1; } diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c index f6a127174c..4a82bd18cd 100644 --- a/src/shared/path-lookup.c +++ b/src/shared/path-lookup.c @@ -19,16 +19,18 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdlib.h> +#include <errno.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> -#include <errno.h> -#include "util.h" -#include "strv.h" -#include "path-util.h" -#include "path-lookup.h" +#include "alloc-util.h" #include "install.h" +#include "path-lookup.h" +#include "path-util.h" +#include "string-util.h" +#include "strv.h" +#include "util.h" int user_config_home(char **config_home) { const char *e; @@ -181,7 +183,7 @@ static char** user_dirs( if (strv_extend_strv_concat(&res, config_dirs, "/systemd/user") < 0) return NULL; - if (strv_extend_strv(&res, (char**) config_unit_paths) < 0) + if (strv_extend_strv(&res, (char**) config_unit_paths, false) < 0) return NULL; if (runtime_dir) @@ -203,14 +205,14 @@ static char** user_dirs( if (strv_extend_strv_concat(&res, data_dirs, "/systemd/user") < 0) return NULL; - if (strv_extend_strv(&res, (char**) data_unit_paths) < 0) + if (strv_extend_strv(&res, (char**) data_unit_paths, false) < 0) return NULL; if (generator_late) if (strv_extend(&res, generator_late) < 0) return NULL; - if (!path_strv_make_absolute_cwd(res)) + if (path_strv_make_absolute_cwd(res) < 0) return NULL; tmp = res; @@ -244,6 +246,7 @@ int lookup_paths_init( const char *e; bool append = false; /* Add items from SYSTEMD_UNIT_PATH before normal directories */ + int r; assert(p); @@ -259,9 +262,9 @@ int lookup_paths_init( /* FIXME: empty components in other places should be * rejected. */ - p->unit_path = path_split_and_make_absolute(e); - if (!p->unit_path) - return -ENOMEM; + r = path_split_and_make_absolute(e, &p->unit_path); + if (r < 0) + return r; } else p->unit_path = NULL; @@ -269,7 +272,6 @@ int lookup_paths_init( /* Let's figure something out. */ _cleanup_strv_free_ char **unit_path; - int r; /* For the user units we include share/ in the search * path in order to comply with the XDG basedir spec. @@ -318,7 +320,7 @@ int lookup_paths_init( if (!unit_path) return -ENOMEM; - r = strv_extend_strv(&p->unit_path, unit_path); + r = strv_extend_strv(&p->unit_path, unit_path, false); if (r < 0) return r; } @@ -333,8 +335,7 @@ int lookup_paths_init( log_debug("Looking for unit files in (higher priority first):\n\t%s", t); } else { log_debug("Ignoring unit files."); - strv_free(p->unit_path); - p->unit_path = NULL; + p->unit_path = strv_free(p->unit_path); } if (running_as == MANAGER_SYSTEM) { @@ -343,9 +344,9 @@ int lookup_paths_init( e = getenv("SYSTEMD_SYSVINIT_PATH"); if (e) { - p->sysvinit_path = path_split_and_make_absolute(e); - if (!p->sysvinit_path) - return -ENOMEM; + r = path_split_and_make_absolute(e, &p->sysvinit_path); + if (r < 0) + return r; } else p->sysvinit_path = NULL; @@ -361,9 +362,9 @@ int lookup_paths_init( e = getenv("SYSTEMD_SYSVRCND_PATH"); if (e) { - p->sysvrcnd_path = path_split_and_make_absolute(e); - if (!p->sysvrcnd_path) - return -ENOMEM; + r = path_split_and_make_absolute(e, &p->sysvrcnd_path); + if (r < 0) + return r; } else p->sysvrcnd_path = NULL; @@ -390,8 +391,7 @@ int lookup_paths_init( log_debug("Looking for SysV init scripts in:\n\t%s", t); } else { log_debug("Ignoring SysV init scripts."); - strv_free(p->sysvinit_path); - p->sysvinit_path = NULL; + p->sysvinit_path = strv_free(p->sysvinit_path); } if (!strv_isempty(p->sysvrcnd_path)) { @@ -403,8 +403,7 @@ int lookup_paths_init( log_debug("Looking for SysV rcN.d links in:\n\t%s", t); } else { log_debug("Ignoring SysV rcN.d links."); - strv_free(p->sysvrcnd_path); - p->sysvrcnd_path = NULL; + p->sysvrcnd_path = strv_free(p->sysvrcnd_path); } #else log_debug("SysV init scripts and rcN.d links support disabled"); @@ -417,13 +416,11 @@ int lookup_paths_init( void lookup_paths_free(LookupPaths *p) { assert(p); - strv_free(p->unit_path); - p->unit_path = NULL; + p->unit_path = strv_free(p->unit_path); #ifdef HAVE_SYSV_COMPAT - strv_free(p->sysvinit_path); - strv_free(p->sysvrcnd_path); - p->sysvinit_path = p->sysvrcnd_path = NULL; + p->sysvinit_path = strv_free(p->sysvinit_path); + p->sysvrcnd_path = strv_free(p->sysvrcnd_path); #endif } diff --git a/src/shared/pty.c b/src/shared/pty.c deleted file mode 100644 index 35d9ff5f4d..0000000000 --- a/src/shared/pty.c +++ /dev/null @@ -1,633 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2014 David Herrmann <dh.herrmann@gmail.com> - - 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/>. -***/ - -/* - * PTY - * A PTY object represents a single PTY connection between a master and a - * child. The child process is fork()ed so the caller controls what program - * will be run. - * - * Programs like /bin/login tend to perform a vhangup() on their TTY - * before running the login procedure. This also causes the pty master - * to get a EPOLLHUP event as long as no client has the TTY opened. - * This means, we cannot use the TTY connection as reliable way to track - * the client. Instead, we _must_ rely on the PID of the client to track - * them. - * However, this has the side effect that if the client forks and the - * parent exits, we loose them and restart the client. But this seems to - * be the expected behavior so we implement it here. - * - * Unfortunately, epoll always polls for EPOLLHUP so as long as the - * vhangup() is ongoing, we will _always_ get EPOLLHUP and cannot sleep. - * This gets worse if the client closes the TTY but doesn't exit. - * Therefore, the fd must be edge-triggered in the epoll-set so we - * only get the events once they change. - */ - -#include <errno.h> -#include <fcntl.h> -#include <signal.h> -#include <stdbool.h> -#include <stdint.h> -#include <stdlib.h> -#include <sys/epoll.h> -#include <sys/ioctl.h> -#include <sys/uio.h> -#include <sys/wait.h> -#include <termios.h> -#include <unistd.h> - -#include "barrier.h" -#include "macro.h" -#include "ring.h" -#include "util.h" -#include "signal-util.h" -#include "pty.h" - -#define PTY_BUFSIZE 4096 - -enum { - PTY_ROLE_UNKNOWN, - PTY_ROLE_PARENT, - PTY_ROLE_CHILD, -}; - -struct Pty { - unsigned long ref; - Barrier barrier; - int fd; - pid_t child; - sd_event_source *fd_source; - sd_event_source *child_source; - - char in_buf[PTY_BUFSIZE]; - Ring out_buf; - - pty_event_t event_fn; - void *event_fn_userdata; - - bool needs_requeue : 1; - unsigned int role : 2; -}; - -int pty_new(Pty **out) { - _pty_unref_ Pty *pty = NULL; - int r; - - assert_return(out, -EINVAL); - - pty = new0(Pty, 1); - if (!pty) - return -ENOMEM; - - pty->ref = 1; - pty->fd = -1; - pty->barrier = (Barrier) BARRIER_NULL; - - pty->fd = posix_openpt(O_RDWR | O_NOCTTY | O_CLOEXEC | O_NONBLOCK); - if (pty->fd < 0) - return -errno; - - /* - * The slave-node is initialized to uid/gid of the caller of - * posix_openpt(). Only if devpts is mounted with fixed uid/gid this is - * skipped. In that case, grantpt() can overwrite these, but then you - * have to be root to use chown() (or a pt_chown helper has to be - * present). In those cases grantpt() really does something, - * otherwise it's a no-op. We call grantpt() here to try supporting - * those cases, even though no-one uses that, I guess. If you need other - * access-rights, set them yourself after this call returns (no, this is - * not racy, it looks racy, but races regarding your own UID are never - * important as an attacker could ptrace you; and the slave-pty is also - * still locked). - */ - r = grantpt(pty->fd); - if (r < 0) - return -errno; - - r = barrier_create(&pty->barrier); - if (r < 0) - return r; - - *out = pty; - pty = NULL; - return 0; -} - -Pty *pty_ref(Pty *pty) { - if (!pty || pty->ref < 1) - return NULL; - - ++pty->ref; - return pty; -} - -Pty *pty_unref(Pty *pty) { - if (!pty || pty->ref < 1 || --pty->ref > 0) - return NULL; - - pty_close(pty); - pty->child_source = sd_event_source_unref(pty->child_source); - barrier_destroy(&pty->barrier); - ring_clear(&pty->out_buf); - free(pty); - - return NULL; -} - -Barrier *pty_get_barrier(Pty *pty) { - assert(pty); - return &pty->barrier; -} - -bool pty_is_unknown(Pty *pty) { - return pty && pty->role == PTY_ROLE_UNKNOWN; -} - -bool pty_is_parent(Pty *pty) { - return pty && pty->role == PTY_ROLE_PARENT; -} - -bool pty_is_child(Pty *pty) { - return pty && pty->role == PTY_ROLE_CHILD; -} - -bool pty_has_child(Pty *pty) { - return pty_is_parent(pty) && pty->child > 0; -} - -pid_t pty_get_child(Pty *pty) { - return pty_has_child(pty) ? pty->child : -ECHILD; -} - -bool pty_is_open(Pty *pty) { - return pty && pty->fd >= 0; -} - -int pty_get_fd(Pty *pty) { - assert_return(pty, -EINVAL); - - return pty_is_open(pty) ? pty->fd : -EPIPE; -} - -int pty_make_child(Pty *pty) { - _cleanup_free_ char *slave_name = NULL; - int r, fd; - - assert_return(pty, -EINVAL); - assert_return(pty_is_unknown(pty), -EALREADY); - - r = ptsname_malloc(pty->fd, &slave_name); - if (r < 0) - return -errno; - - fd = open(slave_name, O_RDWR | O_CLOEXEC | O_NOCTTY); - if (fd < 0) - return -errno; - - safe_close(pty->fd); - pty->fd = fd; - pty->child = getpid(); - pty->role = PTY_ROLE_CHILD; - barrier_set_role(&pty->barrier, BARRIER_CHILD); - - return 0; -} - -int pty_make_parent(Pty *pty, pid_t child) { - assert_return(pty, -EINVAL); - assert_return(pty_is_unknown(pty), -EALREADY); - - pty->child = child; - pty->role = PTY_ROLE_PARENT; - - return 0; -} - -int pty_unlock(Pty *pty) { - assert_return(pty, -EINVAL); - assert_return(pty_is_unknown(pty) || pty_is_parent(pty), -EINVAL); - assert_return(pty_is_open(pty), -ENODEV); - - return unlockpt(pty->fd) < 0 ? -errno : 0; -} - -int pty_setup_child(Pty *pty) { - struct termios attr; - pid_t pid; - int r; - - assert_return(pty, -EINVAL); - assert_return(pty_is_child(pty), -EINVAL); - assert_return(pty_is_open(pty), -EALREADY); - - r = reset_signal_mask(); - if (r < 0) - return r; - - r = reset_all_signal_handlers(); - if (r < 0) - return r; - - pid = setsid(); - if (pid < 0 && errno != EPERM) - return -errno; - - r = ioctl(pty->fd, TIOCSCTTY, 0); - if (r < 0) - return -errno; - - r = tcgetattr(pty->fd, &attr); - if (r < 0) - return -errno; - - /* erase character should be normal backspace, PLEASEEE! */ - attr.c_cc[VERASE] = 010; - /* always set UTF8 flag */ - attr.c_iflag |= IUTF8; - - r = tcsetattr(pty->fd, TCSANOW, &attr); - if (r < 0) - return -errno; - - if (dup2(pty->fd, STDIN_FILENO) != STDIN_FILENO || - dup2(pty->fd, STDOUT_FILENO) != STDOUT_FILENO || - dup2(pty->fd, STDERR_FILENO) != STDERR_FILENO) - return -errno; - - /* only close FD if it's not a std-fd */ - pty->fd = (pty->fd > 2) ? safe_close(pty->fd) : -1; - - return 0; -} - -void pty_close(Pty *pty) { - if (!pty_is_open(pty)) - return; - - pty->fd_source = sd_event_source_unref(pty->fd_source); - pty->fd = safe_close(pty->fd); -} - -/* - * Drain input-queue and dispatch data via the event-handler. Returns <0 on - * error, 0 if queue is empty and 1 if we couldn't empty the input queue fast - * enough and there's still data left. - */ -static int pty_dispatch_read(Pty *pty) { - unsigned int i; - ssize_t len; - int r; - - /* - * We're edge-triggered, means we need to read the whole queue. This, - * however, might cause us to stall if the writer is faster than we - * are. Therefore, try reading as much as 8 times (32KiB) and only - * bail out then. - */ - - for (i = 0; i < 8; ++i) { - len = read(pty->fd, pty->in_buf, sizeof(pty->in_buf) - 1); - if (len < 0) { - if (errno == EINTR) - continue; - - return (errno == EAGAIN) ? 0 : -errno; - } else if (len == 0) - continue; - - /* set terminating zero for debugging safety */ - pty->in_buf[len] = 0; - r = pty->event_fn(pty, pty->event_fn_userdata, PTY_DATA, pty->in_buf, len); - if (r < 0) - return r; - } - - /* still data left, make sure we're queued again */ - pty->needs_requeue = true; - - return 1; -} - -/* - * Drain output-queue by writing data to the pty. Returns <0 on error, 0 if the - * output queue is empty now and 1 if we couldn't empty the output queue fast - * enough and there's still data left. - */ -static int pty_dispatch_write(Pty *pty) { - struct iovec vec[2]; - unsigned int i; - ssize_t len; - size_t num; - - /* - * Same as pty_dispatch_read(), we're edge-triggered so we need to call - * write() until either all data is written or it returns EAGAIN. We - * call it twice and if it still writes successfully, we reschedule. - */ - - for (i = 0; i < 2; ++i) { - num = ring_peek(&pty->out_buf, vec); - if (num < 1) - return 0; - - len = writev(pty->fd, vec, (int)num); - if (len < 0) { - if (errno == EINTR) - continue; - - return (errno == EAGAIN) ? 1 : -errno; - } else if (len == 0) - continue; - - ring_pull(&pty->out_buf, (size_t)len); - } - - /* still data left, make sure we're queued again */ - if (ring_get_size(&pty->out_buf) > 0) { - pty->needs_requeue = true; - return 1; - } - - return 0; -} - -static int pty_fd_fn(sd_event_source *source, int fd, uint32_t revents, void *userdata) { - Pty *pty = userdata; - int r_hup = 0, r_write = 0, r_read = 0, r; - - /* - * Whenever we encounter I/O errors, we have to make sure to drain the - * input queue first, before we handle any HUP. A child might send us - * a message and immediately close the queue. We must not handle the - * HUP first or we loose data. - * Therefore, if we read a message successfully, we always return - * success and wait for the next event-loop iteration. Furthermore, - * whenever there is a write-error, we must try reading from the input - * queue even if EPOLLIN is not set. The input might have arrived in - * between epoll_wait() and write(). Therefore, write-errors are only - * ever handled if the input-queue is empty. In all other cases they - * are ignored until either reading fails or the input queue is empty. - */ - - if (revents & (EPOLLHUP | EPOLLERR)) - r_hup = -EPIPE; - - if (revents & EPOLLOUT) - r_write = pty_dispatch_write(pty); - - /* Awesome! Kernel signals HUP without IN but queues are not empty.. */ - if ((revents & EPOLLIN) || r_hup < 0 || r_write < 0) { - r_read = pty_dispatch_read(pty); - if (r_read > 0) - return 0; /* still data left to fetch next round */ - } - - if (r_hup < 0 || r_write < 0 || r_read < 0) { - /* PTY closed and input-queue drained */ - pty_close(pty); - r = pty->event_fn(pty, pty->event_fn_userdata, PTY_HUP, NULL, 0); - if (r < 0) - return r; - } - - return 0; -} - -static int pty_fd_prepare_fn(sd_event_source *source, void *userdata) { - Pty *pty = userdata; - int r; - - if (pty->needs_requeue) { - /* - * We're edge-triggered. In case we couldn't handle all events - * or in case new write-data is queued, we set needs_requeue. - * Before going asleep, we set the io-events *again*. sd-event - * notices that we're edge-triggered and forwards the call to - * the kernel even if the events didn't change. The kernel will - * check the events and re-queue us on the ready queue in case - * an event is pending. - */ - r = sd_event_source_set_io_events(source, EPOLLHUP | EPOLLERR | EPOLLIN | EPOLLOUT | EPOLLET); - if (r >= 0) - pty->needs_requeue = false; - } - - return 0; -} - -static int pty_child_fn(sd_event_source *source, const siginfo_t *si, void *userdata) { - Pty *pty = userdata; - int r; - - pty->child = 0; - - r = pty->event_fn(pty, pty->event_fn_userdata, PTY_CHILD, si, sizeof(*si)); - if (r < 0) - return r; - - return 0; -} - -int pty_attach_event(Pty *pty, sd_event *event, pty_event_t event_fn, void *event_fn_userdata) { - int r; - - assert_return(pty, -EINVAL); - assert_return(event, -EINVAL); - assert_return(event_fn, -EINVAL); - assert_return(pty_is_parent(pty), -EINVAL); - - pty_detach_event(pty); - - if (pty_is_open(pty)) { - r = sd_event_add_io(event, - &pty->fd_source, - pty->fd, - EPOLLHUP | EPOLLERR | EPOLLIN | EPOLLOUT | EPOLLET, - pty_fd_fn, - pty); - if (r < 0) - goto error; - - r = sd_event_source_set_prepare(pty->fd_source, pty_fd_prepare_fn); - if (r < 0) - goto error; - } - - if (pty_has_child(pty)) { - r = sd_event_add_child(event, - &pty->child_source, - pty->child, - WEXITED, - pty_child_fn, - pty); - if (r < 0) - goto error; - } - - pty->event_fn = event_fn; - pty->event_fn_userdata = event_fn_userdata; - - return 0; - -error: - pty_detach_event(pty); - return r; -} - -void pty_detach_event(Pty *pty) { - if (!pty) - return; - - pty->child_source = sd_event_source_unref(pty->child_source); - pty->fd_source = sd_event_source_unref(pty->fd_source); - pty->event_fn = NULL; - pty->event_fn_userdata = NULL; -} - -int pty_write(Pty *pty, const void *buf, size_t size) { - bool was_empty; - int r; - - assert_return(pty, -EINVAL); - assert_return(pty_is_open(pty), -ENODEV); - assert_return(pty_is_parent(pty), -ENODEV); - - if (size < 1) - return 0; - - /* - * Push @buf[0..@size] into the output ring-buffer. In case the - * ring-buffer wasn't empty beforehand, we're already waiting for - * EPOLLOUT and we're done. If it was empty, we have to re-queue the - * FD for EPOLLOUT as we're edge-triggered and wouldn't get any new - * EPOLLOUT event. - */ - - was_empty = ring_get_size(&pty->out_buf) < 1; - - r = ring_push(&pty->out_buf, buf, size); - if (r < 0) - return r; - - if (was_empty) - pty->needs_requeue = true; - - return 0; -} - -int pty_signal(Pty *pty, int sig) { - assert_return(pty, -EINVAL); - assert_return(pty_is_open(pty), -ENODEV); - assert_return(pty_is_parent(pty), -ENODEV); - - return ioctl(pty->fd, TIOCSIG, sig) < 0 ? -errno : 0; -} - -int pty_resize(Pty *pty, unsigned short term_width, unsigned short term_height) { - struct winsize ws = { - .ws_col = term_width, - .ws_row = term_height, - }; - - assert_return(pty, -EINVAL); - assert_return(pty_is_open(pty), -ENODEV); - assert_return(pty_is_parent(pty), -ENODEV); - - /* - * This will send SIGWINCH to the pty slave foreground process group. - * We will also get one, but we don't need it. - */ - return ioctl(pty->fd, TIOCSWINSZ, &ws) < 0 ? -errno : 0; -} - -pid_t pty_fork(Pty **out, sd_event *event, pty_event_t event_fn, void *event_fn_userdata, unsigned short initial_term_width, unsigned short initial_term_height) { - _pty_unref_ Pty *pty = NULL; - int r; - pid_t pid; - - assert_return(out, -EINVAL); - assert_return((event && event_fn) || (!event && !event_fn), -EINVAL); - - r = pty_new(&pty); - if (r < 0) - return r; - - r = pty_unlock(pty); - if (r < 0) - return r; - - pid = fork(); - if (pid < 0) - return -errno; - - if (pid == 0) { - /* child */ - - r = pty_make_child(pty); - if (r < 0) - _exit(-r); - - r = pty_setup_child(pty); - if (r < 0) - _exit(-r); - - /* sync with parent */ - if (!barrier_place_and_sync(&pty->barrier)) - _exit(1); - - /* fallthrough and return the child's PTY object */ - } else { - /* parent */ - - r = pty_make_parent(pty, pid); - if (r < 0) - goto parent_error; - - r = pty_resize(pty, initial_term_width, initial_term_height); - if (r < 0) - goto parent_error; - - if (event) { - r = pty_attach_event(pty, event, event_fn, event_fn_userdata); - if (r < 0) - goto parent_error; - } - - /* sync with child */ - if (!barrier_place_and_sync(&pty->barrier)) { - r = -ECHILD; - goto parent_error; - } - - /* fallthrough and return the parent's PTY object */ - } - - *out = pty; - pty = NULL; - return pid; - -parent_error: - barrier_abort(&pty->barrier); - waitpid(pty->child, NULL, 0); - pty->child = 0; - return r; -} diff --git a/src/shared/pty.h b/src/shared/pty.h deleted file mode 100644 index 63c7db2833..0000000000 --- a/src/shared/pty.h +++ /dev/null @@ -1,72 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#pragma once - -/*** - This file is part of systemd. - - Copyright 2014 David Herrmann <dh.herrmann@gmail.com> - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <stdbool.h> -#include <unistd.h> - -#include "barrier.h" -#include "macro.h" -#include "sd-event.h" - -typedef struct Pty Pty; - -enum { - PTY_CHILD, - PTY_HUP, - PTY_DATA, -}; - -typedef int (*pty_event_t) (Pty *pty, void *userdata, unsigned int event, const void *ptr, size_t size); - -int pty_new(Pty **out); -Pty *pty_ref(Pty *pty); -Pty *pty_unref(Pty *pty); - -#define _pty_unref_ _cleanup_(pty_unrefp) -DEFINE_TRIVIAL_CLEANUP_FUNC(Pty*, pty_unref); - -Barrier *pty_get_barrier(Pty *pty); - -bool pty_is_unknown(Pty *pty); -bool pty_is_parent(Pty *pty); -bool pty_is_child(Pty *pty); -bool pty_has_child(Pty *pty); -pid_t pty_get_child(Pty *pty); - -bool pty_is_open(Pty *pty); -int pty_get_fd(Pty *pty); - -int pty_make_child(Pty *pty); -int pty_make_parent(Pty *pty, pid_t child); -int pty_unlock(Pty *pty); -int pty_setup_child(Pty *pty); -void pty_close(Pty *pty); - -int pty_attach_event(Pty *pty, sd_event *event, pty_event_t event_fn, void *event_fn_userdata); -void pty_detach_event(Pty *pty); - -int pty_write(Pty *pty, const void *buf, size_t size); -int pty_signal(Pty *pty, int sig); -int pty_resize(Pty *pty, unsigned short term_width, unsigned short term_height); - -pid_t pty_fork(Pty **out, sd_event *event, pty_event_t event_fn, void *event_fn_userdata, unsigned short initial_term_width, unsigned short initial_term_height); diff --git a/src/shared/ptyfwd.c b/src/shared/ptyfwd.c index 789f217efc..2666b8f7e2 100644 --- a/src/shared/ptyfwd.c +++ b/src/shared/ptyfwd.c @@ -19,19 +19,23 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <limits.h> #include <sys/epoll.h> #include <sys/ioctl.h> -#include <limits.h> #include <termios.h> -#include "util.h" +#include "alloc-util.h" +#include "fd-util.h" #include "ptyfwd.h" +#include "util.h" struct PTYForward { sd_event *event; int master; + PTYForwardFlags flags; + sd_event_source *stdin_event_source; sd_event_source *stdout_event_source; sd_event_source *master_event_source; @@ -41,8 +45,6 @@ struct PTYForward { struct termios saved_stdin_attr; struct termios saved_stdout_attr; - bool read_only:1; - bool saved_stdin:1; bool saved_stdout:1; @@ -54,8 +56,7 @@ struct PTYForward { bool master_writable:1; bool master_hangup:1; - /* Continue reading after hangup? */ - bool ignore_vhangup:1; + bool read_from_master:1; bool last_char_set:1; char last_char; @@ -100,6 +101,18 @@ static bool look_for_escape(PTYForward *f, const char *buffer, size_t n) { return false; } +static bool ignore_vhangup(PTYForward *f) { + assert(f); + + if (f->flags & PTY_FORWARD_IGNORE_VHANGUP) + return true; + + if ((f->flags & PTY_FORWARD_IGNORE_INITIAL_VHANGUP) && !f->read_from_master) + return true; + + return false; +} + static int shovel(PTYForward *f) { ssize_t k; @@ -179,7 +192,7 @@ static int shovel(PTYForward *f) { * EAGAIN here and try again, unless * ignore_vhangup is off. */ - if (errno == EAGAIN || (errno == EIO && f->ignore_vhangup)) + if (errno == EAGAIN || (errno == EIO && ignore_vhangup(f))) f->master_readable = false; else if (errno == EPIPE || errno == ECONNRESET || errno == EIO) { f->master_readable = f->master_writable = false; @@ -190,8 +203,10 @@ static int shovel(PTYForward *f) { log_error_errno(errno, "read(): %m"); return sd_event_exit(f->event, EXIT_FAILURE); } - } else + } else { + f->read_from_master = true; f->out_buffer_full += (size_t) k; + } } if (f->stdout_writable && f->out_buffer_full > 0) { @@ -302,8 +317,7 @@ static int on_sigwinch_event(sd_event_source *e, const struct signalfd_siginfo * int pty_forward_new( sd_event *event, int master, - bool ignore_vhangup, - bool read_only, + PTYForwardFlags flags, PTYForward **ret) { _cleanup_(pty_forward_freep) PTYForward *f = NULL; @@ -314,8 +328,7 @@ int pty_forward_new( if (!f) return -ENOMEM; - f->read_only = read_only; - f->ignore_vhangup = ignore_vhangup; + f->flags = flags; if (event) f->event = sd_event_ref(event); @@ -325,7 +338,7 @@ int pty_forward_new( return r; } - if (!read_only) { + if (!(flags & PTY_FORWARD_READ_ONLY)) { r = fd_nonblock(STDIN_FILENO, true); if (r < 0) return r; @@ -344,7 +357,7 @@ int pty_forward_new( if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0) (void) ioctl(master, TIOCSWINSZ, &ws); - if (!read_only) { + if (!(flags & PTY_FORWARD_READ_ONLY)) { if (tcgetattr(STDIN_FILENO, &f->saved_stdin_attr) >= 0) { struct termios raw_stdin_attr; @@ -400,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) @@ -429,16 +443,20 @@ int pty_forward_get_last_char(PTYForward *f, char *ch) { return 0; } -int pty_forward_set_ignore_vhangup(PTYForward *f, bool ignore_vhangup) { +int pty_forward_set_ignore_vhangup(PTYForward *f, bool b) { int r; assert(f); - if (f->ignore_vhangup == ignore_vhangup) + if (!!(f->flags & PTY_FORWARD_IGNORE_VHANGUP) == b) return 0; - f->ignore_vhangup = ignore_vhangup; - if (!f->ignore_vhangup) { + if (b) + f->flags |= PTY_FORWARD_IGNORE_VHANGUP; + else + f->flags &= ~PTY_FORWARD_IGNORE_VHANGUP; + + if (!ignore_vhangup(f)) { /* We shall now react to vhangup()s? Let's check * immediately if we might be in one */ @@ -455,5 +473,5 @@ int pty_forward_set_ignore_vhangup(PTYForward *f, bool ignore_vhangup) { int pty_forward_get_ignore_vhangup(PTYForward *f) { assert(f); - return f->ignore_vhangup; + return !!(f->flags & PTY_FORWARD_IGNORE_VHANGUP); } diff --git a/src/shared/ptyfwd.h b/src/shared/ptyfwd.h index 6f84e4036a..9b3214221b 100644 --- a/src/shared/ptyfwd.h +++ b/src/shared/ptyfwd.h @@ -27,7 +27,17 @@ typedef struct PTYForward PTYForward; -int pty_forward_new(sd_event *event, int master, bool ignore_vhangup, bool read_only, PTYForward **f); +typedef enum PTYForwardFlags { + PTY_FORWARD_READ_ONLY = 1, + + /* Continue reading after hangup? */ + PTY_FORWARD_IGNORE_VHANGUP = 2, + + /* Continue reading after hangup but only if we never read anything else? */ + PTY_FORWARD_IGNORE_INITIAL_VHANGUP = 4, +} PTYForwardFlags; + +int pty_forward_new(sd_event *event, int master, PTYForwardFlags flags, PTYForward **f); PTYForward *pty_forward_free(PTYForward *f); int pty_forward_get_last_char(PTYForward *f, char *ch); diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index d73a74912e..09baf51661 100644 --- a/src/shared/seccomp-util.c +++ b/src/shared/seccomp-util.c @@ -21,8 +21,9 @@ #include <seccomp.h> -#include "util.h" #include "seccomp-util.h" +#include "string-util.h" +#include "util.h" const char* seccomp_arch_to_string(uint32_t c) { diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c index 1064fd5cbd..39b836d053 100644 --- a/src/shared/sleep-config.c +++ b/src/shared/sleep-config.c @@ -21,10 +21,15 @@ #include <stdio.h> +#include "alloc-util.h" #include "conf-parser.h" -#include "sleep-config.h" +#include "def.h" +#include "fd-util.h" #include "fileio.h" #include "log.h" +#include "parse-util.h" +#include "sleep-config.h" +#include "string-util.h" #include "strv.h" #include "util.h" @@ -49,7 +54,7 @@ int parse_sleep_config(const char *verb, char ***_modes, char ***_states) { }; config_parse_many(PKGSYSCONFDIR "/sleep.conf", - CONF_DIRS_NULSTR("systemd/sleep.conf"), + CONF_PATHS_NULSTR("systemd/sleep.conf.d"), "Sleep\0", config_item_table_lookup, items, false, NULL); @@ -226,7 +231,7 @@ static bool enough_memory_for_hibernation(void) { if (r < 0) return false; - r = get_status_field("/proc/meminfo", "\nActive(anon):", &active); + r = get_proc_field("/proc/meminfo", "Active(anon)", WHITESPACE, &active); if (r < 0) { log_error_errno(r, "Failed to retrieve Active(anon) from /proc/meminfo: %m"); return false; diff --git a/src/shared/spawn-ask-password-agent.c b/src/shared/spawn-ask-password-agent.c index 70466d17e5..3fcea61873 100644 --- a/src/shared/spawn-ask-password-agent.c +++ b/src/shared/spawn-ask-password-agent.c @@ -19,14 +19,14 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ +#include <signal.h> #include <stdlib.h> #include <unistd.h> -#include <signal.h> #include "log.h" -#include "util.h" #include "process-util.h" #include "spawn-ask-password-agent.h" +#include "util.h" static pid_t agent_pid = 0; @@ -46,9 +46,9 @@ int ask_password_agent_open(void) { SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, "--watch", NULL); if (r < 0) - log_error_errno(r, "Failed to fork TTY ask password agent: %m"); + return log_error_errno(r, "Failed to fork TTY ask password agent: %m"); - return r; + return 1; } void ask_password_agent_close(void) { @@ -57,8 +57,8 @@ void ask_password_agent_close(void) { return; /* Inform agent that we are done */ - kill(agent_pid, SIGTERM); - kill(agent_pid, SIGCONT); + (void) kill(agent_pid, SIGTERM); + (void) kill(agent_pid, SIGCONT); (void) wait_for_terminate(agent_pid, NULL); agent_pid = 0; } diff --git a/src/shared/spawn-polkit-agent.c b/src/shared/spawn-polkit-agent.c index 4db249e1ca..8ea6cb830b 100644 --- a/src/shared/spawn-polkit-agent.c +++ b/src/shared/spawn-polkit-agent.c @@ -19,16 +19,19 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdlib.h> -#include <unistd.h> -#include <signal.h> #include <errno.h> #include <poll.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> +#include "fd-util.h" +#include "io-util.h" #include "log.h" -#include "util.h" #include "process-util.h" #include "spawn-polkit-agent.h" +#include "stdio-util.h" +#include "util.h" #ifdef ENABLE_POLKIT static pid_t agent_pid = 0; @@ -76,8 +79,9 @@ void polkit_agent_close(void) { return; /* Inform agent that we are done */ - kill(agent_pid, SIGTERM); - kill(agent_pid, SIGCONT); + (void) kill(agent_pid, SIGTERM); + (void) kill(agent_pid, SIGCONT); + (void) wait_for_terminate(agent_pid, NULL); agent_pid = 0; } diff --git a/src/shared/specifier.c b/src/shared/specifier.c index 85bd477f2d..c5c4a4d7d7 100644 --- a/src/shared/specifier.c +++ b/src/shared/specifier.c @@ -22,10 +22,12 @@ #include <string.h> #include <sys/utsname.h> -#include "macro.h" -#include "util.h" +#include "alloc-util.h" #include "hostname-util.h" +#include "macro.h" #include "specifier.h" +#include "string-util.h" +#include "util.h" /* * Generic infrastructure for replacing %x style specifiers in diff --git a/src/shared/switch-root.c b/src/shared/switch-root.c index b12189cd10..fc885f6cb8 100644 --- a/src/shared/switch-root.c +++ b/src/shared/switch-root.c @@ -19,21 +19,24 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/stat.h> -#include <stdbool.h> #include <errno.h> +#include <fcntl.h> +#include <stdbool.h> #include <string.h> #include <sys/mount.h> +#include <sys/stat.h> #include <unistd.h> -#include <fcntl.h> -#include "util.h" -#include "path-util.h" -#include "mkdir.h" -#include "rm-rf.h" #include "base-filesystem.h" +#include "fd-util.h" #include "missing.h" +#include "mkdir.h" +#include "path-util.h" +#include "rm-rf.h" +#include "string-util.h" #include "switch-root.h" +#include "user-util.h" +#include "util.h" int switch_root(const char *new_root, const char *oldroot, bool detach_oldroot, unsigned long mountflags) { diff --git a/src/shared/sysctl-util.c b/src/shared/sysctl-util.c index 1de0b94fd5..70caa542e7 100644 --- a/src/shared/sysctl-util.c +++ b/src/shared/sysctl-util.c @@ -19,19 +19,19 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <stdlib.h> -#include <stdbool.h> #include <errno.h> -#include <string.h> -#include <stdio.h> -#include <limits.h> #include <getopt.h> +#include <limits.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> -#include "log.h" -#include "util.h" #include "fileio.h" -#include "build.h" +#include "log.h" +#include "string-util.h" #include "sysctl-util.h" +#include "util.h" char *sysctl_normalize(char *s) { char *n; diff --git a/src/shared/uid-range.c b/src/shared/uid-range.c index 4794ff45bb..079dd8752c 100644 --- a/src/shared/uid-range.c +++ b/src/shared/uid-range.c @@ -19,8 +19,9 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include "util.h" #include "uid-range.h" +#include "user-util.h" +#include "util.h" static bool uid_range_intersect(UidRange *range, uid_t start, uid_t nr) { assert(range); diff --git a/src/shared/utmp-wtmp.c b/src/shared/utmp-wtmp.c index 63f1e4ca6f..13b32a0509 100644 --- a/src/shared/utmp-wtmp.c +++ b/src/shared/utmp-wtmp.c @@ -19,18 +19,22 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <utmpx.h> #include <errno.h> +#include <fcntl.h> +#include <poll.h> #include <string.h> #include <sys/utsname.h> -#include <fcntl.h> #include <unistd.h> -#include <poll.h> +#include <utmpx.h> +#include "alloc-util.h" +#include "fd-util.h" +#include "hostname-util.h" #include "macro.h" #include "path-util.h" +#include "string-util.h" #include "terminal-util.h" -#include "hostname-util.h" +#include "user-util.h" #include "utmp-wtmp.h" int utmp_get_runlevel(int *runlevel, int *previous) { diff --git a/src/shared/watchdog.c b/src/shared/watchdog.c index 9d39beb340..7131e94cdb 100644 --- a/src/shared/watchdog.c +++ b/src/shared/watchdog.c @@ -19,14 +19,15 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/ioctl.h> #include <errno.h> #include <fcntl.h> +#include <sys/ioctl.h> #include <unistd.h> #include <linux/watchdog.h> -#include "watchdog.h" +#include "fd-util.h" #include "log.h" +#include "watchdog.h" static int watchdog_fd = -1; static usec_t watchdog_timeout = USEC_INFINITY; |