2 This file is part of systemd.
4 Copyright 2010 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
30 //#include <sys/mman.h>
31 #include <sys/prctl.h>
32 #include <sys/statfs.h>
33 #include <sys/sysmacros.h>
34 //#include <sys/types.h>
37 #include "alloc-util.h"
40 #include "dirent-util.h"
43 //#include "formats-util.h"
45 #include "hostname-util.h"
48 //#include "missing.h"
49 #include "parse-util.h"
50 //#include "path-util.h"
51 #include "process-util.h"
53 #include "signal-util.h"
54 #include "stat-util.h"
55 #include "string-util.h"
57 #include "time-util.h"
58 #include "umask-util.h"
59 #include "user-util.h"
62 /* Put this test here for a lack of better place */
63 assert_cc(EAGAIN == EWOULDBLOCK);
66 char **saved_argv = NULL;
68 size_t page_size(void) {
69 static thread_local size_t pgsz = 0;
72 if (_likely_(pgsz > 0))
75 r = sysconf(_SC_PAGESIZE);
82 static int do_execute(char **directories, usec_t timeout, char *argv[]) {
83 _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
84 _cleanup_set_free_free_ Set *seen = NULL;
87 /* We fork this all off from a child process so that we can
88 * somewhat cleanly make use of SIGALRM to set a time limit */
90 (void) reset_all_signal_handlers();
91 (void) reset_signal_mask();
93 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
95 pids = hashmap_new(NULL);
99 seen = set_new(&string_hash_ops);
103 STRV_FOREACH(directory, directories) {
104 _cleanup_closedir_ DIR *d;
107 d = opendir(*directory);
112 return log_error_errno(errno, "Failed to open directory %s: %m", *directory);
115 FOREACH_DIRENT(de, d, break) {
116 _cleanup_free_ char *path = NULL;
120 if (!dirent_is_file(de))
123 if (set_contains(seen, de->d_name)) {
124 log_debug("%1$s/%2$s skipped (%2$s was already seen).", *directory, de->d_name);
128 r = set_put_strdup(seen, de->d_name);
132 path = strjoin(*directory, "/", de->d_name, NULL);
136 if (null_or_empty_path(path)) {
137 log_debug("%s is empty (a mask).", path);
143 log_error_errno(errno, "Failed to fork: %m");
145 } else if (pid == 0) {
148 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
158 return log_error_errno(errno, "Failed to execute %s: %m", path);
161 log_debug("Spawned %s as " PID_FMT ".", path, pid);
163 r = hashmap_put(pids, PID_TO_PTR(pid), path);
170 /* Abort execution of this process after the timout. We simply
171 * rely on SIGALRM as default action terminating the process,
172 * and turn on alarm(). */
174 if (timeout != USEC_INFINITY)
175 alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
177 while (!hashmap_isempty(pids)) {
178 _cleanup_free_ char *path = NULL;
181 pid = PTR_TO_PID(hashmap_first_key(pids));
184 path = hashmap_remove(pids, PID_TO_PTR(pid));
187 wait_for_terminate_and_warn(path, pid, true);
193 void execute_directories(const char* const* directories, usec_t timeout, char *argv[]) {
197 char **dirs = (char**) directories;
199 assert(!strv_isempty(dirs));
201 name = basename(dirs[0]);
202 assert(!isempty(name));
204 /* Executes all binaries in the directories in parallel and waits
205 * for them to finish. Optionally a timeout is applied. If a file
206 * with the same name exists in more than one directory, the
207 * earliest one wins. */
209 executor_pid = fork();
210 if (executor_pid < 0) {
211 log_error_errno(errno, "Failed to fork: %m");
214 } else if (executor_pid == 0) {
215 r = do_execute(dirs, timeout, argv);
216 _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
219 wait_for_terminate_and_warn(name, executor_pid, true);
222 #if 0 /// UNNEEDED by elogind
223 bool plymouth_running(void) {
224 return access("/run/plymouth/pid", F_OK) >= 0;
228 bool display_is_local(const char *display) {
237 int socket_from_display(const char *display, char **path) {
244 if (!display_is_local(display))
247 k = strspn(display+1, "0123456789");
249 f = new(char, strlen("/tmp/.X11-unix/X") + k + 1);
253 c = stpcpy(f, "/tmp/.X11-unix/X");
254 memcpy(c, display+1, k);
262 #if 0 /// UNNEEDED by elogind
263 int block_get_whole_disk(dev_t d, dev_t *ret) {
270 /* If it has a queue this is good enough for us */
271 if (asprintf(&p, "/sys/dev/block/%u:%u/queue", major(d), minor(d)) < 0)
282 /* If it is a partition find the originating device */
283 if (asprintf(&p, "/sys/dev/block/%u:%u/partition", major(d), minor(d)) < 0)
292 /* Get parent dev_t */
293 if (asprintf(&p, "/sys/dev/block/%u:%u/../dev", major(d), minor(d)) < 0)
296 r = read_one_line_file(p, &s);
302 r = sscanf(s, "%u:%u", &m, &n);
308 /* Only return this if it is really good enough for us. */
309 if (asprintf(&p, "/sys/dev/block/%u:%u/queue", m, n) < 0)
316 *ret = makedev(m, n);
323 bool kexec_loaded(void) {
327 if (read_one_line_file("/sys/kernel/kexec_loaded", &s) >= 0) {
335 int prot_from_flags(int flags) {
337 switch (flags & O_ACCMODE) {
346 return PROT_READ|PROT_WRITE;
354 int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...) {
355 bool stdout_is_tty, stderr_is_tty;
356 pid_t parent_pid, agent_pid;
357 sigset_t ss, saved_ss;
365 /* Spawns a temporary TTY agent, making sure it goes away when
368 parent_pid = getpid();
370 /* First we temporarily block all signals, so that the new
371 * child has them blocked initially. This way, we can be sure
372 * that SIGTERMs are not lost we might send to the agent. */
373 assert_se(sigfillset(&ss) >= 0);
374 assert_se(sigprocmask(SIG_SETMASK, &ss, &saved_ss) >= 0);
378 assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
382 if (agent_pid != 0) {
383 assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
390 * Make sure the agent goes away when the parent dies */
391 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
394 /* Make sure we actually can kill the agent, if we need to, in
395 * case somebody invoked us from a shell script that trapped
396 * SIGTERM or so... */
397 (void) reset_all_signal_handlers();
398 (void) reset_signal_mask();
400 /* Check whether our parent died before we were able
401 * to set the death signal and unblock the signals */
402 if (getppid() != parent_pid)
405 /* Don't leak fds to the agent */
406 close_all_fds(except, n_except);
408 stdout_is_tty = isatty(STDOUT_FILENO);
409 stderr_is_tty = isatty(STDERR_FILENO);
411 if (!stdout_is_tty || !stderr_is_tty) {
414 /* Detach from stdout/stderr. and reopen
415 * /dev/tty for them. This is important to
416 * ensure that when systemctl is started via
417 * popen() or a similar call that expects to
418 * read EOF we actually do generate EOF and
419 * not delay this indefinitely by because we
420 * keep an unused copy of stdin around. */
421 fd = open("/dev/tty", O_WRONLY);
423 log_error_errno(errno, "Failed to open /dev/tty: %m");
427 if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) {
428 log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
432 if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) {
433 log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
437 if (fd > STDERR_FILENO)
441 /* Count arguments */
443 for (n = 0; va_arg(ap, char*); n++)
448 l = alloca(sizeof(char *) * (n + 1));
450 /* Fill in arguments */
452 for (i = 0; i <= n; i++)
453 l[i] = va_arg(ap, char*);
460 bool in_initrd(void) {
461 static int saved = -1;
467 /* We make two checks here:
469 * 1. the flag file /etc/initrd-release must exist
470 * 2. the root file system must be a memory file system
472 * The second check is extra paranoia, since misdetecting an
473 * initrd can have bad bad consequences due the initrd
474 * emptying when transititioning to the main systemd.
477 saved = access("/etc/initrd-release", F_OK) >= 0 &&
478 statfs("/", &s) >= 0 &&
484 #if 0 /// UNNEEDED by elogind
485 /* hey glibc, APIs with callbacks without a user pointer are so useless */
486 void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
487 int (*compar) (const void *, const void *, void *), void *arg) {
496 p = (void *)(((const char *) base) + (idx * size));
497 comparison = compar(key, p, arg);
500 else if (comparison > 0)
508 int on_ac_power(void) {
509 bool found_offline = false, found_online = false;
510 _cleanup_closedir_ DIR *d = NULL;
512 d = opendir("/sys/class/power_supply");
514 return errno == ENOENT ? true : -errno;
518 _cleanup_close_ int fd = -1, device = -1;
524 if (!de && errno > 0)
530 if (hidden_file(de->d_name))
533 device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
535 if (errno == ENOENT || errno == ENOTDIR)
541 fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY);
549 n = read(fd, contents, sizeof(contents));
553 if (n != 6 || memcmp(contents, "Mains\n", 6))
557 fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY);
565 n = read(fd, contents, sizeof(contents));
569 if (n != 2 || contents[1] != '\n')
572 if (contents[0] == '1') {
575 } else if (contents[0] == '0')
576 found_offline = true;
581 return found_online || !found_offline;
584 bool id128_is_valid(const char *s) {
590 /* Simple formatted 128bit hex string */
592 for (i = 0; i < l; i++) {
595 if (!(c >= '0' && c <= '9') &&
596 !(c >= 'a' && c <= 'z') &&
597 !(c >= 'A' && c <= 'Z'))
601 } else if (l == 36) {
605 for (i = 0; i < l; i++) {
608 if ((i == 8 || i == 13 || i == 18 || i == 23)) {
612 if (!(c >= '0' && c <= '9') &&
613 !(c >= 'a' && c <= 'z') &&
614 !(c >= 'A' && c <= 'Z'))
626 int container_get_leader(const char *machine, pid_t *pid) {
627 _cleanup_free_ char *s = NULL, *class = NULL;
635 if (!machine_name_is_valid(machine))
638 p = strjoina("/run/systemd/machines/", machine);
639 r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL);
647 if (!streq_ptr(class, "container"))
650 r = parse_pid(s, &leader);
660 int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) {
661 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1, usernsfd = -1;
669 mntns = procfs_file_alloca(pid, "ns/mnt");
670 mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
678 pidns = procfs_file_alloca(pid, "ns/pid");
679 pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
687 netns = procfs_file_alloca(pid, "ns/net");
688 netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
696 userns = procfs_file_alloca(pid, "ns/user");
697 usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
698 if (usernsfd < 0 && errno != ENOENT)
705 root = procfs_file_alloca(pid, "root");
706 rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
721 *userns_fd = usernsfd;
726 pidnsfd = mntnsfd = netnsfd = usernsfd = -1;
731 int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) {
732 if (userns_fd >= 0) {
733 /* Can't setns to your own userns, since then you could
734 * escalate from non-root to root in your own namespace, so
735 * check if namespaces equal before attempting to enter. */
736 _cleanup_free_ char *userns_fd_path = NULL;
738 if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0)
741 r = files_same(userns_fd_path, "/proc/self/ns/user");
749 if (setns(pidns_fd, CLONE_NEWPID) < 0)
753 if (setns(mntns_fd, CLONE_NEWNS) < 0)
757 if (setns(netns_fd, CLONE_NEWNET) < 0)
761 if (setns(userns_fd, CLONE_NEWUSER) < 0)
765 if (fchdir(root_fd) < 0)
772 return reset_uid_gid();
775 uint64_t physical_memory(void) {
778 /* We return this as uint64_t in case we are running as 32bit
779 * process on a 64bit kernel with huge amounts of memory */
781 mem = sysconf(_SC_PHYS_PAGES);
784 return (uint64_t) mem * (uint64_t) page_size();
787 #if 0 /// UNNEEDED by elogind
788 int update_reboot_param_file(const char *param) {
793 r = write_string_file(REBOOT_PARAM_FILE, param, WRITE_STRING_FILE_CREATE);
795 return log_error_errno(r, "Failed to write reboot param to "REBOOT_PARAM_FILE": %m");
797 (void) unlink(REBOOT_PARAM_FILE);
804 puts(PACKAGE_STRING "\n"