summaryrefslogtreecommitdiff
path: root/src/shared
diff options
context:
space:
mode:
Diffstat (limited to 'src/shared')
-rw-r--r--src/shared/acl-util.c5
-rw-r--r--src/shared/acl-util.h2
-rw-r--r--src/shared/acpi-fpdt.c14
-rw-r--r--src/shared/apparmor-util.c7
-rw-r--r--src/shared/architecture.c2
-rw-r--r--src/shared/ask-password-api.c387
-rw-r--r--src/shared/ask-password-api.h20
-rw-r--r--src/shared/base-filesystem.c7
-rw-r--r--src/shared/boot-timestamps.c2
-rw-r--r--src/shared/bus-util.c278
-rw-r--r--src/shared/bus-util.h6
-rw-r--r--src/shared/cgroup-show.c16
-rw-r--r--src/shared/cgroup-show.h1
-rw-r--r--src/shared/clean-ipc.c45
-rw-r--r--src/shared/condition.c33
-rw-r--r--src/shared/conf-parser.c50
-rw-r--r--src/shared/conf-parser.h2
-rw-r--r--src/shared/dev-setup.c6
-rw-r--r--src/shared/dns-domain.c49
-rw-r--r--src/shared/dns-domain.h4
-rw-r--r--src/shared/dropin.c13
-rw-r--r--src/shared/efivars.c14
-rw-r--r--src/shared/efivars.h1
-rw-r--r--src/shared/firewall-util.c5
-rw-r--r--src/shared/formats-util.h63
-rw-r--r--src/shared/fstab-util.c63
-rw-r--r--src/shared/fstab-util.h7
-rw-r--r--src/shared/generator.c39
-rw-r--r--src/shared/import-util.c33
-rw-r--r--src/shared/import-util.h2
-rw-r--r--src/shared/install-printf.c58
-rw-r--r--src/shared/install.c2045
-rw-r--r--src/shared/install.h50
-rw-r--r--src/shared/logs-show.c27
-rw-r--r--src/shared/logs-show.h2
-rw-r--r--src/shared/machine-image.c52
-rw-r--r--src/shared/machine-image.h4
-rw-r--r--src/shared/machine-pool.c67
-rw-r--r--src/shared/nss-util.h6
-rw-r--r--src/shared/pager.c13
-rw-r--r--src/shared/path-lookup.c47
-rw-r--r--src/shared/ptyfwd.c56
-rw-r--r--src/shared/ptyfwd.h12
-rw-r--r--src/shared/seccomp-util.c3
-rw-r--r--src/shared/sleep-config.c9
-rw-r--r--src/shared/spawn-ask-password-agent.c12
-rw-r--r--src/shared/spawn-polkit-agent.c16
-rw-r--r--src/shared/specifier.c6
-rw-r--r--src/shared/switch-root.c17
-rw-r--r--src/shared/sysctl-util.c3
-rw-r--r--src/shared/uid-range.c3
-rw-r--r--src/shared/utmp-wtmp.c12
-rw-r--r--src/shared/watchdog.c5
53 files changed, 2274 insertions, 1427 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/ask-password-api.c b/src/shared/ask-password-api.c
index b02cdf9a17..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, sizeof(ANSI_HIGHLIGHT)-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_NORMAL, sizeof(ANSI_NORMAL)-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 ccb3be0fca..913cad9f8a 100644
--- a/src/shared/ask-password-api.h
+++ b/src/shared/ask-password-api.h
@@ -25,10 +25,16 @@
#include "time-util.h"
-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);
+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 48492ed13d..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 {
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 10df3fc3d6..38281045b8 100644
--- a/src/shared/bus-util.c
+++ b/src/shared/bus-util.c
@@ -21,27 +21,39 @@
#include <sys/socket.h>
+#include "sd-bus.h"
#include "sd-daemon.h"
#include "sd-event.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 "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"
-#include "bus-util.h"
-
static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) {
sd_event *e = userdata;
@@ -1416,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);
@@ -1426,13 +1469,12 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
"CPUAccounting", "MemoryAccounting", "BlockIOAccounting", "TasksAccounting",
"SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies",
"IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit",
- "PrivateTmp", "PrivateDevices", "PrivateNetwork", "NoNewPrivileges")) {
+ "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);
@@ -1493,10 +1535,33 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
"UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath",
"StandardInput", "StandardOutput", "StandardError",
"Description", "Slice", "Type", "WorkingDirectory",
- "RootDirectory"))
+ "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);
@@ -1607,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_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);
- r = sd_bus_message_append(m, "v", "as", 1, eq);
+ 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;
@@ -1632,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);
@@ -2137,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 f03f951dc7..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 {
@@ -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 c282fb1231..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,
@@ -694,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);
@@ -719,25 +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)) {
+ if (!utf8_is_valid(word)) {
log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
- free(n);
+ 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, 0, "Trailing garbage, ignoring.");
return 0;
}
diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h
index fb0234baae..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"
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 1845068adb..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) {
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 db2146f8c1..eb2845cddf 100644
--- a/src/shared/fstab-util.c
+++ b/src/shared/fstab-util.c
@@ -19,13 +19,17 @@
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_free_ char *device = NULL;
_cleanup_endmntent_ FILE *f = NULL;
struct mntent *m;
@@ -195,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 872b2363cd..5ebea44019 100644
--- a/src/shared/fstab-util.h
+++ b/src/shared/fstab-util.h
@@ -23,11 +23,12 @@
#include <stdbool.h>
#include <stddef.h>
+
#include "macro.h"
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_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);
@@ -49,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 238433c808..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,63 +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, 0, "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;
}
@@ -1024,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 },
@@ -1034,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,
@@ -1070,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) +
@@ -1079,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;
@@ -1095,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);
@@ -1107,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)) {
@@ -1136,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(
@@ -1356,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);
@@ -1374,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,
@@ -1428,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);
@@ -1436,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;
@@ -1526,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(
@@ -1588,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;
@@ -1603,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(
@@ -1638,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;
@@ -1655,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(
@@ -1677,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) {
@@ -1699,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(
@@ -1743,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;
@@ -1871,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) {
@@ -1890,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",
@@ -1906,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) {
@@ -1922,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);
}
}
@@ -1963,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,
@@ -1977,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;
@@ -1992,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(
@@ -2045,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;
@@ -2062,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)
@@ -2075,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) {
@@ -2141,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(
@@ -2156,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)
@@ -2172,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)
@@ -2185,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;
@@ -2221,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 */
}
}
@@ -2276,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 dbc07aa7ad..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
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 9c1e4d5e13..2c1da0a40d 100644
--- a/src/shared/machine-image.c
+++ b/src/shared/machine-image.c
@@ -23,15 +23,22 @@
#include <linux/fs.h>
#include <sys/statfs.h>
+#include "alloc-util.h"
#include "btrfs-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 "string-table.h"
+#include "string-util.h"
#include "strv.h"
#include "utf8.h"
-
-#include "machine-image.h"
+#include "xattr-util.h"
static const char image_search_path[] =
"/var/lib/machines\0"
@@ -176,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;
@@ -195,13 +201,17 @@ static int image_make(
if (r < 0)
return r;
- r = btrfs_subvol_get_quota_fd(fd, &quota);
- 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, &quota);
+ 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;
@@ -397,7 +407,7 @@ int image_remove(Image *i) {
switch (i->type) {
case IMAGE_SUBVOLUME:
- r = btrfs_subvol_remove(i->path, true);
+ r = btrfs_subvol_remove(i->path, BTRFS_REMOVE_RECURSIVE|BTRFS_REMOVE_QUOTA);
if (r < 0)
return r;
break;
@@ -587,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:
@@ -629,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;
@@ -729,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 d8f0fb404d..d149bc1722 100644
--- a/src/shared/pager.c
+++ b/src/shared/pager.c
@@ -21,17 +21,20 @@
#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;
diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c
index d803bbe07e..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;
}
@@ -342,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;
@@ -360,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;
@@ -417,9 +419,8 @@ void lookup_paths_free(LookupPaths *p) {
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/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 3dedbd1f62..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);
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 b2cab948ef..70caa542e7 100644
--- a/src/shared/sysctl-util.c
+++ b/src/shared/sysctl-util.c
@@ -29,8 +29,9 @@
#include "fileio.h"
#include "log.h"
-#include "util.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;