summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/activate/activate.c410
-rw-r--r--src/core/cgroup-attr.c58
-rw-r--r--src/core/cgroup-attr.h7
-rw-r--r--src/core/cgroup-semantics.c333
-rw-r--r--src/core/cgroup-semantics.h43
-rw-r--r--src/core/dbus-manager.c41
-rw-r--r--src/core/dbus-unit.c329
-rw-r--r--src/core/dbus-unit.h25
-rw-r--r--src/core/execute.c41
-rw-r--r--src/core/execute.h2
-rw-r--r--src/core/job.c29
-rw-r--r--src/core/load-fragment-gperf.gperf.m416
-rw-r--r--src/core/load-fragment.c300
-rw-r--r--src/core/load-fragment.h6
-rw-r--r--src/core/manager.c191
-rw-r--r--src/core/manager.h13
-rw-r--r--src/core/mount-setup.c10
-rw-r--r--src/core/transaction.c4
-rw-r--r--src/core/unit.c218
-rw-r--r--src/core/unit.h5
-rw-r--r--src/journal/journalctl.c4
-rw-r--r--src/journal/sd-journal.c4
-rw-r--r--src/kernel-install/kernel-install152
-rw-r--r--src/nspawn/nspawn.c126
-rw-r--r--src/shared/log.c19
-rw-r--r--src/shared/path-lookup.c9
-rw-r--r--src/shared/socket-util.c39
-rw-r--r--src/shared/socket-util.h2
-rw-r--r--src/shared/strv.c25
-rw-r--r--src/shared/strv.h1
-rw-r--r--src/shared/util.c57
-rw-r--r--src/shared/util.h7
-rw-r--r--src/systemctl/systemctl.c186
-rw-r--r--src/test/test-util.c108
34 files changed, 2051 insertions, 769 deletions
diff --git a/src/activate/activate.c b/src/activate/activate.c
new file mode 100644
index 0000000000..7fcb0a97c4
--- /dev/null
+++ b/src/activate/activate.c
@@ -0,0 +1,410 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Zbigniew Jędrzejewski-Szmek
+
+ 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 <unistd.h>
+#include <fcntl.h>
+#include <sys/epoll.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <getopt.h>
+
+#include <systemd/sd-daemon.h>
+
+#include "socket-util.h"
+#include "build.h"
+#include "log.h"
+#include "strv.h"
+#include "macro.h"
+
+static char** arg_listen = NULL;
+static bool arg_accept = false;
+static char** arg_args = NULL;
+
+static int add_epoll(int epoll_fd, int fd) {
+ int r;
+ struct epoll_event ev = {EPOLLIN};
+ ev.data.fd = fd;
+
+ assert(epoll_fd >= 0);
+ assert(fd >= 0);
+
+ r = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
+ if (r < 0)
+ log_error("Failed to add event on epoll fd:%d for fd:%d: %s",
+ epoll_fd, fd, strerror(-r));
+ return r;
+}
+
+static int set_nocloexec(int fd) {
+ int flags;
+
+ flags = fcntl(fd, F_GETFD);
+ if (flags < 0) {
+ log_error("Querying flags for fd:%d: %m", fd);
+ return -errno;
+ }
+
+ if (!(flags & FD_CLOEXEC))
+ return 0;
+
+ if (fcntl(fd, F_SETFD, flags & ~FD_CLOEXEC) < 0) {
+ log_error("Settings flags for fd:%d: %m", fd);
+ return -errno;
+ }
+
+ return 0;
+}
+
+static int print_socket(const char* desc, int fd) {
+ int r;
+ SocketAddress addr = {
+ .size = sizeof(union sockaddr_union),
+ .type = SOCK_STREAM,
+ };
+ int family;
+
+ r = getsockname(fd, &addr.sockaddr.sa, &addr.size);
+ if (r < 0) {
+ log_warning("Failed to query socket on fd:%d: %m", fd);
+ return 0;
+ }
+
+ family = socket_address_family(&addr);
+ switch(family) {
+ case AF_INET:
+ case AF_INET6: {
+ char* _cleanup_free_ a = NULL;
+ r = socket_address_print(&addr, &a);
+ if (r < 0)
+ log_warning("socket_address_print(): %s", strerror(-r));
+ else
+ log_info("%s %s address %s",
+ desc,
+ family == AF_INET ? "IP" : "IPv6",
+ a);
+ break;
+ }
+ default:
+ log_warning("Connection with unknown family %d", family);
+ }
+
+ return 0;
+}
+
+static int open_sockets(int *epoll_fd, bool accept) {
+ int n, fd;
+ int count = 0;
+ char **address;
+
+ n = sd_listen_fds(true);
+ if (n < 0) {
+ log_error("Failed to read listening file descriptors from environment: %s",
+ strerror(-n));
+ return n;
+ }
+ log_info("Received %d descriptors", n);
+
+ for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
+ log_debug("Received descriptor fd:%d", fd);
+ print_socket("Listening on", fd);
+
+ if (!arg_accept) {
+ int r = set_nocloexec(fd);
+ if (r < 0)
+ return r;
+ }
+
+ count ++;
+ }
+
+ STRV_FOREACH(address, arg_listen) {
+ log_info("Opening address %s", *address);
+
+ fd = make_socket_fd(*address, SOCK_STREAM | (arg_accept*SOCK_CLOEXEC));
+ if (fd < 0) {
+ log_error("Failed to open '%s': %s", *address, strerror(-fd));
+ return fd;
+ }
+
+ count ++;
+ }
+
+ *epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+ if (*epoll_fd < 0) {
+ log_error("Failed to create epoll object: %m");
+ return -errno;
+ }
+
+ for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) {
+ int r = add_epoll(*epoll_fd, fd);
+ if (r < 0)
+ return r;
+ }
+
+ return count;
+}
+
+static int launch(char* name, char **argv, char **environ, int fds) {
+ unsigned n_env = 0;
+ char* envp[7] = {NULL}; /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID */
+ static const char* tocopy[] = {"TERM=", "PATH=", "USER=", "HOME="};
+ char _cleanup_free_ *tmp = NULL;
+ unsigned i;
+
+ for (i = 0; i < ELEMENTSOF(tocopy); i++) {
+ envp[n_env] = strv_find_prefix(environ, tocopy[i]);
+ if (envp[n_env])
+ n_env ++;
+ }
+
+ if ((asprintf((char**)(envp + n_env++), "LISTEN_FDS=%d", fds) < 0) ||
+ (asprintf((char**)(envp + n_env++), "LISTEN_PID=%d", getpid()) < 0))
+ return log_oom();
+
+ tmp = strv_join(argv, " ");
+ if (!tmp)
+ return log_oom();
+
+ log_info("Execing %s (%s)", name, tmp);
+ execvpe(name, argv, envp);
+ log_error("Failed to execp %s (%s): %m", name, tmp);
+ return -errno;
+}
+
+static int launch1(const char* child, char** argv, char **environ, int fd) {
+ pid_t parent_pid, child_pid;
+ int r;
+
+ char _cleanup_free_ *tmp = NULL;
+ tmp = strv_join(argv, " ");
+ if (!tmp)
+ return log_oom();
+
+ parent_pid = getpid();
+
+ child_pid = fork();
+ if (child_pid < 0) {
+ log_error("Failed to fork: %m");
+ return -errno;
+ }
+
+ /* In the child */
+ if (child_pid == 0) {
+ r = dup2(fd, STDIN_FILENO);
+ if (r < 0) {
+ log_error("Failed to dup connection to stdin: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ r = dup2(fd, STDOUT_FILENO);
+ if (r < 0) {
+ log_error("Failed to dup connection to stdout: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ r = close(fd);
+ if (r < 0) {
+ log_error("Failed to close dupped connection: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ /* Make sure the child goes away when the parent dies */
+ if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
+ _exit(EXIT_FAILURE);
+
+ /* Check whether our parent died before we were able
+ * to set the death signal */
+ if (getppid() != parent_pid)
+ _exit(EXIT_SUCCESS);
+
+ execvp(child, argv);
+ log_error("Failed to exec child %s: %m", child);
+ _exit(EXIT_FAILURE);
+ }
+
+ log_info("Spawned %s (%s) as PID %d", child, tmp, child_pid);
+
+ return 0;
+}
+
+static int do_accept(const char* name, char **argv, char **envp, int fd) {
+ SocketAddress addr = {
+ .size = sizeof(union sockaddr_union),
+ .type = SOCK_STREAM,
+ };
+ int fd2, r;
+
+ fd2 = accept(fd, &addr.sockaddr.sa, &addr.size);
+ if (fd2 < 0) {
+ log_error("Failed to accept connection on fd:%d: %m", fd);
+ return fd2;
+ }
+
+ print_socket("Connection from", fd2);
+
+ r = launch1(name, argv, envp, fd2);
+ return r;
+}
+
+/* SIGCHLD handler. */
+static void sigchld_hdl(int sig, siginfo_t *t, void *data)
+{
+ log_info("Child %d died with code %d", t->si_pid, t->si_status);
+ /* Wait for a dead child. */
+ waitpid(t->si_pid, NULL, 0);
+}
+
+static int install_chld_handler(void) {
+ int r;
+ struct sigaction act;
+ zero(act);
+ act.sa_flags = SA_SIGINFO;
+ act.sa_sigaction = sigchld_hdl;
+
+ r = sigaction(SIGCHLD, &act, 0);
+ if (r < 0)
+ log_error("Failed to install SIGCHLD handler: %m");
+ return r;
+}
+
+static int help(void) {
+ printf("%s [OPTIONS...]\n\n"
+ "Listen on sockets and launch child on connection.\n\n"
+ "Options:\n"
+ " -l --listen=ADDR Listen for raw connections at ADDR\n"
+ " -a --accept Spawn separate child for each connection\n"
+ " -h --help Show this help and exit\n"
+ " --version Print version string and exit\n"
+ "\n"
+ "Note: file descriptors from sd_listen_fds() will be passed through.\n"
+ , program_invocation_short_name
+ );
+
+ return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+ enum {
+ ARG_VERSION = 0x100,
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ { "listen", required_argument, NULL, 'l' },
+ { "accept", no_argument, NULL, 'a' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "+hl:sa", options, NULL)) >= 0)
+ switch(c) {
+ case 'h':
+ help();
+ return 0 /* done */;
+
+ case ARG_VERSION:
+ puts(PACKAGE_STRING);
+ puts(SYSTEMD_FEATURES);
+ return 0 /* done */;
+
+ case 'l': {
+ int r = strv_extend(&arg_listen, optarg);
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
+ case 'a':
+ arg_accept = true;
+ break;
+
+ case '?':
+ return -EINVAL;
+
+ default:
+ log_error("Unknown option code %c", c);
+ return -EINVAL;
+ }
+
+ if (optind == argc) {
+ log_error("Usage: %s [OPTION...] PROGRAM [OPTION...]",
+ program_invocation_short_name);
+ return -EINVAL;
+ }
+
+ arg_args = argv + optind;
+
+ return 1 /* work to do */;
+}
+
+int main(int argc, char **argv, char **envp) {
+ int r, n;
+ int epoll_fd = -1;
+
+ log_set_max_level(LOG_DEBUG);
+ log_show_color(true);
+ log_parse_environment();
+
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+
+ r = install_chld_handler();
+ if (r < 0)
+ return EXIT_FAILURE;
+
+ n = open_sockets(&epoll_fd, arg_accept);
+ if (n < 0)
+ return EXIT_FAILURE;
+
+ while (true) {
+ struct epoll_event event;
+
+ r = epoll_wait(epoll_fd, &event, 1, -1);
+ if (r < 0) {
+ if (errno == EINTR)
+ continue;
+
+ log_error("epoll_wait() failed: %m");
+ return EXIT_FAILURE;
+ }
+
+ log_info("Communication attempt on fd:%d", event.data.fd);
+ if (arg_accept) {
+ r = do_accept(argv[optind], argv + optind, envp,
+ event.data.fd);
+ if (r < 0)
+ return EXIT_FAILURE;
+ } else
+ break;
+ }
+
+ launch(argv[optind], argv + optind, envp, n);
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/core/cgroup-attr.c b/src/core/cgroup-attr.c
index 1373684bdb..2ab4d4623e 100644
--- a/src/core/cgroup-attr.c
+++ b/src/core/cgroup-attr.c
@@ -25,8 +25,8 @@
#include "fileio.h"
int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b) {
- int r;
_cleanup_free_ char *path = NULL, *v = NULL;
+ int r;
assert(a);
@@ -34,8 +34,8 @@ int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b) {
if (!b)
return 0;
- if (a->map_callback) {
- r = a->map_callback(a->controller, a->name, a->value, &v);
+ if (a->semantics && a->semantics->map_write) {
+ r = a->semantics->map_write(a->semantics, a->value, &v);
if (r < 0)
return r;
}
@@ -66,6 +66,29 @@ int cgroup_attribute_apply_list(CGroupAttribute *first, CGroupBonding *b) {
return r;
}
+bool cgroup_attribute_matches(CGroupAttribute *a, const char *controller, const char *name) {
+ assert(a);
+
+ if (controller) {
+ if (streq(a->controller, controller) && (!name || streq(a->name, name)))
+ return true;
+
+ } else if (!name)
+ return true;
+ else if (streq(a->name, name)) {
+ size_t x, y;
+ x = strlen(a->controller);
+ y = strlen(name);
+
+ if (y > x &&
+ memcmp(a->controller, name, x) == 0 &&
+ name[x] == '.')
+ return true;
+ }
+
+ return false;
+}
+
CGroupAttribute *cgroup_attribute_find_list(
CGroupAttribute *first,
const char *controller,
@@ -74,24 +97,9 @@ CGroupAttribute *cgroup_attribute_find_list(
assert(name);
- LIST_FOREACH(by_unit, a, first) {
-
-
- if (controller) {
- if (streq(a->controller, controller) && streq(a->name, name))
- return a;
-
- } else if (streq(a->name, name)) {
- size_t x, y;
- x = strlen(a->controller);
- y = strlen(name);
-
- if (y > x &&
- memcmp(a->controller, name, x) == 0 &&
- name[x] == '.')
- return a;
- }
- }
+ LIST_FOREACH(by_unit, a, first)
+ if (cgroup_attribute_matches(a, controller, name))
+ return a;
return NULL;
}
@@ -114,3 +122,11 @@ void cgroup_attribute_free_list(CGroupAttribute *first) {
LIST_FOREACH_SAFE(by_unit, a, n, first)
cgroup_attribute_free(a);
}
+
+void cgroup_attribute_free_some(CGroupAttribute *first, const char *controller, const char *name) {
+ CGroupAttribute *a, *n;
+
+ LIST_FOREACH_SAFE(by_unit, a, n, first)
+ if (cgroup_attribute_matches(a, controller, name))
+ cgroup_attribute_free(a);
+}
diff --git a/src/core/cgroup-attr.h b/src/core/cgroup-attr.h
index 0f5b854898..0b542981e8 100644
--- a/src/core/cgroup-attr.h
+++ b/src/core/cgroup-attr.h
@@ -23,10 +23,9 @@
typedef struct CGroupAttribute CGroupAttribute;
-typedef int (*CGroupAttributeMapCallback)(const char *controller, const char*name, const char *value, char **ret);
-
#include "unit.h"
#include "cgroup.h"
+#include "cgroup-semantics.h"
struct CGroupAttribute {
char *controller;
@@ -35,7 +34,7 @@ struct CGroupAttribute {
Unit *unit;
- CGroupAttributeMapCallback map_callback;
+ const CGroupSemantics *semantics;
LIST_FIELDS(CGroupAttribute, by_unit);
};
@@ -43,7 +42,9 @@ struct CGroupAttribute {
int cgroup_attribute_apply(CGroupAttribute *a, CGroupBonding *b);
int cgroup_attribute_apply_list(CGroupAttribute *first, CGroupBonding *b);
+bool cgroup_attribute_matches(CGroupAttribute *a, const char *controller, const char *name);
CGroupAttribute *cgroup_attribute_find_list(CGroupAttribute *first, const char *controller, const char *name);
void cgroup_attribute_free(CGroupAttribute *a);
void cgroup_attribute_free_list(CGroupAttribute *first);
+void cgroup_attribute_free_some(CGroupAttribute *first, const char *controller, const char *name);
diff --git a/src/core/cgroup-semantics.c b/src/core/cgroup-semantics.c
new file mode 100644
index 0000000000..82b02bbd78
--- /dev/null
+++ b/src/core/cgroup-semantics.c
@@ -0,0 +1,333 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "util.h"
+#include "strv.h"
+#include "path-util.h"
+#include "cgroup-util.h"
+
+#include "cgroup-semantics.h"
+
+static int parse_cpu_shares(const CGroupSemantics *s, const char *value, char **ret) {
+ unsigned long ul;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ if (safe_atolu(value, &ul) < 0 || ul < 1)
+ return -EINVAL;
+
+ if (asprintf(ret, "%lu", ul) < 0)
+ return -ENOMEM;
+
+ return 1;
+}
+
+static int parse_memory_limit(const CGroupSemantics *s, const char *value, char **ret) {
+ off_t sz;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ if (parse_bytes(value, &sz) < 0 || sz <= 0)
+ return -EINVAL;
+
+ if (asprintf(ret, "%llu", (unsigned long long) sz) < 0)
+ return -ENOMEM;
+
+ return 1;
+}
+
+static int parse_device(const CGroupSemantics *s, const char *value, char **ret) {
+ _cleanup_strv_free_ char **l = NULL;
+ char *x;
+ unsigned k;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ l = strv_split_quoted(value);
+ if (!l)
+ return -ENOMEM;
+
+ k = strv_length(l);
+ if (k < 1 || k > 2)
+ return -EINVAL;
+
+ if (!streq(l[0], "*") && !path_startswith(l[0], "/dev"))
+ return -EINVAL;
+
+ if (!isempty(l[1]) && !in_charset(l[1], "rwm"))
+ return -EINVAL;
+
+ x = strdup(value);
+ if (!x)
+ return -ENOMEM;
+
+ *ret = x;
+ return 1;
+}
+
+static int parse_blkio_weight(const CGroupSemantics *s, const char *value, char **ret) {
+ _cleanup_strv_free_ char **l = NULL;
+ unsigned long ul;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ l = strv_split_quoted(value);
+ if (!l)
+ return -ENOMEM;
+
+ if (strv_length(l) != 1)
+ return 0; /* Returning 0 will cause parse_blkio_weight_device() be tried instead */
+
+ if (safe_atolu(l[0], &ul) < 0 || ul < 10 || ul > 1000)
+ return -EINVAL;
+
+ if (asprintf(ret, "%lu", ul) < 0)
+ return -ENOMEM;
+
+ return 1;
+}
+
+static int parse_blkio_weight_device(const CGroupSemantics *s, const char *value, char **ret) {
+ _cleanup_strv_free_ char **l = NULL;
+ unsigned long ul;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ l = strv_split_quoted(value);
+ if (!l)
+ return -ENOMEM;
+
+ if (strv_length(l) != 2)
+ return -EINVAL;
+
+ if (!path_startswith(l[0], "/dev"))
+ return -EINVAL;
+
+ if (safe_atolu(l[1], &ul) < 0 || ul < 10 || ul > 1000)
+ return -EINVAL;
+
+ if (asprintf(ret, "%s %lu", l[0], ul) < 0)
+ return -ENOMEM;
+
+ return 1;
+}
+
+static int parse_blkio_bandwidth(const CGroupSemantics *s, const char *value, char **ret) {
+ _cleanup_strv_free_ char **l = NULL;
+ off_t bytes;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ l = strv_split_quoted(value);
+ if (!l)
+ return -ENOMEM;
+
+ if (strv_length(l) != 2)
+ return -EINVAL;
+
+ if (!path_startswith(l[0], "/dev")) {
+ return -EINVAL;
+ }
+
+ if (parse_bytes(l[1], &bytes) < 0 || bytes <= 0)
+ return -EINVAL;
+
+ if (asprintf(ret, "%s %llu", l[0], (unsigned long long) bytes) < 0)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int map_device(const CGroupSemantics *s, const char *value, char **ret) {
+ _cleanup_strv_free_ char **l = NULL;
+ unsigned k;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ l = strv_split_quoted(value);
+ if (!l)
+ return -ENOMEM;
+
+ k = strv_length(l);
+ if (k < 1 || k > 2)
+ return -EINVAL;
+
+ if (streq(l[0], "*")) {
+
+ if (asprintf(ret, "a *:*%s%s",
+ isempty(l[1]) ? "" : " ", strempty(l[1])) < 0)
+ return -ENOMEM;
+ } else {
+ struct stat st;
+
+ if (stat(l[0], &st) < 0) {
+ log_warning("Couldn't stat device %s", l[0]);
+ return -errno;
+ }
+
+ if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
+ log_warning("%s is not a device.", l[0]);
+ return -ENODEV;
+ }
+
+ if (asprintf(ret, "%c %u:%u%s%s",
+ S_ISCHR(st.st_mode) ? 'c' : 'b',
+ major(st.st_rdev), minor(st.st_rdev),
+ isempty(l[1]) ? "" : " ", strempty(l[1])) < 0)
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int map_blkio(const CGroupSemantics *s, const char *value, char **ret) {
+ _cleanup_strv_free_ char **l = NULL;
+ struct stat st;
+ dev_t d;
+
+ assert(s);
+ assert(value);
+ assert(ret);
+
+ l = strv_split_quoted(value);
+ if (!l)
+ return log_oom();
+
+ if (strv_length(l) != 2)
+ return -EINVAL;
+
+ if (stat(l[0], &st) < 0) {
+ log_warning("Couldn't stat device %s", l[0]);
+ return -errno;
+ }
+
+ if (S_ISBLK(st.st_mode))
+ d = st.st_rdev;
+ else if (major(st.st_dev) != 0) {
+ /* If this is not a device node then find the block
+ * device this file is stored on */
+ d = st.st_dev;
+
+ /* If this is a partition, try to get the originating
+ * block device */
+ block_get_whole_disk(d, &d);
+ } else {
+ log_warning("%s is not a block device and file system block device cannot be determined or is not local.", l[0]);
+ return -ENODEV;
+ }
+
+ if (asprintf(ret, "%u:%u %s", major(d), minor(d), l[1]) < 0)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static const CGroupSemantics semantics[] = {
+ { "cpu", "cpu.shares", "CPUShare", false, parse_cpu_shares, NULL, NULL },
+ { "memory", "memory.soft_limit_in_bytes", "MemorySoftLimit", false, parse_memory_limit, NULL, NULL },
+ { "memory", "memory.limit_in_bytes", "MemoryLimit", false, parse_memory_limit, NULL, NULL },
+ { "devices", "devices.allow", "DeviceAllow", true, parse_device, map_device, NULL },
+ { "devices", "devices.deny", "DeviceDeny", true, parse_device, map_device, NULL },
+ { "blkio", "blkio.weight", "BlockIOWeight", false, parse_blkio_weight, NULL, NULL },
+ { "blkio", "blkio.weight_device", "BlockIOWeight", true, parse_blkio_weight_device, map_blkio, NULL },
+ { "blkio", "blkio.read_bps_device", "BlockIOReadBandwidth", true, parse_blkio_bandwidth, map_blkio, NULL },
+ { "blkio", "blkio.write_bps_device", "BlockIOWriteBandwidth", true, parse_blkio_bandwidth, map_blkio, NULL }
+};
+
+int cgroup_semantics_find(
+ const char *controller,
+ const char *name,
+ const char *value,
+ char **ret,
+ const CGroupSemantics **_s) {
+
+ _cleanup_free_ char *c = NULL;
+ unsigned i;
+ int r;
+
+ assert(name);
+ assert(_s);
+ assert(!value == !ret);
+
+ if (!controller) {
+ r = cg_controller_from_attr(name, &c);
+ if (r < 0)
+ return r;
+
+ controller = c;
+ }
+
+ for (i = 0; i < ELEMENTSOF(semantics); i++) {
+ const CGroupSemantics *s = semantics + i;
+ bool matches_name, matches_pretty;
+
+ if (controller && s->controller && !streq(s->controller, controller))
+ continue;
+
+ matches_name = s->name && streq(s->name, name);
+ matches_pretty = s->pretty && streq(s->pretty, name);
+
+ if (!matches_name && !matches_pretty)
+ continue;
+
+ if (value) {
+ if (matches_pretty && s->map_pretty) {
+
+ r = s->map_pretty(s, value, ret);
+ if (r < 0)
+ return r;
+
+ if (r == 0)
+ continue;
+
+ } else {
+ char *x;
+
+ x = strdup(value);
+ if (!x)
+ return -ENOMEM;
+
+ *ret = x;
+ }
+ }
+
+ *_s = s;
+ return 1;
+ }
+
+ *ret = NULL;
+ *_s = NULL;
+ return 0;
+}
diff --git a/src/core/cgroup-semantics.h b/src/core/cgroup-semantics.h
new file mode 100644
index 0000000000..4f848f4bb7
--- /dev/null
+++ b/src/core/cgroup-semantics.h
@@ -0,0 +1,43 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2011 Lennart Poettering
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+typedef struct CGroupSemantics CGroupSemantics;
+
+struct CGroupSemantics {
+ const char *controller;
+ const char *name;
+ const char *pretty;
+
+ bool multiple;
+
+ /* This call is used for parsing the pretty value to the actual attribute value */
+ int (*map_pretty)(const CGroupSemantics *semantics, const char *value, char **ret);
+
+ /* Right before writing this attribute the attribute value is converted to a low-level value */
+ int (*map_write)(const CGroupSemantics *semantics, const char *value, char **ret);
+
+ /* If this attribute takes a list, this call can be used to reset the list to empty */
+ int (*reset)(const CGroupSemantics *semantics, const char *group);
+};
+
+int cgroup_semantics_find(const char *controller, const char *name, const char *value, char **ret, const CGroupSemantics **semantics);
diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c
index de23369397..8f4bbc59b7 100644
--- a/src/core/dbus-manager.c
+++ b/src/core/dbus-manager.c
@@ -103,30 +103,31 @@
" <method name=\"ResetFailedUnit\">\n" \
" <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
- " <method name=\"GetUnitControlGroupAttributes\">\n" \
+ " <method name=\"SetUnitControlGroup\">\n" \
" <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"attributes\" type=\"as\" direction=\"in\"/>\n" \
- " <arg name=\"values\" type=\"as\" direction=\"out\"/>\n" \
+ " <arg name=\"group\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
- " <method name=\"SetUnitControlGroupAttributes\">\n" \
+ " <method name=\"UnsetUnitControlGroup\">\n" \
" <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"attributes\" type=\"a(sss)\" direction=\"in\"/>\n" \
+ " <arg name=\"group\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"mode\" type=\"s\" direction=\"in\"\n/>" \
" </method>\n" \
- " <method name=\"UnsetUnitControlGroupAttributes\">\n" \
+ " <method name=\"GetUnitControlGroupAttribute\">\n" \
" <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"attributes\" type=\"a(ss)\" direction=\"in\"/>\n" \
- " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"values\" type=\"as\" direction=\"out\"/>\n" \
" </method>\n" \
- " <method name=\"SetUnitControlGroups\">\n" \
+ " <method name=\"SetUnitControlGroupAttribute\">\n" \
" <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"groups\" type=\"as\" direction=\"in\"/>\n" \
- " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"values\" type=\"as\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"\n/>" \
" </method>\n" \
- " <method name=\"UnsetUnitControlGroups\">\n" \
+ " <method name=\"UnsetUnitControlGroupAttributes\">\n" \
" <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
- " <arg name=\"groups\" type=\"as\" direction=\"in\"/>\n" \
- " <arg name=\"mode\" type=\"s\" direction=\"in\"\n/>" \
+ " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"GetJob\">\n" \
" <arg name=\"id\" type=\"u\" direction=\"in\"/>\n" \
@@ -874,7 +875,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
if (!reply)
goto oom;
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetUnitControlGroups")) {
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetUnitControlGroup")) {
const char *name;
Unit *u;
DBusMessageIter iter;
@@ -902,7 +903,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
if (!reply)
goto oom;
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetUnitControlGroups")) {
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetUnitControlGroup")) {
const char *name;
Unit *u;
DBusMessageIter iter;
@@ -930,7 +931,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
if (!reply)
goto oom;
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetUnitControlGroupAttributes")) {
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "SetUnitControlGroupAttribute")) {
const char *name;
Unit *u;
DBusMessageIter iter;
@@ -949,6 +950,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
}
SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
+
r = bus_unit_cgroup_attribute_set(u, &iter);
if (r < 0)
return bus_send_error_reply(connection, message, NULL, r);
@@ -957,7 +959,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
if (!reply)
goto oom;
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetUnitControlGroupAttributes")) {
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "UnsetUnitControlGroupAttribute")) {
const char *name;
Unit *u;
DBusMessageIter iter;
@@ -985,7 +987,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
if (!reply)
goto oom;
- } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnitControlGroupAttributes")) {
+ } else if (dbus_message_is_method_call(message, "org.freedesktop.systemd1.Manager", "GetUnitControlGroupAttribute")) {
const char *name;
Unit *u;
DBusMessageIter iter;
@@ -1005,6 +1007,7 @@ static DBusHandlerResult bus_manager_message_handler(DBusConnection *connection,
}
SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "status");
+
r = bus_unit_cgroup_attribute_get(u, &iter, &list);
if (r < 0)
return bus_send_error_reply(connection, message, NULL, r);
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c
index 4f968c20f2..7c23e1e616 100644
--- a/src/core/dbus-unit.c
+++ b/src/core/dbus-unit.c
@@ -344,8 +344,8 @@ static int bus_unit_append_cgroup_attrs(DBusMessageIter *i, const char *property
char _cleanup_free_ *v = NULL;
bool success;
- if (a->map_callback)
- a->map_callback(a->controller, a->name, a->value, &v);
+ if (a->semantics && a->semantics->map_write)
+ a->semantics->map_write(a->semantics, a->value, &v);
success =
dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) &&
@@ -472,7 +472,7 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn
if (!reply)
goto oom;
- } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroups")) {
+ } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroup")) {
DBusMessageIter iter;
SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
@@ -488,7 +488,7 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn
if (!reply)
goto oom;
- } else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroups")) {
+ } else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroup")) {
DBusMessageIter iter;
SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
@@ -496,14 +496,14 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn
if (!dbus_message_iter_init(message, &iter))
goto oom;
- r = bus_unit_cgroup_set(u, &iter);
+ r = bus_unit_cgroup_unset(u, &iter);
if (r < 0)
return bus_send_error_reply(connection, message, NULL, r);
reply = dbus_message_new_method_return(message);
if (!reply)
goto oom;
- } else if (streq_ptr(dbus_message_get_member(message), "GetControlGroupAttributes")) {
+ } else if (streq_ptr(dbus_message_get_member(message), "GetControlGroupAttribute")) {
DBusMessageIter iter;
_cleanup_strv_free_ char **list = NULL;
@@ -524,7 +524,7 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn
if (bus_append_strv_iter(&iter, list) < 0)
goto oom;
- } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroupAttributes")) {
+ } else if (streq_ptr(dbus_message_get_member(message), "SetControlGroupAttribute")) {
DBusMessageIter iter;
SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "start");
@@ -540,7 +540,7 @@ static DBusHandlerResult bus_unit_message_dispatch(Unit *u, DBusConnection *conn
if (!reply)
goto oom;
- } else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroupAttributes")) {
+ } else if (streq_ptr(dbus_message_get_member(message), "UnsetControlGroupAttribute")) {
DBusMessageIter iter;
SELINUX_UNIT_ACCESS_CHECK(u, connection, message, "stop");
@@ -897,17 +897,17 @@ oom:
return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
-static int next_and_parse_mode(DBusMessageIter *iter, bool *runtime) {
+static int parse_mode(DBusMessageIter *iter, bool *runtime, bool next) {
const char *mode;
+ int r;
assert(iter);
assert(runtime);
- dbus_message_iter_next(iter);
- if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
- return -EINVAL;
+ r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &mode, next);
+ if (r < 0)
+ return r;
- dbus_message_iter_get_basic(iter, &mode);
if (streq(mode, "runtime"))
*runtime = true;
else if (streq(mode, "persistent"))
@@ -919,10 +919,11 @@ static int next_and_parse_mode(DBusMessageIter *iter, bool *runtime) {
}
int bus_unit_cgroup_set(Unit *u, DBusMessageIter *iter) {
- int r;
- _cleanup_strv_free_ char **a = NULL;
- char **name;
+ _cleanup_free_ char *controller = NULL, *old_path = NULL, *new_path = NULL, *contents = NULL;
+ const char *name;
+ CGroupBonding *b;
bool runtime;
+ int r;
assert(u);
assert(iter);
@@ -930,60 +931,74 @@ int bus_unit_cgroup_set(Unit *u, DBusMessageIter *iter) {
if (!unit_get_exec_context(u))
return -EINVAL;
- r = bus_parse_strv_iter(iter, &a);
+ r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
if (r < 0)
return r;
- r = next_and_parse_mode(iter, &runtime);
+ r = parse_mode(iter, &runtime, false);
if (r < 0)
return r;
- STRV_FOREACH(name, a) {
- _cleanup_free_ char *controller = NULL, *old_path = NULL, *new_path = NULL, *contents = NULL;
- CGroupBonding *b;
-
- r = cg_split_spec(*name, &controller, &new_path);
- if (r < 0)
- return r;
+ r = cg_split_spec(name, &controller, &new_path);
+ if (r < 0)
+ return r;
- b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
- if (b) {
- old_path = strdup(b->path);
- if (!old_path)
- return -ENOMEM;
- }
+ if (!new_path) {
+ new_path = unit_default_cgroup_path(u);
+ if (!new_path)
+ return -ENOMEM;
+ }
- r = unit_add_cgroup_from_text(u, *name, true, &b);
- if (r < 0)
- return r;
+ if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
+ return -EINVAL;
- if (r > 0) {
- /* Try to move things to the new place, and clean up the old place */
- cgroup_bonding_realize(b);
- cgroup_bonding_migrate(b, u->cgroup_bondings);
+ b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
+ if (b) {
+ if (streq(b->path, new_path))
+ return 0;
- if (old_path)
- cg_trim(controller, old_path, true);
- }
+ if (b->essential)
+ return -EINVAL;
- contents = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
- "ControlGroup=", *name, "\n", NULL);
- if (!contents)
+ old_path = strdup(b->path);
+ if (!old_path)
return -ENOMEM;
+ }
- r = unit_write_drop_in(u, runtime, *name, contents);
- if (r < 0)
- return r;
+ r = unit_add_cgroup_from_text(u, name, true, &b);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ CGroupAttribute *a;
+
+ /* Try to move things to the new place, and clean up the old place */
+ cgroup_bonding_realize(b);
+ cgroup_bonding_migrate(b, u->cgroup_bondings);
+
+ if (old_path)
+ cg_trim(controller, old_path, true);
+
+ /* Apply the attributes to the new group */
+ LIST_FOREACH(by_unit, a, u->cgroup_attributes)
+ if (streq(a->controller, controller))
+ cgroup_attribute_apply(a, b);
}
- return 0;
+ contents = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
+ "ControlGroup=", name, "\n", NULL);
+ if (!contents)
+ return -ENOMEM;
+
+ return unit_write_drop_in(u, runtime, controller, contents);
}
int bus_unit_cgroup_unset(Unit *u, DBusMessageIter *iter) {
- _cleanup_strv_free_ char **a = NULL;
- char **name;
- int r;
+ _cleanup_free_ char *controller = NULL, *path = NULL, *target = NULL;
+ const char *name;
+ CGroupAttribute *a, *n;
+ CGroupBonding *b;
bool runtime;
+ int r;
assert(u);
assert(iter);
@@ -991,50 +1006,57 @@ int bus_unit_cgroup_unset(Unit *u, DBusMessageIter *iter) {
if (!unit_get_exec_context(u))
return -EINVAL;
- r = bus_parse_strv_iter(iter, &a);
+ r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
if (r < 0)
return r;
- r = next_and_parse_mode(iter, &runtime);
+ r = parse_mode(iter, &runtime, false);
if (r < 0)
return r;
- STRV_FOREACH(name, a) {
- _cleanup_free_ char *controller = NULL, *path = NULL, *target = NULL;
- CGroupBonding *b;
+ r = cg_split_spec(name, &controller, &path);
+ if (r < 0)
+ return r;
- r = cg_split_spec(*name, &controller, &path);
- if (r < 0)
- return r;
+ if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
+ return -EINVAL;
- if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
- return -EINVAL;
+ b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
+ if (!b)
+ return -ENOENT;
- unit_remove_drop_in(u, runtime, *name);
+ if (path && !path_equal(path, b->path))
+ return -ENOENT;
- b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
- if (!b)
- continue;
+ if (b->essential)
+ return -EINVAL;
- if (path && !path_equal(path, b->path))
- continue;
+ unit_remove_drop_in(u, runtime, controller);
- if (b->essential)
- return -EINVAL;
+ /* Try to migrate the old group away */
+ if (cg_get_by_pid(controller, 0, &target) >= 0)
+ cgroup_bonding_migrate_to(u->cgroup_bondings, target, false);
+
+ cgroup_bonding_free(b, true);
- /* Try to migrate the old group away */
- if (cg_get_by_pid(controller, 0, &target) >= 0)
- cgroup_bonding_migrate_to(u->cgroup_bondings, target, false);
+ /* Drop all attributes of this controller */
+ LIST_FOREACH_SAFE(by_unit, a, n, u->cgroup_attributes) {
+ if (!streq(a->controller, controller))
+ continue;
- cgroup_bonding_free(b, true);
+ unit_remove_drop_in(u, runtime, a->name);
+ cgroup_attribute_free(a);
}
return 0;
}
int bus_unit_cgroup_attribute_get(Unit *u, DBusMessageIter *iter, char ***_result) {
- _cleanup_strv_free_ char **l = NULL, **result = NULL;
- char **name;
+ _cleanup_free_ char *controller = NULL;
+ CGroupAttribute *a;
+ CGroupBonding *b;
+ const char *name;
+ char **l = NULL;
int r;
assert(u);
@@ -1044,63 +1066,99 @@ int bus_unit_cgroup_attribute_get(Unit *u, DBusMessageIter *iter, char ***_resul
if (!unit_get_exec_context(u))
return -EINVAL;
- r = bus_parse_strv_iter(iter, &l);
+ r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, false);
if (r < 0)
return r;
- STRV_FOREACH(name, l) {
- _cleanup_free_ char *controller = NULL;
- CGroupAttribute *a;
- CGroupBonding *b;
+ r = cg_controller_from_attr(name, &controller);
+ if (r < 0)
+ return r;
+
+ /* First attempt, read the value from the kernel */
+ b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
+ if (b) {
+ _cleanup_free_ char *p = NULL, *v = NULL;
- r = cg_controller_from_attr(*name, &controller);
+ r = cg_get_path(b->controller, b->path, name, &p);
if (r < 0)
return r;
- /* First attempt, read the value from the kernel */
- b = cgroup_bonding_find_list(u->cgroup_bondings, controller);
- if (b) {
- _cleanup_free_ char *p = NULL, *v = NULL;
+ r = read_full_file(p, &v, NULL);
+ if (r >= 0) {
+ /* Split on new lines */
+ l = strv_split_newlines(v);
+ if (!l)
+ return -ENOMEM;
- r = cg_get_path(b->controller, b->path, *name, &p);
- if (r < 0)
- return r;
+ *_result = l;
+ return 0;
- r = read_full_file(p, &v, NULL);
- if (r >= 0) {
- r = strv_extend(&result, v);
- if (r < 0)
- return r;
-
- continue;
- } else if (r != -ENOENT)
- return r;
}
+ }
- /* If that didn't work, read our cached value */
- a = cgroup_attribute_find_list(u->cgroup_attributes, NULL, *name);
- if (a) {
- r = strv_extend(&result, a->value);
- if (r < 0)
- return r;
+ /* If that didn't work, read our cached value */
+ LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
+ if (!cgroup_attribute_matches(a, controller, name))
continue;
- }
- return -ENOENT;
+ r = strv_extend(&l, a->value);
+ if (r < 0) {
+ strv_free(l);
+ return r;
+ }
}
- *_result = result;
- result = NULL;
+ if (!l)
+ return -ENOENT;
+ *_result = l;
return 0;
}
+static int update_attribute_drop_in(Unit *u, bool runtime, const char *name) {
+ _cleanup_free_ char *buf = NULL;
+ CGroupAttribute *a;
+
+ assert(u);
+ assert(name);
+
+ LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
+ if (!cgroup_attribute_matches(a, NULL, name))
+ continue;
+
+ if (!buf) {
+ buf = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
+ "ControlGroupAttribute=", a->name, " ", a->value, "\n", NULL);
+
+ if (!buf)
+ return -ENOMEM;
+ } else {
+ char *b;
+
+ b = strjoin(buf,
+ "ControlGroupAttribute=", a->name, " ", a->value, "\n", NULL);
+
+ if (!b)
+ return -ENOMEM;
+
+ free(buf);
+ buf = b;
+ }
+ }
+
+ if (buf)
+ return unit_write_drop_in(u, runtime, name, buf);
+ else
+ return unit_remove_drop_in(u, runtime, name);
+}
+
int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter) {
_cleanup_strv_free_ char **l = NULL;
int r;
bool runtime = false;
- char **name, **value;
+ char **value;
+ const char *name;
assert(u);
assert(iter);
@@ -1108,19 +1166,34 @@ int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter) {
if (!unit_get_exec_context(u))
return -EINVAL;
- r = bus_parse_strv_pairs_iter(iter, &l);
+ r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
+ if (r < 0)
+ return r;
+
+ r = bus_parse_strv_iter(iter, &l);
if (r < 0)
return r;
- r = next_and_parse_mode(iter, &runtime);
+ if (!dbus_message_iter_next(iter))
+ return -EINVAL;
+
+ r = parse_mode(iter, &runtime, false);
if (r < 0)
return r;
- STRV_FOREACH_PAIR(name, value, l) {
- _cleanup_free_ char *contents = NULL;
+ STRV_FOREACH(value, l) {
+ _cleanup_free_ char *v = NULL;
CGroupAttribute *a;
+ const CGroupSemantics *s;
+
+ r = cgroup_semantics_find(NULL, name, *value, &v, &s);
+ if (r < 0)
+ return r;
+
+ if (s && !s->multiple && l[1])
+ return -EINVAL;
- r = unit_add_cgroup_attribute(u, NULL, *name, *value, NULL, &a);
+ r = unit_add_cgroup_attribute(u, s, NULL, name, v ? v : *value, &a);
if (r < 0)
return r;
@@ -1144,22 +1217,17 @@ int bus_unit_cgroup_attribute_set(Unit *u, DBusMessageIter *iter) {
cgroup_attribute_apply(a, u->cgroup_bondings);
}
- contents = strjoin("[", UNIT_VTABLE(u)->exec_section, "]\n"
- "ControlGroupAttribute=", *name, " ", *value, "\n", NULL);
- if (!contents)
- return -ENOMEM;
-
- r = unit_write_drop_in(u, runtime, *name, contents);
- if (r < 0)
- return r;
}
+ r = update_attribute_drop_in(u, runtime, name);
+ if (r < 0)
+ return r;
+
return 0;
}
int bus_unit_cgroup_attribute_unset(Unit *u, DBusMessageIter *iter) {
- _cleanup_strv_free_ char **l = NULL;
- char **name;
+ const char *name;
bool runtime;
int r;
@@ -1169,23 +1237,16 @@ int bus_unit_cgroup_attribute_unset(Unit *u, DBusMessageIter *iter) {
if (!unit_get_exec_context(u))
return -EINVAL;
- r = bus_parse_strv_iter(iter, &l);
+ r = bus_iter_get_basic_and_next(iter, DBUS_TYPE_STRING, &name, true);
if (r < 0)
return r;
- r = next_and_parse_mode(iter, &runtime);
+ r = parse_mode(iter, &runtime, false);
if (r < 0)
return r;
- STRV_FOREACH(name, l) {
- CGroupAttribute *a;
-
- a = cgroup_attribute_find_list(u->cgroup_attributes, NULL, *name);
- if (a)
- cgroup_attribute_free(a);
-
- unit_remove_drop_in(u, runtime, *name);
- }
+ cgroup_attribute_free_some(u->cgroup_attributes, NULL, name);
+ update_attribute_drop_in(u, runtime, name);
return 0;
}
diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h
index c8903f8e5e..34980b046e 100644
--- a/src/core/dbus-unit.h
+++ b/src/core/dbus-unit.h
@@ -127,24 +127,25 @@
" <property name=\"DefaultControlGroup\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"ControlGroups\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"ControlGroupAttributes\" type=\"a(sss)\" access=\"read\"/>\n" \
- " <method name=\"GetControlGroupAttributes\">\n" \
- " <arg name=\"attributes\" type=\"as\" direction=\"in\"/>\n" \
- " <arg name=\"values\" type=\"as\" direction=\"out\"/>\n" \
- " </method>\n" \
- " <method name=\"SetControlGroupAttributes\">\n" \
- " <arg name=\"attributes\" type=\"a(ss)\" direction=\"in\"/>\n" \
+ " <method name=\"SetControlGroup\">\n" \
+ " <arg name=\"group\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
- " <method name=\"UnsetControlGroupAttributes\">\n" \
- " <arg name=\"attributes\" type=\"as\" direction=\"in\"/>\n" \
+ " <method name=\"UnsetControlGroup\">\n" \
+ " <arg name=\"group\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
- " <method name=\"SetControlGroups\">\n" \
- " <arg name=\"groups\" type=\"as\" direction=\"in\"/>\n" \
+ " <method name=\"GetControlGroupAttribute\">\n" \
+ " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"values\" type=\"as\" direction=\"out\"/>\n" \
+ " </method>\n" \
+ " <method name=\"SetControlGroupAttribute\">\n" \
+ " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
+ " <arg name=\"values\" type=\"as\" direction=\"in\"/>\n" \
" <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
- " <method name=\"UnsetControlGroups\">\n" \
- " <arg name=\"groups\" type=\"as\" direction=\"in\"/>\n" \
+ " <method name=\"UnsetControlGroupAttribute\">\n" \
+ " <arg name=\"attribute\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"mode\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n"
diff --git a/src/core/execute.c b/src/core/execute.c
index b28962a3c3..92cf174641 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -165,6 +165,14 @@ void exec_context_tty_reset(const ExecContext *context) {
vt_disallocate(context->tty_path);
}
+static bool is_terminal_output(ExecOutput o) {
+ return
+ o == EXEC_OUTPUT_TTY ||
+ o == EXEC_OUTPUT_SYSLOG_AND_CONSOLE ||
+ o == EXEC_OUTPUT_KMSG_AND_CONSOLE ||
+ o == EXEC_OUTPUT_JOURNAL_AND_CONSOLE;
+}
+
static int open_null_as(int flags, int nfd) {
int fd, r;
@@ -224,7 +232,7 @@ static int connect_logger_as(const ExecContext *context, ExecOutput output, cons
!!context->syslog_level_prefix,
output == EXEC_OUTPUT_SYSLOG || output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE,
output == EXEC_OUTPUT_KMSG || output == EXEC_OUTPUT_KMSG_AND_CONSOLE,
- output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || output == EXEC_OUTPUT_KMSG_AND_CONSOLE || output == EXEC_OUTPUT_JOURNAL_AND_CONSOLE);
+ is_terminal_output(output));
if (fd != nfd) {
r = dup2(fd, nfd) < 0 ? -errno : nfd;
@@ -1710,6 +1718,37 @@ int exec_context_load_environment(const ExecContext *c, char ***l) {
return 0;
}
+static bool tty_may_match_dev_console(const char *tty) {
+ char *active = NULL, *console;
+ bool b;
+
+ if (startswith(tty, "/dev/"))
+ tty += 5;
+
+ /* trivial identity? */
+ if (streq(tty, "console"))
+ return true;
+
+ console = resolve_dev_console(&active);
+ /* if we could not resolve, assume it may */
+ if (!console)
+ return true;
+
+ /* "tty0" means the active VC, so it may be the same sometimes */
+ b = streq(console, tty) || (streq(console, "tty0") && tty_is_vc(tty));
+ free(active);
+
+ return b;
+}
+
+bool exec_context_may_touch_console(ExecContext *ec) {
+ return (ec->tty_reset || ec->tty_vhangup || ec->tty_vt_disallocate ||
+ is_terminal_input(ec->std_input) ||
+ is_terminal_output(ec->std_output) ||
+ is_terminal_output(ec->std_error)) &&
+ tty_may_match_dev_console(tty_path(ec));
+}
+
static void strv_fprintf(FILE *f, char **l) {
char **g;
diff --git a/src/core/execute.h b/src/core/execute.h
index 2bcd2e1e6c..001eb0e7cc 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -198,6 +198,8 @@ void exec_context_tty_reset(const ExecContext *context);
int exec_context_load_environment(const ExecContext *c, char ***l);
+bool exec_context_may_touch_console(ExecContext *c);
+
void exec_status_start(ExecStatus *s, pid_t pid);
void exec_status_exit(ExecStatus *s, ExecContext *context, pid_t pid, int code, int status);
void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix);
diff --git a/src/core/job.c b/src/core/job.c
index 990607f990..90de550016 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -206,6 +206,7 @@ Job* job_install(Job *j) {
"Merged into running job, re-running: %s/%s as %u",
uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id);
uj->state = JOB_WAITING;
+ uj->manager->n_running_jobs--;
return uj;
}
}
@@ -483,7 +484,7 @@ static void job_change_type(Job *j, JobType newtype) {
int job_run_and_invalidate(Job *j) {
int r;
uint32_t id;
- Manager *m;
+ Manager *m = j->manager;
assert(j);
assert(j->installed);
@@ -500,6 +501,7 @@ int job_run_and_invalidate(Job *j) {
return -EAGAIN;
j->state = JOB_RUNNING;
+ m->n_running_jobs++;
job_add_to_dbus_queue(j);
/* While we execute this operation the job might go away (for
@@ -508,7 +510,6 @@ int job_run_and_invalidate(Job *j) {
* store the id here, so that we can verify the job is still
* valid. */
id = j->id;
- m = j->manager;
switch (j->type) {
@@ -558,9 +559,10 @@ int job_run_and_invalidate(Job *j) {
r = job_finish_and_invalidate(j, JOB_DONE, true);
else if (r == -ENOEXEC)
r = job_finish_and_invalidate(j, JOB_SKIPPED, true);
- else if (r == -EAGAIN)
+ else if (r == -EAGAIN) {
j->state = JOB_WAITING;
- else if (r < 0)
+ m->n_running_jobs--;
+ } else if (r < 0)
r = job_finish_and_invalidate(j, JOB_FAILED, true);
}
@@ -642,20 +644,20 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
case JOB_DONE:
if (u->condition_result)
- unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, format, unit_description(u));
+ unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, format);
break;
case JOB_FAILED:
- unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, format, unit_description(u));
- unit_status_printf(u, NULL, "See 'systemctl status %s' for details.", u->id);
+ unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, format);
+ manager_status_printf(u->manager, false, NULL, "See 'systemctl status %s' for details.", u->id);
break;
case JOB_DEPENDENCY:
- unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "DEPEND" ANSI_HIGHLIGHT_OFF, format, unit_description(u));
+ unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "DEPEND" ANSI_HIGHLIGHT_OFF, format);
break;
case JOB_TIMEOUT:
- unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format, unit_description(u));
+ unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format);
break;
default:
@@ -671,12 +673,12 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
switch (result) {
case JOB_TIMEOUT:
- unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format, unit_description(u));
+ unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format);
break;
case JOB_DONE:
case JOB_FAILED:
- unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, format, unit_description(u));
+ unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, format);
break;
default:
@@ -689,7 +691,7 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) {
* Most likely a DEPEND warning from a requisiting unit will
* occur next and it's nice to see what was requisited. */
if (result == JOB_SKIPPED)
- unit_status_printf(u, ANSI_HIGHLIGHT_ON " INFO " ANSI_HIGHLIGHT_OFF, "%s is not active.", unit_description(u));
+ unit_status_printf(u, ANSI_HIGHLIGHT_ON " INFO " ANSI_HIGHLIGHT_OFF, "%s is not active.");
}
}
@@ -760,6 +762,9 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) {
j->result = result;
+ if (j->state == JOB_RUNNING)
+ j->manager->n_running_jobs--;
+
log_debug_unit(u->id, "Job %s/%s finished, result=%s",
u->id, job_type_to_string(t), job_result_to_string(result));
diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4
index 0b6a5cc659..22c1761dc2 100644
--- a/src/core/load-fragment-gperf.gperf.m4
+++ b/src/core/load-fragment-gperf.gperf.m4
@@ -68,14 +68,14 @@ $1.LimitRTPRIO, config_parse_limit, RLIMIT_RTPR
$1.LimitRTTIME, config_parse_limit, RLIMIT_RTTIME, offsetof($1, exec_context.rlimit)
$1.ControlGroup, config_parse_unit_cgroup, 0, 0
$1.ControlGroupAttribute, config_parse_unit_cgroup_attr, 0, 0
-$1.CPUShares, config_parse_unit_cpu_shares, 0, 0
-$1.MemoryLimit, config_parse_unit_memory_limit, 0, 0
-$1.MemorySoftLimit, config_parse_unit_memory_limit, 0, 0
-$1.DeviceAllow, config_parse_unit_device_allow, 0, 0
-$1.DeviceDeny, config_parse_unit_device_allow, 0, 0
-$1.BlockIOWeight, config_parse_unit_blkio_weight, 0, 0
-$1.BlockIOReadBandwidth, config_parse_unit_blkio_bandwidth, 0, 0
-$1.BlockIOWriteBandwidth, config_parse_unit_blkio_bandwidth, 0, 0
+$1.CPUShares, config_parse_unit_cgroup_attr_pretty, 0, 0
+$1.MemoryLimit, config_parse_unit_cgroup_attr_pretty, 0, 0
+$1.MemorySoftLimit, config_parse_unit_cgroup_attr_pretty, 0, 0
+$1.DeviceAllow, config_parse_unit_cgroup_attr_pretty, 0, 0
+$1.DeviceDeny, config_parse_unit_cgroup_attr_pretty, 0, 0
+$1.BlockIOWeight, config_parse_unit_cgroup_attr_pretty, 0, 0
+$1.BlockIOReadBandwidth, config_parse_unit_cgroup_attr_pretty, 0, 0
+$1.BlockIOWriteBandwidth, config_parse_unit_cgroup_attr_pretty, 0, 0
$1.ReadWriteDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.read_write_dirs)
$1.ReadOnlyDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.read_only_dirs)
$1.InaccessibleDirectories, config_parse_path_strv, 0, offsetof($1, exec_context.inaccessible_dirs)
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 6e333aaf15..d79e1d936c 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -1769,7 +1769,9 @@ int config_parse_unit_cgroup_attr(
void *userdata) {
Unit *u = data;
- _cleanup_strv_free_ char **l = NULL;
+ size_t a, b;
+ _cleanup_free_ char *n = NULL, *v = NULL;
+ const CGroupSemantics *s;
int r;
assert(filename);
@@ -1784,160 +1786,24 @@ int config_parse_unit_cgroup_attr(
return 0;
}
- l = strv_split_quoted(rvalue);
- if (!l)
- return log_oom();
-
- if (strv_length(l) != 2) {
+ a = strcspn(rvalue, WHITESPACE);
+ b = strspn(rvalue + a, WHITESPACE);
+ if (a <= 0 || b <= 0) {
log_error("[%s:%u] Failed to parse cgroup attribute value, ignoring: %s", filename, line, rvalue);
return 0;
}
- r = unit_add_cgroup_attribute(u, NULL, l[0], l[1], NULL, NULL);
- if (r < 0) {
- log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- return 0;
-}
-
-int config_parse_unit_cpu_shares(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
- Unit *u = data;
- int r;
- unsigned long ul;
- _cleanup_free_ char *t = NULL;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if (safe_atolu(rvalue, &ul) < 0 || ul < 1) {
- log_error("[%s:%u] Failed to parse CPU shares value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- if (asprintf(&t, "%lu", ul) < 0)
- return log_oom();
-
- r = unit_add_cgroup_attribute(u, "cpu", "cpu.shares", t, NULL, NULL);
- if (r < 0) {
- log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- return 0;
-}
-
-int config_parse_unit_memory_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
- Unit *u = data;
- int r;
- off_t sz;
- _cleanup_free_ char *t = NULL;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if (parse_bytes(rvalue, &sz) < 0 || sz <= 0) {
- log_error("[%s:%u] Failed to parse memory limit value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- if (asprintf(&t, "%llu", (unsigned long long) sz) < 0)
+ n = strndup(rvalue, a);
+ if (!n)
return log_oom();
- r = unit_add_cgroup_attribute(u,
- "memory",
- streq(lvalue, "MemorySoftLimit") ? "memory.soft_limit_in_bytes" : "memory.limit_in_bytes",
- t, NULL, NULL);
+ r = cgroup_semantics_find(NULL, n, rvalue + a + b, &v, &s);
if (r < 0) {
- log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- return 0;
-}
-
-static int device_map(const char *controller, const char *name, const char *value, char **ret) {
- _cleanup_strv_free_ char **l = NULL;
-
- assert(controller);
- assert(name);
- assert(value);
- assert(ret);
-
- l = strv_split_quoted(value);
- if (!l)
- return -ENOMEM;
-
- assert(strv_length(l) >= 1);
-
- if (streq(l[0], "*")) {
-
- if (asprintf(ret, "a *:*%s%s",
- isempty(l[1]) ? "" : " ", strempty(l[1])) < 0)
- return -ENOMEM;
- } else {
- struct stat st;
-
- if (stat(l[0], &st) < 0) {
- log_warning("Couldn't stat device %s", l[0]);
- return -errno;
- }
-
- if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
- log_warning("%s is not a device.", l[0]);
- return -ENODEV;
- }
-
- if (asprintf(ret, "%c %u:%u%s%s",
- S_ISCHR(st.st_mode) ? 'c' : 'b',
- major(st.st_rdev), minor(st.st_rdev),
- isempty(l[1]) ? "" : " ", strempty(l[1])) < 0)
- return -ENOMEM;
- }
-
- return 0;
-}
-
-int config_parse_unit_device_allow(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
- Unit *u = data;
- _cleanup_strv_free_ char **l = NULL;
- int r;
- unsigned k;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- l = strv_split_quoted(rvalue);
- if (!l)
- return log_oom();
-
- k = strv_length(l);
- if (k < 1 || k > 2) {
- log_error("[%s:%u] Failed to parse device value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- if (!streq(l[0], "*") && !path_startswith(l[0], "/dev")) {
- log_error("[%s:%u] Device node path not absolute, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- if (!isempty(l[1]) && !in_charset(l[1], "rwm")) {
- log_error("[%s:%u] Device access string invalid, ignoring: %s", filename, line, rvalue);
+ log_error("[%s:%u] Failed to parse cgroup attribute value, ignoring: %s", filename, line, rvalue);
return 0;
}
- r = unit_add_cgroup_attribute(u, "devices",
- streq(lvalue, "DeviceAllow") ? "devices.allow" : "devices.deny",
- rvalue, device_map, NULL);
-
+ r = unit_add_cgroup_attribute(u, s, NULL, n, v ? v : rvalue + a + b, NULL);
if (r < 0) {
log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
return 0;
@@ -1946,148 +1812,36 @@ int config_parse_unit_device_allow(const char *filename, unsigned line, const ch
return 0;
}
-static int blkio_map(const char *controller, const char *name, const char *value, char **ret) {
- struct stat st;
- _cleanup_strv_free_ char **l = NULL;
- dev_t d;
-
- assert(controller);
- assert(name);
- assert(value);
- assert(ret);
-
- l = strv_split_quoted(value);
- if (!l)
- return log_oom();
-
- assert(strv_length(l) == 2);
-
- if (stat(l[0], &st) < 0) {
- log_warning("Couldn't stat device %s", l[0]);
- return -errno;
- }
-
- if (S_ISBLK(st.st_mode))
- d = st.st_rdev;
- else if (major(st.st_dev) != 0) {
- /* If this is not a device node then find the block
- * device this file is stored on */
- d = st.st_dev;
-
- /* If this is a partition, try to get the originating
- * block device */
- block_get_whole_disk(d, &d);
- } else {
- log_warning("%s is not a block device and file system block device cannot be determined or is not local.", l[0]);
- return -ENODEV;
- }
-
- if (asprintf(ret, "%u:%u %s", major(d), minor(d), l[1]) < 0)
- return -ENOMEM;
-
- return 0;
-}
+int config_parse_unit_cgroup_attr_pretty(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
-int config_parse_unit_blkio_weight(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
Unit *u = data;
+ _cleanup_free_ char *v = NULL;
+ const CGroupSemantics *s;
int r;
- unsigned long ul;
- const char *device = NULL, *weight;
- unsigned k;
- _cleanup_free_ char *t = NULL;
- _cleanup_strv_free_ char **l = NULL;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
- l = strv_split_quoted(rvalue);
- if (!l)
- return log_oom();
-
- k = strv_length(l);
- if (k < 1 || k > 2) {
- log_error("[%s:%u] Failed to parse weight value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- if (k == 1)
- weight = l[0];
- else {
- device = l[0];
- weight = l[1];
- }
-
- if (device && !path_is_absolute(device)) {
- log_error("[%s:%u] Failed to parse block device node value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- if (safe_atolu(weight, &ul) < 0 || ul < 10 || ul > 1000) {
- log_error("[%s:%u] Failed to parse block IO weight value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- if (device)
- r = asprintf(&t, "%s %lu", device, ul);
- else
- r = asprintf(&t, "%lu", ul);
- if (r < 0)
- return log_oom();
-
- if (device)
- r = unit_add_cgroup_attribute(u, "blkio", "blkio.weight_device", t, blkio_map, NULL);
- else
- r = unit_add_cgroup_attribute(u, "blkio", "blkio.weight", t, NULL, NULL);
+ r = cgroup_semantics_find(NULL, lvalue, rvalue, &v, &s);
if (r < 0) {
- log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- return 0;
-}
-
-int config_parse_unit_blkio_bandwidth(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
- Unit *u = data;
- int r;
- off_t bytes;
- unsigned k;
- _cleanup_free_ char *t = NULL;
- _cleanup_strv_free_ char **l = NULL;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- l = strv_split_quoted(rvalue);
- if (!l)
- return log_oom();
-
- k = strv_length(l);
- if (k != 2) {
- log_error("[%s:%u] Failed to parse bandwidth value, ignoring: %s", filename, line, rvalue);
+ log_error("[%s:%u] Failed to parse cgroup attribute value, ignoring: %s", filename, line, rvalue);
return 0;
- }
-
- if (!path_is_absolute(l[0])) {
- log_error("[%s:%u] Failed to parse block device node value, ignoring: %s", filename, line, rvalue);
+ } else if (r == 0) {
+ log_error("[%s:%u] Unknown or unsupported cgroup attribute %s, ignoring: %s", filename, line, lvalue, rvalue);
return 0;
}
- if (parse_bytes(l[1], &bytes) < 0 || bytes <= 0) {
- log_error("[%s:%u] Failed to parse block IO bandwidth value, ignoring: %s", filename, line, rvalue);
- return 0;
- }
-
- r = asprintf(&t, "%s %llu", l[0], (unsigned long long) bytes);
- if (r < 0)
- return log_oom();
-
- r = unit_add_cgroup_attribute(u, "blkio",
- streq(lvalue, "BlockIOReadBandwidth") ? "blkio.read_bps_device" : "blkio.write_bps_device",
- t, blkio_map, NULL);
+ r = unit_add_cgroup_attribute(u, s, NULL, NULL, v, NULL);
if (r < 0) {
log_error("[%s:%u] Failed to add cgroup attribute value, ignoring: %s", filename, line, rvalue);
return 0;
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index 421b4c33ec..dfb2ef0c3b 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -75,11 +75,7 @@ int config_parse_kill_mode(const char *filename, unsigned line, const char *sect
int config_parse_notify_access(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_start_limit_action(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_unit_cgroup_attr(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_cpu_shares(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_memory_limit(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_device_allow(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_blkio_weight(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
-int config_parse_unit_blkio_bandwidth(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
+int config_parse_unit_cgroup_attr_pretty(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_unit_requires_mounts_for(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_syscall_filter(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_environ(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
diff --git a/src/core/manager.c b/src/core/manager.c
index a578813617..ec12a75371 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -79,6 +79,11 @@
/* As soon as 5s passed since a unit was added to our GC queue, make sure to run a gc sweep */
#define GC_QUEUE_USEC_MAX (10*USEC_PER_SEC)
+/* Initial delay and the interval for printing status messages about running jobs */
+#define JOBS_IN_PROGRESS_WAIT_SEC 5
+#define JOBS_IN_PROGRESS_PERIOD_SEC 1
+#define JOBS_IN_PROGRESS_PERIOD_DIVISOR 3
+
/* Where clients shall send notification messages to */
#define NOTIFY_SOCKET "@/org/freedesktop/systemd1/notify"
@@ -140,6 +145,146 @@ static int manager_setup_notify(Manager *m) {
return 0;
}
+static int manager_jobs_in_progress_mod_timer(Manager *m) {
+ struct itimerspec its;
+
+ zero(its);
+
+ its.it_value.tv_sec = JOBS_IN_PROGRESS_WAIT_SEC;
+ its.it_interval.tv_sec = JOBS_IN_PROGRESS_PERIOD_SEC;
+
+ if (timerfd_settime(m->jobs_in_progress_watch.fd, 0, &its, NULL) < 0)
+ return -errno;
+
+ return 0;
+}
+
+static int manager_watch_jobs_in_progress(Manager *m) {
+ struct epoll_event ev;
+ int r;
+
+ assert(m);
+
+ if (m->jobs_in_progress_watch.type != WATCH_INVALID)
+ return 0;
+
+ m->jobs_in_progress_watch.type = WATCH_JOBS_IN_PROGRESS;
+ m->jobs_in_progress_watch.fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC);
+ if (m->jobs_in_progress_watch.fd < 0) {
+ log_error("Failed to create timerfd: %m");
+ r = -errno;
+ goto err;
+ }
+
+ r = manager_jobs_in_progress_mod_timer(m);
+ if (r < 0) {
+ log_error("Failed to set up timer for jobs progress watch: %s", strerror(-r));
+ goto err;
+ }
+
+ zero(ev);
+ ev.events = EPOLLIN;
+ ev.data.ptr = &m->jobs_in_progress_watch;
+
+ if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->jobs_in_progress_watch.fd, &ev) < 0) {
+ log_error("Failed to add jobs progress timer fd to epoll: %m");
+ r = -errno;
+ goto err;
+ }
+
+ log_debug("Set up jobs progress timerfd.");
+
+ return 0;
+
+err:
+ if (m->jobs_in_progress_watch.fd >= 0)
+ close_nointr_nofail(m->jobs_in_progress_watch.fd);
+ watch_init(&m->jobs_in_progress_watch);
+ return r;
+}
+
+static void manager_unwatch_jobs_in_progress(Manager *m) {
+ if (m->jobs_in_progress_watch.type != WATCH_JOBS_IN_PROGRESS)
+ return;
+
+ assert_se(epoll_ctl(m->epoll_fd, EPOLL_CTL_DEL, m->jobs_in_progress_watch.fd, NULL) >= 0);
+ close_nointr_nofail(m->jobs_in_progress_watch.fd);
+ watch_init(&m->jobs_in_progress_watch);
+ m->jobs_in_progress_iteration = 0;
+
+ log_debug("Closed jobs progress timerfd.");
+}
+
+#define CYLON_BUFFER_EXTRA (2*strlen(ANSI_RED_ON) + strlen(ANSI_HIGHLIGHT_RED_ON) + 2*strlen(ANSI_HIGHLIGHT_OFF))
+static void draw_cylon(char buffer[], size_t buflen, unsigned width, unsigned pos) {
+ char *p = buffer;
+
+ assert(buflen >= CYLON_BUFFER_EXTRA + width + 1);
+ assert(pos <= width+1); /* 0 or width+1 mean that the center light is behind the corner */
+
+ if (pos > 1) {
+ if (pos > 2) {
+ memset(p, ' ', pos-2);
+ p += pos-2;
+ }
+ memcpy(p, ANSI_RED_ON, strlen(ANSI_RED_ON));
+ p += strlen(ANSI_RED_ON);
+ *p++ = '*';
+ }
+
+ if (pos > 0 && pos <= width) {
+ memcpy(p, ANSI_HIGHLIGHT_RED_ON, strlen(ANSI_HIGHLIGHT_RED_ON));
+ p += strlen(ANSI_HIGHLIGHT_RED_ON);
+ *p++ = '*';
+ }
+
+ memcpy(p, ANSI_HIGHLIGHT_OFF, strlen(ANSI_HIGHLIGHT_OFF));
+ p += strlen(ANSI_HIGHLIGHT_OFF);
+
+ if (pos < width) {
+ memcpy(p, ANSI_RED_ON, strlen(ANSI_RED_ON));
+ p += strlen(ANSI_RED_ON);
+ *p++ = '*';
+ if (pos < width-1) {
+ memset(p, ' ', width-1-pos);
+ p += width-1-pos;
+ }
+ memcpy(p, ANSI_HIGHLIGHT_OFF, strlen(ANSI_HIGHLIGHT_OFF));
+ p += strlen(ANSI_HIGHLIGHT_OFF);
+ }
+ *p = 0;
+}
+
+static void manager_print_jobs_in_progress(Manager *m) {
+ Iterator i;
+ Job *j;
+ char *job_of_n = NULL;
+ unsigned counter = 0, print_nr;
+ char cylon[6 + CYLON_BUFFER_EXTRA + 1];
+ unsigned cylon_pos;
+
+ print_nr = (m->jobs_in_progress_iteration / JOBS_IN_PROGRESS_PERIOD_DIVISOR) % m->n_running_jobs;
+
+ HASHMAP_FOREACH(j, m->jobs, i)
+ if (j->state == JOB_RUNNING && counter++ == print_nr)
+ break;
+
+ cylon_pos = m->jobs_in_progress_iteration % 14;
+ if (cylon_pos >= 8)
+ cylon_pos = 14 - cylon_pos;
+ draw_cylon(cylon, sizeof(cylon), 6, cylon_pos);
+
+ if (m->n_running_jobs > 1)
+ if (asprintf(&job_of_n, "(%u of %u) ", counter, m->n_running_jobs) < 0)
+ job_of_n = NULL;
+
+ manager_status_printf(m, true, cylon, "%sA %s job is running for %s",
+ strempty(job_of_n), job_type_to_string(j->type), unit_description(j->unit));
+ free(job_of_n);
+
+ m->jobs_in_progress_iteration++;
+}
+
static int manager_setup_time_change(Manager *m) {
struct epoll_event ev;
struct itimerspec its;
@@ -324,6 +469,7 @@ int manager_new(SystemdRunningAs running_as, Manager **_m) {
watch_init(&m->swap_watch);
watch_init(&m->udev_watch);
watch_init(&m->time_change_watch);
+ watch_init(&m->jobs_in_progress_watch);
m->epoll_fd = m->dev_autofs_fd = -1;
m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
@@ -564,6 +710,8 @@ void manager_free(Manager *m) {
close_nointr_nofail(m->notify_watch.fd);
if (m->time_change_watch.fd >= 0)
close_nointr_nofail(m->time_change_watch.fd);
+ if (m->jobs_in_progress_watch.fd >= 0)
+ close_nointr_nofail(m->jobs_in_progress_watch.fd);
free(m->notify_socket);
@@ -988,6 +1136,10 @@ unsigned manager_dispatch_run_queue(Manager *m) {
}
m->dispatching_run_queue = false;
+
+ if (hashmap_size(m->jobs) > 0)
+ manager_watch_jobs_in_progress(m);
+
return n;
}
@@ -1527,6 +1679,16 @@ static int process_event(Manager *m, struct epoll_event *ev) {
break;
}
+ case WATCH_JOBS_IN_PROGRESS: {
+ uint64_t v;
+
+ /* not interested in the data */
+ read(w->fd, &v, sizeof(v));
+
+ manager_print_jobs_in_progress(m);
+ break;
+ }
+
default:
log_error("event type=%i", w->type);
assert_not_reached("Unknown epoll event type.");
@@ -2152,7 +2314,7 @@ finish:
return r;
}
-bool manager_is_booting_or_shutting_down(Manager *m) {
+static bool manager_is_booting_or_shutting_down(Manager *m) {
Unit *u;
assert(m);
@@ -2199,8 +2361,10 @@ void manager_check_finished(Manager *m) {
assert(m);
- if (hashmap_size(m->jobs) > 0)
+ if (hashmap_size(m->jobs) > 0) {
+ manager_jobs_in_progress_mod_timer(m);
return;
+ }
/* Notify Type=idle units that we are done now */
close_pipe(m->idle_pipe);
@@ -2208,6 +2372,8 @@ void manager_check_finished(Manager *m) {
/* Turn off confirm spawn now */
m->confirm_spawn = false;
+ manager_unwatch_jobs_in_progress(m);
+
if (dual_timestamp_is_set(&m->finish_timestamp))
return;
@@ -2479,7 +2645,7 @@ void manager_set_show_status(Manager *m, bool b) {
unlink("/run/systemd/show-status");
}
-bool manager_get_show_status(Manager *m) {
+static bool manager_get_show_status(Manager *m) {
assert(m);
if (m->running_as != SYSTEMD_SYSTEM)
@@ -2494,6 +2660,25 @@ bool manager_get_show_status(Manager *m) {
return plymouth_running();
}
+void manager_status_printf(Manager *m, bool ephemeral, const char *status, const char *format, ...) {
+ va_list ap;
+
+ if (!manager_get_show_status(m))
+ return;
+
+ /* XXX We should totally drop the check for ephemeral here
+ * and thus effectively make 'Type=idle' pointless. */
+ if (ephemeral && m->n_on_console > 0)
+ return;
+
+ if (!manager_is_booting_or_shutting_down(m))
+ return;
+
+ va_start(ap, format);
+ status_vprintf(status, true, ephemeral, format, ap);
+ va_end(ap);
+}
+
void watch_init(Watch *w) {
assert(w);
diff --git a/src/core/manager.h b/src/core/manager.h
index cc4edf8f1e..c486a16887 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -61,7 +61,8 @@ enum WatchType {
WATCH_UDEV,
WATCH_DBUS_WATCH,
WATCH_DBUS_TIMEOUT,
- WATCH_TIME_CHANGE
+ WATCH_TIME_CHANGE,
+ WATCH_JOBS_IN_PROGRESS
};
struct Watch {
@@ -127,6 +128,7 @@ struct Manager {
Watch notify_watch;
Watch signal_watch;
Watch time_change_watch;
+ Watch jobs_in_progress_watch;
int epoll_fd;
@@ -225,6 +227,11 @@ struct Manager {
unsigned n_installed_jobs;
unsigned n_failed_jobs;
+ /* Jobs in progress watching */
+ unsigned n_running_jobs;
+ unsigned n_on_console;
+ unsigned jobs_in_progress_iteration;
+
/* Type=idle pipes */
int idle_pipe[2];
@@ -276,8 +283,6 @@ int manager_distribute_fds(Manager *m, FDSet *fds);
int manager_reload(Manager *m);
-bool manager_is_booting_or_shutting_down(Manager *m);
-
void manager_reset_failed(Manager *m);
void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success);
@@ -293,6 +298,6 @@ void manager_undo_generators(Manager *m);
void manager_recheck_journal(Manager *m);
void manager_set_show_status(Manager *m, bool b);
-bool manager_get_show_status(Manager *m);
+void manager_status_printf(Manager *m, bool ephemeral, const char *status, const char *format, ...);
void watch_init(Watch *w);
diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c
index e7e2736615..dab3601467 100644
--- a/src/core/mount-setup.c
+++ b/src/core/mount-setup.c
@@ -77,10 +77,6 @@ static const MountPoint mount_table[] = {
NULL, MNT_FATAL|MNT_IN_CONTAINER },
{ "securityfs", "/sys/kernel/security", "securityfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
NULL, MNT_NONE },
-#ifdef ENABLE_EFI
- { "efivarfs", "/sys/firmware/efi/efivars", "efivarfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
- is_efi_boot, MNT_NONE },
-#endif
{ "tmpfs", "/dev/shm", "tmpfs", "mode=1777", MS_NOSUID|MS_NODEV|MS_STRICTATIME,
NULL, MNT_FATAL|MNT_IN_CONTAINER },
{ "devpts", "/dev/pts", "devpts", "mode=620,gid=" STRINGIFY(TTY_GID), MS_NOSUID|MS_NOEXEC,
@@ -91,6 +87,12 @@ static const MountPoint mount_table[] = {
NULL, MNT_IN_CONTAINER },
{ "cgroup", "/sys/fs/cgroup/systemd", "cgroup", "none,name=systemd", MS_NOSUID|MS_NOEXEC|MS_NODEV,
NULL, MNT_IN_CONTAINER },
+ { "pstore", "/sys/fs/pstore", "pstore", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ NULL, MNT_NONE },
+#ifdef ENABLE_EFI
+ { "efivarfs", "/sys/firmware/efi/efivars", "efivarfs", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV,
+ is_efi_boot, MNT_NONE },
+#endif
};
/* These are API file systems that might be mounted by other software,
diff --git a/src/core/transaction.c b/src/core/transaction.c
index 0329366350..4a8d90e6e5 100644
--- a/src/core/transaction.c
+++ b/src/core/transaction.c
@@ -396,8 +396,8 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
"Job %s/%s deleted to break ordering cycle starting with %s/%s",
delete->unit->id, job_type_to_string(delete->type),
j->unit->id, job_type_to_string(j->type));
- status_printf(ANSI_HIGHLIGHT_RED_ON " SKIP " ANSI_HIGHLIGHT_OFF, true,
- "Ordering cycle found, skipping %s", unit_description(delete->unit));
+ unit_status_printf(delete->unit, ANSI_HIGHLIGHT_RED_ON " SKIP " ANSI_HIGHLIGHT_OFF,
+ "Ordering cycle found, skipping %s");
transaction_delete_unit(tr, delete->unit);
return -EAGAIN;
}
diff --git a/src/core/unit.c b/src/core/unit.c
index 3a88996eb7..e2c06ae8b6 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -749,15 +749,13 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
prefix, b->controller, b->path);
LIST_FOREACH(by_unit, a, u->cgroup_attributes) {
- char *v = NULL;
+ _cleanup_free_ char *v = NULL;
- if (a->map_callback)
- a->map_callback(a->controller, a->name, a->value, &v);
+ if (a->semantics && a->semantics->map_write)
+ a->semantics->map_write(a->semantics, a->value, &v);
fprintf(f, "%s\tControlGroupAttribute: %s %s \"%s\"\n",
prefix, a->controller, a->name, v ? v : a->value);
-
- free(v);
}
if (UNIT_VTABLE(u)->dump)
@@ -996,7 +994,7 @@ static void unit_status_print_starting_stopping(Unit *u, JobType t) {
if (!format)
return;
- unit_status_printf(u, "", format, unit_description(u));
+ unit_status_printf(u, "", format);
}
#pragma GCC diagnostic push
@@ -1322,6 +1320,7 @@ void unit_trigger_on_failure(Unit *u) {
}
void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_success) {
+ Manager *m;
bool unexpected;
assert(u);
@@ -1334,7 +1333,9 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
* behavior here. For example: if a mount point is remounted
* this function will be called too! */
- if (u->manager->n_reloading <= 0) {
+ m = u->manager;
+
+ if (m->n_reloading <= 0) {
dual_timestamp ts;
dual_timestamp_get(&ts);
@@ -1356,6 +1357,21 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
if (UNIT_IS_INACTIVE_OR_FAILED(ns))
cgroup_bonding_trim_list(u->cgroup_bondings, true);
+ if (UNIT_IS_INACTIVE_OR_FAILED(os) != UNIT_IS_INACTIVE_OR_FAILED(ns)) {
+ ExecContext *ec = unit_get_exec_context(u);
+ if (ec && exec_context_may_touch_console(ec)) {
+ /* XXX The counter may get out of sync if the admin edits
+ * TTY-related unit file properties and issues a daemon-reload
+ * while the unit is active. No big deal though, because
+ * it influences only the printing of boot/shutdown
+ * status messages. */
+ if (UNIT_IS_INACTIVE_OR_FAILED(ns))
+ m->n_on_console--;
+ else
+ m->n_on_console++;
+ }
+ }
+
if (u->job) {
unexpected = false;
@@ -1422,7 +1438,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
} else
unexpected = true;
- if (u->manager->n_reloading <= 0) {
+ if (m->n_reloading <= 0) {
/* If this state change happened without being
* requested by a job, then let's retroactively start
@@ -1458,18 +1474,18 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
/* The bus just might have become available,
* hence try to connect to it, if we aren't
* yet connected. */
- bus_init(u->manager, true);
+ bus_init(m, true);
if (u->type == UNIT_SERVICE &&
!UNIT_IS_ACTIVE_OR_RELOADING(os) &&
- u->manager->n_reloading <= 0) {
+ m->n_reloading <= 0) {
/* Write audit record if we have just finished starting up */
- manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_START, true);
+ manager_send_unit_audit(m, u, AUDIT_SERVICE_START, true);
u->in_audit = true;
}
if (!UNIT_IS_ACTIVE_OR_RELOADING(os))
- manager_send_unit_plymouth(u->manager, u);
+ manager_send_unit_plymouth(m, u);
} else {
@@ -1479,25 +1495,25 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
if (u->type == UNIT_SERVICE &&
UNIT_IS_INACTIVE_OR_FAILED(ns) &&
!UNIT_IS_INACTIVE_OR_FAILED(os) &&
- u->manager->n_reloading <= 0) {
+ m->n_reloading <= 0) {
/* Hmm, if there was no start record written
* write it now, so that we always have a nice
* pair */
if (!u->in_audit) {
- manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_START, ns == UNIT_INACTIVE);
+ manager_send_unit_audit(m, u, AUDIT_SERVICE_START, ns == UNIT_INACTIVE);
if (ns == UNIT_INACTIVE)
- manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_STOP, true);
+ manager_send_unit_audit(m, u, AUDIT_SERVICE_STOP, true);
} else
/* Write audit record if we have just finished shutting down */
- manager_send_unit_audit(u->manager, u, AUDIT_SERVICE_STOP, ns == UNIT_INACTIVE);
+ manager_send_unit_audit(m, u, AUDIT_SERVICE_STOP, ns == UNIT_INACTIVE);
u->in_audit = false;
}
}
- manager_recheck_journal(u->manager);
+ manager_recheck_journal(m);
/* Maybe we finished startup and are now ready for being
* stopped because unneeded? */
@@ -1900,30 +1916,12 @@ finish:
}
int set_unit_path(const char *p) {
- char *cwd, *c;
- int r;
+ _cleanup_free_ char *c = NULL;
/* This is mostly for debug purposes */
-
- if (path_is_absolute(p)) {
- if (!(c = strdup(p)))
- return -ENOMEM;
- } else {
- if (!(cwd = get_current_dir_name()))
- return -errno;
-
- r = asprintf(&c, "%s/%s", cwd, p);
- free(cwd);
-
- if (r < 0)
- return -ENOMEM;
- }
-
- if (setenv("SYSTEMD_UNIT_PATH", c, 0) < 0) {
- r = -errno;
- free(c);
- return r;
- }
+ c = path_make_absolute_cwd(p);
+ if (setenv("SYSTEMD_UNIT_PATH", c, 0) < 0)
+ return -errno;
return 0;
}
@@ -2109,7 +2107,7 @@ static int unit_add_one_default_cgroup(Unit *u, const char *controller) {
if (r < 0)
goto fail;
- return 0;
+ return 1;
fail:
free(b->path);
@@ -2153,10 +2151,10 @@ CGroupBonding* unit_get_default_cgroup(Unit *u) {
int unit_add_cgroup_attribute(
Unit *u,
+ const CGroupSemantics *semantics,
const char *controller,
const char *name,
const char *value,
- CGroupAttributeMapCallback map_callback,
CGroupAttribute **ret) {
_cleanup_free_ char *c = NULL;
@@ -2164,48 +2162,67 @@ int unit_add_cgroup_attribute(
int r;
assert(u);
- assert(name);
assert(value);
+ if (semantics) {
+ /* Semantics always take precedence */
+ if (semantics->name)
+ name = semantics->name;
+
+ if (semantics->controller)
+ controller = semantics->controller;
+ }
+
+ if (!name)
+ return -EINVAL;
+
if (!controller) {
r = cg_controller_from_attr(name, &c);
if (r < 0)
return -EINVAL;
controller = c;
- } else {
- if (!filename_is_safe(name))
- return -EINVAL;
-
- if (!filename_is_safe(controller))
- return -EINVAL;
}
if (!controller || streq(controller, SYSTEMD_CGROUP_CONTROLLER))
return -EINVAL;
+ if (!filename_is_safe(name))
+ return -EINVAL;
+
+ if (!filename_is_safe(controller))
+ return -EINVAL;
+
+ /* Check if this attribute already exists. Note that we will
+ * explicitly check for the value here too, as there are
+ * attributes which accept multiple values. */
a = cgroup_attribute_find_list(u->cgroup_attributes, controller, name);
if (a) {
- char *v;
-
if (streq(value, a->value)) {
+ /* Exactly the same value is always OK, let's ignore this */
if (ret)
*ret = a;
return 0;
}
- v = strdup(value);
- if (!v)
- return -ENOMEM;
+ if (semantics && !semantics->multiple) {
+ char *v;
- free(a->value);
- a->value = v;
+ /* If this is a single-item entry, we can
+ * simply patch the existing attribute */
- if (ret)
- *ret = a;
+ v = strdup(value);
+ if (!v)
+ return -ENOMEM;
+
+ free(a->value);
+ a->value = v;
- return 1;
+ if (ret)
+ *ret = a;
+ return 1;
+ }
}
a = new0(CGroupAttribute, 1);
@@ -2226,11 +2243,10 @@ int unit_add_cgroup_attribute(
free(a->name);
free(a->value);
free(a);
-
return -ENOMEM;
}
- a->map_callback = map_callback;
+ a->semantics = semantics;
a->unit = u;
LIST_PREPEND(CGroupAttribute, by_unit, u->cgroup_attributes, a);
@@ -2537,21 +2553,8 @@ int unit_coldplug(Unit *u) {
return 0;
}
-void unit_status_printf(Unit *u, const char *status, const char *format, ...) {
- va_list ap;
-
- assert(u);
- assert(format);
-
- if (!manager_get_show_status(u->manager))
- return;
-
- if (!manager_is_booting_or_shutting_down(u->manager))
- return;
-
- va_start(ap, format);
- status_vprintf(status, true, format, ap);
- va_end(ap);
+void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) {
+ manager_status_printf(u->manager, false, status, unit_status_msg_format, unit_description(u));
}
bool unit_need_daemon_reload(Unit *u) {
@@ -2763,52 +2766,77 @@ ExecContext *unit_get_exec_context(Unit *u) {
return (ExecContext*) ((uint8_t*) u + offset);
}
-int unit_write_drop_in(Unit *u, bool runtime, const char *name, const char *data) {
- _cleanup_free_ char *p = NULL, *q = NULL;
+static int drop_in_file(Unit *u, bool runtime, const char *name, char **_p, char **_q) {
+ char *p, *q;
+ int r;
+
assert(u);
+ assert(name);
+ assert(_p);
+ assert(_q);
- if (u->manager->running_as != SYSTEMD_SYSTEM)
+ if (u->manager->running_as == SYSTEMD_USER && runtime)
return -ENOTSUP;
if (!filename_is_safe(name))
return -EINVAL;
- p = strjoin(runtime ? "/run/systemd/system/" : "/etc/systemd/system/", u->id, ".d", NULL);
+ if (u->manager->running_as == SYSTEMD_USER) {
+ _cleanup_free_ char *c = NULL;
+
+ r = user_config_home(&c);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENOENT;
+
+ p = strjoin(c, "/", u->id, ".d", NULL);
+ } else if (runtime)
+ p = strjoin("/run/systemd/system/", u->id, ".d", NULL);
+ else
+ p = strjoin("/etc/systemd/system/", u->id, ".d", NULL);
if (!p)
return -ENOMEM;
q = strjoin(p, "/50-", name, ".conf", NULL);
- if (!q)
+ if (!q) {
+ free(p);
return -ENOMEM;
+ }
- mkdir_p(p, 0755);
- return write_one_line_file_atomic_label(q, data);
+ *_p = p;
+ *_q = q;
+ return 0;
}
-int unit_remove_drop_in(Unit *u, bool runtime, const char *name) {
+int unit_write_drop_in(Unit *u, bool runtime, const char *name, const char *data) {
_cleanup_free_ char *p = NULL, *q = NULL;
+ int r;
assert(u);
- if (u->manager->running_as != SYSTEMD_SYSTEM)
- return -ENOTSUP;
+ r = drop_in_file(u, runtime, name, &p, &q);
+ if (r < 0)
+ return r;
- if (!filename_is_safe(name))
- return -EINVAL;
+ mkdir_p(p, 0755);
+ return write_one_line_file_atomic_label(q, data);
+}
- p = strjoin(runtime ? "/run/systemd/system/" : "/etc/systemd/system/", u->id, ".d", NULL);
- if (!p)
- return -ENOMEM;
+int unit_remove_drop_in(Unit *u, bool runtime, const char *name) {
+ _cleanup_free_ char *p = NULL, *q = NULL;
+ int r;
- q = strjoin(p, "/50-", name, ".conf", NULL);
- if (!q)
- return -ENOMEM;
+ assert(u);
+ r = drop_in_file(u, runtime, name, &p, &q);
if (unlink(q) < 0)
- return -errno;
+ r = -errno;
+ else
+ r = 0;
rmdir(p);
- return 0;
+ return r;
}
int unit_kill_context(
diff --git a/src/core/unit.h b/src/core/unit.h
index c90210302d..9029d6225b 100644
--- a/src/core/unit.h
+++ b/src/core/unit.h
@@ -40,6 +40,7 @@ typedef struct UnitStatusMessageFormats UnitStatusMessageFormats;
#include "condition.h"
#include "install.h"
#include "unit-name.h"
+#include "cgroup-semantics.h"
enum UnitActiveState {
UNIT_ACTIVE,
@@ -445,7 +446,7 @@ int unit_add_cgroup(Unit *u, CGroupBonding *b);
int unit_add_cgroup_from_text(Unit *u, const char *name, bool overwrite, CGroupBonding **ret);
int unit_add_default_cgroups(Unit *u);
CGroupBonding* unit_get_default_cgroup(Unit *u);
-int unit_add_cgroup_attribute(Unit *u, const char *controller, const char *name, const char *value, CGroupAttributeMapCallback map_callback, CGroupAttribute **ret);
+int unit_add_cgroup_attribute(Unit *u, const CGroupSemantics *semantics, const char *controller, const char *name, const char *value, CGroupAttribute **ret);
int unit_choose_id(Unit *u, const char *name);
int unit_set_description(Unit *u, const char *description);
@@ -519,7 +520,7 @@ int unit_add_node_link(Unit *u, const char *what, bool wants);
int unit_coldplug(Unit *u);
-void unit_status_printf(Unit *u, const char *status, const char *format, ...);
+void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format);
bool unit_need_daemon_reload(Unit *u);
diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
index 0de159b89a..9084509704 100644
--- a/src/journal/journalctl.c
+++ b/src/journal/journalctl.c
@@ -433,7 +433,7 @@ static int parse_argv(int argc, char *argv[]) {
if (arg_follow && !arg_no_tail && arg_lines < 0)
arg_lines = 10;
- if (arg_since_set && arg_until_set && arg_since_set > arg_until_set) {
+ if (arg_since_set && arg_until_set && arg_since > arg_until) {
log_error("--since= must be before --until=.");
return -EINVAL;
}
@@ -1079,6 +1079,8 @@ int main(int argc, char *argv[]) {
log_error("Failed to determine timestamp: %s", strerror(-r));
goto finish;
}
+ if (usec > arg_until)
+ goto finish;
}
if (!arg_merge) {
diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c
index 8904560094..19123544b3 100644
--- a/src/journal/sd-journal.c
+++ b/src/journal/sd-journal.c
@@ -1321,10 +1321,12 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname)
assert(prefix);
assert(dirname);
+ log_debug("Considering %s/%s.", prefix, dirname);
+
if ((j->flags & SD_JOURNAL_LOCAL_ONLY) &&
(sd_id128_from_string(dirname, &id) < 0 ||
sd_id128_get_machine(&mid) < 0 ||
- !sd_id128_equal(id, mid)))
+ !(sd_id128_equal(id, mid) || path_startswith(prefix, "/run"))))
return 0;
path = strjoin(prefix, "/", dirname, NULL);
diff --git a/src/kernel-install/kernel-install b/src/kernel-install/kernel-install
new file mode 100644
index 0000000000..16c06e0842
--- /dev/null
+++ b/src/kernel-install/kernel-install
@@ -0,0 +1,152 @@
+#!/bin/bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+#
+# This file is part of systemd.
+#
+# Copyright 2013 Harald Hoyer
+#
+# 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
+# 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/>.
+
+export LC_COLLATE=C
+
+COMMAND="$1"
+KERNEL_VERSION="$2"
+KERNEL_IMAGE="$3"
+
+[[ -f /etc/os-release ]] && . /etc/os-release
+if ! [[ $ID ]]; then
+ echo "Can't determine the name of your distribution. Please create /etc/os-release." >&2
+ echo "See man:os-release(5)" >&2
+ exit 1
+fi
+
+[[ -f /etc/machine-id ]] && read MACHINE_ID < /etc/machine-id
+if ! [[ $MACHINE_ID ]]; then
+ echo "Can't determine your machine id. Please create /etc/machine-id!" >&2
+ echo "See man:machine-id(5)" >&2
+ exit 1
+fi
+
+if [[ -f /etc/kernel/cmdline ]]; then
+ readarray -t BOOT_OPTIONS < /etc/kernel/cmdline
+fi
+
+if ! [[ "${BOOT_OPTIONS[@]}" ]]; then
+ readarray -t BOOT_OPTIONS < /proc/cmdline
+fi
+
+if ! [[ $BOOT_OPTIONS ]]; then
+ echo "Can't determine the kernel command line parameters." >&2
+ echo "Please specify the kernel command line in /etc/kernel/cmdline!" >&2
+ exit 1
+fi
+
+usage()
+{
+ {
+ echo "Usage:"
+ echo " $0 add <kernel-version> <kernel-image>"
+ echo " $0 remove <kernel-version> <kernel-image>"
+ } >&2
+}
+
+if ! ( [[ $COMMAND ]] && [[ $KERNEL_VERSION ]] && [[ $KERNEL_IMAGE ]] ); then
+ usage
+ exit 1
+fi
+
+BOOT_DIR="/${MACHINE_ID}/${KERNEL_VERSION}"
+BOOT_DIR_ABS="/boot${BOOT_DIR}"
+LOADER_ENTRY="/boot/loader/entries/${MACHINE_ID}-${KERNEL_VERSION}.conf"
+ret=0
+
+dropindirs_sort()
+{
+ suffix=$1; shift
+ readarray -t files< <(
+ for d in "$@"; do
+ for i in "${d}/"*${suffix}; do
+ [[ -e $i ]] && echo ${i##*/}
+ done
+ done | sort -Vu
+ )
+
+ for f in "${files[@]}"; do
+ for d in "$@"; do
+ if [[ -e "$d/$f" ]]; then
+ echo "$d/$f"
+ continue 2
+ fi
+ done
+ done
+}
+
+readarray -t PLUGINS < <(
+ dropindirs_sort ".install" \
+ "/etc/kernel/install.d" \
+ "/usr/lib/kernel/install.d"
+)
+
+case "$COMMAND" in
+ add)
+ mkdir -p "$BOOT_DIR_ABS" || exit 1
+
+ for f in "${PLUGINS[@]}"; do
+ [[ -x $f ]] && "$f" add "$KERNEL_VERSION" "$BOOT_DIR_ABS"
+ ((ret+=$?))
+ done
+
+ if ! cp --preserve "$KERNEL_IMAGE" "$BOOT_DIR_ABS"/linux; then
+ echo "Can't copy '$KERNEL_IMAGE to '$BOOT_DIR_ABS/linux'!" >&2
+ fi
+
+ [[ -d /boot/loader/entries ]] || mkdir -p /boot/loader/entries
+
+ {
+ echo "title $PRETTY_NAME"
+ echo "version $KERNEL_VERSION"
+ echo "machine-id $MACHINE_ID"
+ echo "options ${BOOT_OPTIONS[@]}"
+ echo "linux $BOOT_DIR/linux"
+ [[ -f "${BOOT_DIR_ABS}"/initrd ]] && \
+ echo "initrd $BOOT_DIR/initrd"
+ :
+ } > $LOADER_ENTRY
+
+ ((ret+=$?))
+
+ if ! [[ -f $LOADER_ENTRY ]]; then
+ echo "Could not create '$LOADER_ENTRY'!" >&2
+ fi
+ ;;
+
+ remove)
+ for f in "${PLUGINS[@]}"; do
+ [[ -x $f ]] && "$f" remove "$KERNEL_VERSION" "$BOOT_DIR_ABS"
+ ((ret+=$?))
+ done
+
+ rm -fr "$BOOT_DIR_ABS"
+ rm -f "$LOADER_ENTRY"
+ ;;
+
+ *)
+ usage
+ ret=1;;
+esac
+
+((ret+=$?))
+
+exit $ret
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index b8962e9894..8b57421431 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -101,23 +101,30 @@ static uint64_t arg_retain =
(1ULL << CAP_SYS_BOOT) |
(1ULL << CAP_AUDIT_WRITE) |
(1ULL << CAP_AUDIT_CONTROL);
+static char **arg_bind = NULL;
+static char **arg_bind_ro = NULL;
static int help(void) {
printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n"
"Spawn a minimal namespace container for debugging, testing and building.\n\n"
- " -h --help Show this help\n"
- " --version Print version string\n"
- " -D --directory=NAME Root directory for the container\n"
- " -b --boot Boot up full system (i.e. invoke init)\n"
- " -u --user=USER Run the command under specified user or uid\n"
- " -C --controllers=LIST Put the container in specified comma-separated cgroup hierarchies\n"
- " --uuid=UUID Set a specific machine UUID for the container\n"
- " --private-network Disable network in container\n"
- " --read-only Mount the root directory read-only\n"
- " --capability=CAP In addition to the default, retain specified capability\n"
- " --link-journal=MODE Link up guest journal, one of no, auto, guest, host\n"
- " -j Equivalent to --link-journal=host\n",
+ " -h --help Show this help\n"
+ " --version Print version string\n"
+ " -D --directory=NAME Root directory for the container\n"
+ " -b --boot Boot up full system (i.e. invoke init)\n"
+ " -u --user=USER Run the command under specified user or uid\n"
+ " -C --controllers=LIST Put the container in specified comma-separated\n"
+ " cgroup hierarchies\n"
+ " --uuid=UUID Set a specific machine UUID for the container\n"
+ " --private-network Disable network in container\n"
+ " --read-only Mount the root directory read-only\n"
+ " --capability=CAP In addition to the default, retain specified\n"
+ " capability\n"
+ " --link-journal=MODE Link up guest journal, one of no, auto, guest, host\n"
+ " -j Equivalent to --link-journal=host\n"
+ " --bind=PATH[:PATH] Bind mount a file or directory from the host into\n"
+ " the container\n"
+ " --bind-ro=PATH[:PATH] Similar, but creates a read-only bind mount\n",
program_invocation_short_name);
return 0;
@@ -131,7 +138,9 @@ static int parse_argv(int argc, char *argv[]) {
ARG_UUID,
ARG_READ_ONLY,
ARG_CAPABILITY,
- ARG_LINK_JOURNAL
+ ARG_LINK_JOURNAL,
+ ARG_BIND,
+ ARG_BIND_RO
};
static const struct option options[] = {
@@ -146,6 +155,8 @@ static int parse_argv(int argc, char *argv[]) {
{ "read-only", no_argument, NULL, ARG_READ_ONLY },
{ "capability", required_argument, NULL, ARG_CAPABILITY },
{ "link-journal", required_argument, NULL, ARG_LINK_JOURNAL },
+ { "bind", required_argument, NULL, ARG_BIND },
+ { "bind-ro", required_argument, NULL, ARG_BIND_RO },
{ NULL, 0, NULL, 0 }
};
@@ -258,6 +269,43 @@ static int parse_argv(int argc, char *argv[]) {
break;
+ case ARG_BIND:
+ case ARG_BIND_RO: {
+ _cleanup_free_ char *a = NULL, *b = NULL;
+ char *e;
+ char ***x;
+ int r;
+
+ x = c == ARG_BIND ? &arg_bind : &arg_bind_ro;
+
+ e = strchr(optarg, ':');
+ if (e) {
+ a = strndup(optarg, e - optarg);
+ b = strdup(e + 1);
+ } else {
+ a = strdup(optarg);
+ b = strdup(optarg);
+ }
+
+ if (!a || !b)
+ return log_oom();
+
+ if (!path_is_absolute(a) || !path_is_absolute(b)) {
+ log_error("Invalid bind mount specification: %s", optarg);
+ return -EINVAL;
+ }
+
+ r = strv_extend(x, a);
+ if (r < 0)
+ return r;
+
+ r = strv_extend(x, b);
+ if (r < 0)
+ return r;
+
+ break;
+ }
+
case '?':
return -EINVAL;
@@ -303,14 +351,9 @@ static int mount_all(const char *dest) {
char _cleanup_free_ *where = NULL;
int t;
- if (asprintf(&where, "%s/%s", dest, mount_table[k].where) < 0) {
- log_oom();
-
- if (r == 0)
- r = -ENOMEM;
-
- break;
- }
+ where = strjoin(dest, "/", mount_table[k].where, NULL);
+ if (!where)
+ return log_oom();
t = path_is_mount_point(where, true);
if (t < 0) {
@@ -326,7 +369,7 @@ static int mount_all(const char *dest) {
if (mount_table[k].what && t > 0)
continue;
- mkdir_p_label(where, 0755);
+ mkdir_p(where, 0755);
if (mount(mount_table[k].what,
where,
@@ -345,6 +388,32 @@ static int mount_all(const char *dest) {
return r;
}
+static int mount_binds(const char *dest, char **l, unsigned long flags) {
+ char **x, **y;
+
+ STRV_FOREACH_PAIR(x, y, l) {
+ _cleanup_free_ char *where = NULL;
+
+ where = strjoin(dest, "/", *y, NULL);
+ if (!where)
+ return log_oom();
+
+ mkdir_p_label(where, 0755);
+
+ if (mount(*x, where, "bind", MS_BIND, NULL) < 0) {
+ log_error("mount(%s) failed: %m", where);
+ return -errno;
+ }
+
+ if (flags && mount(NULL, where, NULL, MS_REMOUNT|MS_BIND|flags, NULL) < 0) {
+ log_error("mount(%s) failed: %m", where);
+ return -errno;
+ }
+ }
+
+ return 0;
+}
+
static int setup_timezone(const char *dest) {
_cleanup_free_ char *where = NULL, *p = NULL, *q = NULL, *check = NULL, *what = NULL;
char *z, *y;
@@ -1227,7 +1296,7 @@ int main(int argc, char *argv[]) {
const char *home = NULL;
uid_t uid = (uid_t) -1;
gid_t gid = (gid_t) -1;
- unsigned n_env = 0;
+ unsigned n_env = 2;
const char *envp[] = {
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"container=systemd-nspawn", /* LXC sets container=lxc, so follow the scheme here */
@@ -1241,8 +1310,9 @@ int main(int argc, char *argv[]) {
NULL
};
- envp[2] = strv_find_prefix(environ, "TERM=");
- n_env = 3;
+ envp[n_env] = strv_find_prefix(environ, "TERM=");
+ if (envp[n_env])
+ n_env ++;
close_nointr_nofail(pipefd[1]);
fd_wait_for_event(pipefd[0], POLLHUP, -1);
@@ -1346,6 +1416,12 @@ int main(int argc, char *argv[]) {
if (setup_journal(arg_directory) < 0)
goto child_fail;
+ if (mount_binds(arg_directory, arg_bind, 0) < 0)
+ goto child_fail;
+
+ if (mount_binds(arg_directory, arg_bind_ro, MS_RDONLY) < 0)
+ goto child_fail;
+
if (chdir(arg_directory) < 0) {
log_error("chdir(%s) failed: %m", arg_directory);
goto child_fail;
diff --git a/src/shared/log.c b/src/shared/log.c
index ff2dd45350..293c261f9e 100644
--- a/src/shared/log.c
+++ b/src/shared/log.c
@@ -541,11 +541,11 @@ static int log_dispatch(
k = write_to_journal(level, file, line, func,
object_name, object, buffer);
- if (k <= 0) {
- if (k < 0 && k != -EAGAIN)
+ if (k < 0) {
+ if (k != -EAGAIN)
log_close_journal();
log_open_kmsg();
- } else
+ } else if (k > 0)
r++;
}
@@ -554,11 +554,11 @@ static int log_dispatch(
k = write_to_syslog(level, file, line, func,
object_name, object, buffer);
- if (k <= 0) {
- if (k < 0 && k != -EAGAIN)
+ if (k < 0) {
+ if (k != -EAGAIN)
log_close_syslog();
log_open_kmsg();
- } else
+ } else if (k > 0)
r++;
}
@@ -571,11 +571,10 @@ static int log_dispatch(
k = write_to_kmsg(level, file, line, func,
object_name, object, buffer);
- if (k <= 0) {
- if (k < 0 && k != -EAGAIN)
- log_close_kmsg();
+ if (k < 0) {
+ log_close_kmsg();
log_open_console();
- } else
+ } else if (k > 0)
r++;
}
diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c
index fa4995ceea..ffdc5367cb 100644
--- a/src/shared/path-lookup.c
+++ b/src/shared/path-lookup.c
@@ -41,21 +41,26 @@ DEFINE_STRING_TABLE_LOOKUP(systemd_running_as, SystemdRunningAs);
int user_config_home(char **config_home) {
const char *e;
+ char *r;
e = getenv("XDG_CONFIG_HOME");
if (e) {
- if (asprintf(config_home, "%s/systemd/user", e) < 0)
+ r = strappend(e, "/systemd/user");
+ if (!r)
return -ENOMEM;
+ *config_home = r;
return 1;
} else {
const char *home;
home = getenv("HOME");
if (home) {
- if (asprintf(config_home, "%s/.config/systemd/user", home) < 0)
+ r = strappend(home, "/.config/systemd/user");
+ if (!r)
return -ENOMEM;
+ *config_home = r;
return 1;
}
}
diff --git a/src/shared/socket-util.c b/src/shared/socket-util.c
index 6c94d69486..f6ddea3183 100644
--- a/src/shared/socket-util.c
+++ b/src/shared/socket-util.c
@@ -565,6 +565,45 @@ bool socket_address_matches_fd(const SocketAddress *a, int fd) {
return false;
}
+int make_socket_fd(const char* address, int flags) {
+ SocketAddress a;
+ int fd, r;
+ char _cleanup_free_ *p = NULL;
+
+ r = socket_address_parse(&a, address);
+ if (r < 0) {
+ log_error("failed to parse socket: %s", strerror(-r));
+ return r;
+ }
+
+ fd = socket(socket_address_family(&a), flags, 0);
+ if (fd < 0) {
+ log_error("socket(): %m");
+ return -errno;
+ }
+
+ r = socket_address_print(&a, &p);
+ if (r < 0) {
+ log_error("socket_address_print(): %s", strerror(-r));
+ return r;
+ }
+ log_info("Listening on %s", p);
+
+ r = bind(fd, &a.sockaddr.sa, a.size);
+ if (r < 0) {
+ log_error("bind to %s: %m", address);
+ return -errno;
+ }
+
+ r = listen(fd, SOMAXCONN);
+ if (r < 0) {
+ log_error("listen on %s: %m", address);
+ return -errno;
+ }
+
+ return fd;
+}
+
static const char* const netlink_family_table[] = {
[NETLINK_ROUTE] = "route",
[NETLINK_FIREWALL] = "firewall",
diff --git a/src/shared/socket-util.h b/src/shared/socket-util.h
index 771765d323..33838345ed 100644
--- a/src/shared/socket-util.h
+++ b/src/shared/socket-util.h
@@ -88,6 +88,8 @@ bool socket_address_is_netlink(const SocketAddress *a, const char *s);
bool socket_address_matches_fd(const SocketAddress *a, int fd);
+int make_socket_fd(const char* address, int flags);
+
bool socket_address_equal(const SocketAddress *a, const SocketAddress *b);
bool socket_address_needs_mount(const SocketAddress *a, const char *prefix);
diff --git a/src/shared/strv.c b/src/shared/strv.c
index ec25755289..60c4762572 100644
--- a/src/shared/strv.c
+++ b/src/shared/strv.c
@@ -305,6 +305,31 @@ char **strv_split_quoted(const char *s) {
return r;
}
+char **strv_split_newlines(const char *s) {
+ char **l;
+ unsigned n;
+
+ assert(s);
+
+ /* Special version of strv_split() that splits on newlines and
+ * suppresses an empty string at the end */
+
+ l = strv_split(s, NEWLINE);
+ if (!l)
+ return NULL;
+
+ n = strv_length(l);
+ if (n <= 0)
+ return l;
+
+ if (isempty(l[n-1])) {
+ free(l[n-1]);
+ l[n-1] = NULL;
+ }
+
+ return l;
+}
+
char *strv_join(char **l, const char *separator) {
char *r, *e;
char **s;
diff --git a/src/shared/strv.h b/src/shared/strv.h
index b3802a7a3f..623f10216d 100644
--- a/src/shared/strv.h
+++ b/src/shared/strv.h
@@ -58,6 +58,7 @@ static inline bool strv_isempty(char **l) {
char **strv_split(const char *s, const char *separator) _malloc_;
char **strv_split_quoted(const char *s) _malloc_;
+char **strv_split_newlines(const char *s) _malloc_;
char *strv_join(char **l, const char *separator) _malloc_;
diff --git a/src/shared/util.c b/src/shared/util.c
index f5adedc531..e643cd367c 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -2859,12 +2859,13 @@ cpu_set_t* cpu_set_malloc(unsigned *ncpus) {
}
}
-int status_vprintf(const char *status, bool ellipse, const char *format, va_list ap) {
+int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap) {
static const char status_indent[] = " "; /* "[" STATUS "] " */
_cleanup_free_ char *s = NULL;
_cleanup_close_ int fd = -1;
- struct iovec iovec[5];
+ struct iovec iovec[6];
int n = 0;
+ static bool prev_ephemeral;
assert(format);
@@ -2902,6 +2903,10 @@ int status_vprintf(const char *status, bool ellipse, const char *format, va_list
zero(iovec);
+ if (prev_ephemeral)
+ IOVEC_SET_STRING(iovec[n++], "\r" ANSI_ERASE_TO_END_OF_LINE);
+ prev_ephemeral = ephemeral;
+
if (status) {
if (!isempty(status)) {
IOVEC_SET_STRING(iovec[n++], "[");
@@ -2912,7 +2917,8 @@ int status_vprintf(const char *status, bool ellipse, const char *format, va_list
}
IOVEC_SET_STRING(iovec[n++], s);
- IOVEC_SET_STRING(iovec[n++], "\n");
+ if (!ephemeral)
+ IOVEC_SET_STRING(iovec[n++], "\n");
if (writev(fd, iovec, n) < 0)
return -errno;
@@ -2920,14 +2926,14 @@ int status_vprintf(const char *status, bool ellipse, const char *format, va_list
return 0;
}
-int status_printf(const char *status, bool ellipse, const char *format, ...) {
+int status_printf(const char *status, bool ellipse, bool ephemeral, const char *format, ...) {
va_list ap;
int r;
assert(format);
va_start(ap, format);
- r = status_vprintf(status, ellipse, format, ap);
+ r = status_vprintf(status, ellipse, ephemeral, format, ap);
va_end(ap);
return r;
@@ -3503,6 +3509,29 @@ int vtnr_from_tty(const char *tty) {
return i;
}
+char *resolve_dev_console(char **active) {
+ char *tty;
+
+ /* Resolve where /dev/console is pointing to, if /sys is actually ours
+ * (i.e. not read-only-mounted which is a sign for container setups) */
+
+ if (path_is_read_only_fs("/sys") > 0)
+ return NULL;
+
+ if (read_one_line_file("/sys/class/tty/console/active", active) < 0)
+ return NULL;
+
+ /* If multiple log outputs are configured the last one is what
+ * /dev/console points to */
+ tty = strrchr(*active, ' ');
+ if (tty)
+ tty++;
+ else
+ tty = *active;
+
+ return tty;
+}
+
bool tty_is_vc_resolve(const char *tty) {
char *active = NULL;
bool b;
@@ -3512,19 +3541,11 @@ bool tty_is_vc_resolve(const char *tty) {
if (startswith(tty, "/dev/"))
tty += 5;
- /* Resolve where /dev/console is pointing to, if /sys is
- * actually ours (i.e. not read-only-mounted which is a sign
- * for container setups) */
- if (streq(tty, "console") && path_is_read_only_fs("/sys") <= 0)
- if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) {
- /* If multiple log outputs are configured the
- * last one is what /dev/console points to */
- tty = strrchr(active, ' ');
- if (tty)
- tty++;
- else
- tty = active;
- }
+ if (streq(tty, "console")) {
+ tty = resolve_dev_console(&active);
+ if (!tty)
+ return false;
+ }
b = tty_is_vc(tty);
free(active);
diff --git a/src/shared/util.h b/src/shared/util.h
index 19cc36af84..b5ad1ff3b5 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -55,10 +55,12 @@ union dirent_storage {
#define FORMAT_BYTES_MAX 8
#define ANSI_HIGHLIGHT_ON "\x1B[1;39m"
+#define ANSI_RED_ON "\x1B[31m"
#define ANSI_HIGHLIGHT_RED_ON "\x1B[1;31m"
#define ANSI_HIGHLIGHT_GREEN_ON "\x1B[1;32m"
#define ANSI_HIGHLIGHT_YELLOW_ON "\x1B[1;33m"
#define ANSI_HIGHLIGHT_OFF "\x1B[0m"
+#define ANSI_ERASE_TO_END_OF_LINE "\x1B[K"
size_t page_size(void);
#define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
@@ -354,8 +356,8 @@ int pipe_eof(int fd);
cpu_set_t* cpu_set_malloc(unsigned *ncpus);
-int status_vprintf(const char *status, bool ellipse, const char *format, va_list ap);
-int status_printf(const char *status, bool ellipse, const char *format, ...);
+int status_vprintf(const char *status, bool ellipse, bool ephemeral, const char *format, va_list ap);
+int status_printf(const char *status, bool ellipse, bool ephemeral, const char *format, ...);
int status_welcome(void);
int fd_columns(int fd);
@@ -388,6 +390,7 @@ DIR *xopendirat(int dirfd, const char *name, int flags);
char *fstab_node_to_udev_node(const char *p);
+char *resolve_dev_console(char **active);
bool tty_is_vc(const char *tty);
bool tty_is_vc_resolve(const char *tty);
bool tty_is_console(const char *tty);
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 8174376257..c67c6c972a 100644
--- a/src/systemctl/systemctl.c
+++ b/src/systemctl/systemctl.c
@@ -1045,7 +1045,7 @@ static bool need_daemon_reload(DBusConnection *bus, const char *unit) {
if (!n)
return log_oom();
- r = bus_method_call_with_reply (
+ r = bus_method_call_with_reply(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
@@ -1963,53 +1963,41 @@ static int kill_unit(DBusConnection *bus, char **args) {
}
static int set_cgroup(DBusConnection *bus, char **args) {
- _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
- DBusError error;
- const char *method;
- DBusMessageIter iter;
- int r;
_cleanup_free_ char *n = NULL;
- const char *runtime;
+ const char *method, *runtime;
+ char **argument;
+ int r;
assert(bus);
assert(args);
- dbus_error_init(&error);
-
method =
- streq(args[0], "set-cgroup") ? "SetUnitControlGroups" :
- streq(args[0], "unset-group") ? "UnsetUnitControlGroups"
- : "UnsetUnitControlGroupAttributes";
+ streq(args[0], "set-cgroup") ? "SetUnitControlGroup" :
+ streq(args[0], "unset-cgroup") ? "UnsetUnitControlGroup"
+ : "UnsetUnitControlGroupAttribute";
+
+ runtime = arg_runtime ? "runtime" : "persistent";
n = unit_name_mangle(args[1]);
if (!n)
return log_oom();
- m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- method);
- if (!m)
- return log_oom();
-
- dbus_message_iter_init_append(m, &iter);
- if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &n))
- return log_oom();
-
- r = bus_append_strv_iter(&iter, args + 2);
- if (r < 0)
- return log_oom();
+ STRV_FOREACH(argument, args + 2) {
- runtime = arg_runtime ? "runtime" : "persistent";
- if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &runtime))
- return log_oom();
-
- reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
- if (!reply) {
- log_error("Failed to issue method call: %s", bus_error_message(&error));
- dbus_error_free(&error);
- return -EIO;
+ r = bus_method_call_with_reply(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ method,
+ NULL,
+ NULL,
+ DBUS_TYPE_STRING, &n,
+ DBUS_TYPE_STRING, argument,
+ DBUS_TYPE_STRING, &runtime,
+ DBUS_TYPE_INVALID);
+ if (r < 0)
+ return r;
}
return 0;
@@ -2018,20 +2006,17 @@ static int set_cgroup(DBusConnection *bus, char **args) {
static int set_cgroup_attr(DBusConnection *bus, char **args) {
_cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
DBusError error;
- DBusMessageIter iter, sub, sub2;
- char **x, **y;
+ DBusMessageIter iter;
_cleanup_free_ char *n = NULL;
const char *runtime;
+ int r;
assert(bus);
assert(args);
dbus_error_init(&error);
- if (strv_length(args) % 2 != 0) {
- log_error("Expecting an uneven number of arguments!");
- return -EINVAL;
- }
+ runtime = arg_runtime ? "runtime" : "persistent";
n = unit_name_mangle(args[1]);
if (!n)
@@ -2041,26 +2026,20 @@ static int set_cgroup_attr(DBusConnection *bus, char **args) {
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
- "SetUnitControlGroupAttributes");
+ "SetUnitControlGroupAttribute");
if (!m)
return log_oom();
dbus_message_iter_init_append(m, &iter);
if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &n) ||
- !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ss)", &sub))
+ !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &args[2]))
return log_oom();
- STRV_FOREACH_PAIR(x, y, args + 2) {
- if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, x) ||
- !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, y) ||
- !dbus_message_iter_close_container(&sub, &sub2))
- return log_oom();
- }
+ r = bus_append_strv_iter(&iter, args + 3);
+ if (r < 0)
+ return log_oom();
- runtime = arg_runtime ? "runtime" : "persistent";
- if (!dbus_message_iter_close_container(&iter, &sub) ||
- !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &runtime))
+ if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &runtime))
return log_oom();
reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
@@ -2075,57 +2054,49 @@ static int set_cgroup_attr(DBusConnection *bus, char **args) {
static int get_cgroup_attr(DBusConnection *bus, char **args) {
_cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
- DBusError error;
- DBusMessageIter iter;
- int r;
_cleanup_free_ char *n = NULL;
- _cleanup_strv_free_ char **list = NULL;
- char **a;
+ char **argument;
+ int r;
assert(bus);
assert(args);
- dbus_error_init(&error);
-
n = unit_name_mangle(args[1]);
if (!n)
return log_oom();
- m = dbus_message_new_method_call(
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "GetUnitControlGroupAttributes");
- if (!m)
- return log_oom();
+ STRV_FOREACH(argument, args + 2) {
+ _cleanup_strv_free_ char **list = NULL;
+ DBusMessageIter iter;
+ char **a;
- dbus_message_iter_init_append(m, &iter);
- if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &n))
- return log_oom();
-
- r = bus_append_strv_iter(&iter, args + 2);
- if (r < 0)
- return log_oom();
-
- reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
- if (!reply) {
- log_error("Failed to issue method call: %s", bus_error_message(&error));
- dbus_error_free(&error);
- return -EIO;
- }
+ r = bus_method_call_with_reply(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "GetUnitControlGroupAttribute",
+ &reply,
+ NULL,
+ DBUS_TYPE_STRING, &n,
+ DBUS_TYPE_STRING, argument,
+ DBUS_TYPE_INVALID);
+ if (r < 0)
+ return r;
- dbus_message_iter_init(reply, &iter);
- r = bus_parse_strv_iter(&iter, &list);
- if (r < 0) {
- log_error("Failed to parse value list.");
- return r;
- }
+ dbus_message_iter_init(reply, &iter);
+ r = bus_parse_strv_iter(&iter, &list);
+ if (r < 0) {
+ log_error("Failed to parse value list.");
+ return r;
+ }
- STRV_FOREACH(a, list) {
- if (endswith(*a, "\n"))
- fputs(*a, stdout);
- else
- puts(*a);
+ STRV_FOREACH(a, list) {
+ if (endswith(*a, "\n"))
+ fputs(*a, stdout);
+ else
+ puts(*a);
+ }
}
return 0;
@@ -3985,16 +3956,15 @@ static int enable_unit(DBusConnection *bus, char **args) {
}
if (carries_install_info == 0)
- log_warning(
-"The unit files have no [Install] section. They are not meant to be enabled\n"
-"using systemctl.\n"
-"Possible reasons for having this kind of units are:\n"
-"1) A unit may be statically enabled by being symlinked from another unit's\n"
-" .wants/ or .requires/ directory.\n"
-"2) A unit's purpose may be to act as a helper for some other unit which has\n"
-" a requirement dependency on it.\n"
-"3) A unit may be started when needed via activation (socket, path, timer,\n"
-" D-Bus, udev, scripted systemctl call, ...).\n");
+ log_warning("The unit files have no [Install] section. They are not meant to be enabled\n"
+ "using systemctl.\n"
+ "Possible reasons for having this kind of units are:\n"
+ "1) A unit may be statically enabled by being symlinked from another unit's\n"
+ " .wants/ or .requires/ directory.\n"
+ "2) A unit's purpose may be to act as a helper for some other unit which has\n"
+ " a requirement dependency on it.\n"
+ "3) A unit may be started when needed via activation (socket, path, timer,\n"
+ " D-Bus, udev, scripted systemctl call, ...).\n");
finish:
unit_file_changes_free(changes, n_changes);
@@ -5124,11 +5094,11 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
{ "condreload", MORE, 2, start_unit }, /* For compatibility with ALTLinux */
{ "condrestart", MORE, 2, start_unit }, /* For compatibility with RH */
{ "isolate", EQUAL, 2, start_unit },
- { "set-cgroup", MORE, 2, set_cgroup },
- { "unset-cgroup", MORE, 2, set_cgroup },
- { "get-cgroup-attr", MORE, 2, get_cgroup_attr },
- { "set-cgroup-attr", MORE, 2, set_cgroup_attr },
- { "unset-cgroup-attr", MORE, 2, set_cgroup },
+ { "set-cgroup", MORE, 3, set_cgroup },
+ { "unset-cgroup", MORE, 3, set_cgroup },
+ { "get-cgroup-attr", MORE, 3, get_cgroup_attr },
+ { "set-cgroup-attr", MORE, 4, set_cgroup_attr },
+ { "unset-cgroup-attr", MORE, 3, set_cgroup },
{ "kill", MORE, 2, kill_unit },
{ "is-active", MORE, 2, check_unit_active },
{ "check", MORE, 2, check_unit_active },
diff --git a/src/test/test-util.c b/src/test/test-util.c
index e4dea09f85..cb0af4f6a1 100644
--- a/src/test/test-util.c
+++ b/src/test/test-util.c
@@ -129,17 +129,107 @@ static void test_safe_atod(void) {
assert_se(r == -EINVAL);
}
+static void test_strstrip(void) {
+ char *r;
+ char input[] = " hello, waldo. ";
+
+ r = strstrip(input);
+ assert_se(streq(r, "hello, waldo."));
+
+}
+
+static void test_delete_chars(void) {
+ char *r;
+ char input[] = " hello, waldo. abc";
+
+ r = delete_chars(input, WHITESPACE);
+ assert_se(streq(r, "hello,waldo.abc"));
+}
+
+static void test_in_charset(void) {
+ assert_se(in_charset("dddaaabbbcccc", "abcd"));
+ assert_se(!in_charset("dddaaabbbcccc", "abc f"));
+}
+
+static void test_hexchar(void) {
+ assert_se(hexchar(0xa) == 'a');
+ assert_se(hexchar(0x0) == '0');
+}
+
+static void test_unhexchar(void) {
+ assert_se(unhexchar('a') == 0xA);
+ assert_se(unhexchar('A') == 0xA);
+ assert_se(unhexchar('0') == 0x0);
+}
+
+static void test_octchar(void) {
+ assert_se(octchar(00) == '0');
+ assert_se(octchar(07) == '7');
+}
+
+static void test_unoctchar(void) {
+ assert_se(unoctchar('0') == 00);
+ assert_se(unoctchar('7') == 07);
+}
+
+static void test_decchar(void) {
+ assert_se(decchar(0) == '0');
+ assert_se(decchar(9) == '9');
+}
+
+static void test_undecchar(void) {
+ assert_se(undecchar('0') == 0);
+ assert_se(undecchar('9') == 9);
+}
+
+static void test_foreach_word(void) {
+ char *w, *state;
+ size_t l;
+ int i = 0;
+ const char test[] = "test abc d\te f ";
+ const char * const expected[] = {
+ "test",
+ "abc",
+ "d",
+ "e",
+ "f",
+ "",
+ NULL
+ };
+
+ FOREACH_WORD(w, l, test, state) {
+ assert_se(strneq(expected[i++], w, l));
+ }
+}
+
static void test_foreach_word_quoted(void) {
char *w, *state;
size_t l;
- const char test[] = "test a b c 'd' e '' '' hhh '' ''";
+ int i = 0;
+ const char test[] = "test a b c 'd' e '' '' hhh '' '' \"a b c\"";
+ const char * const expected[] = {
+ "test",
+ "a",
+ "b",
+ "c",
+ "d",
+ "e",
+ "",
+ "",
+ "hhh",
+ "",
+ "",
+ "a b c",
+ NULL
+ };
+
printf("<%s>\n", test);
FOREACH_WORD_QUOTED(w, l, test, state) {
- char *t;
+ _cleanup_free_ char *t = NULL;
assert_se(t = strndup(w, l));
+ assert_se(strneq(expected[i++], w, l));
printf("<%s>\n", t);
- free(t);
}
}
@@ -175,12 +265,22 @@ int main(int argc, char *argv[]) {
test_streq_ptr();
test_first_word();
test_parse_boolean();
- test_default_term_for_tty();
test_parse_pid();
test_parse_uid();
test_safe_atolli();
test_safe_atod();
+ test_strstrip();
+ test_delete_chars();
+ test_in_charset();
+ test_hexchar();
+ test_unhexchar();
+ test_octchar();
+ test_unoctchar();
+ test_decchar();
+ test_undecchar();
+ test_foreach_word();
test_foreach_word_quoted();
+ test_default_term_for_tty();
test_memdup_multiply();
return 0;