+
+int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...) {
+ pid_t parent_pid, agent_pid;
+ int fd;
+ bool stdout_is_tty, stderr_is_tty;
+ unsigned n, i;
+ va_list ap;
+ char **l;
+
+ assert(pid);
+ assert(path);
+
+ parent_pid = getpid();
+
+ /* Spawns a temporary TTY agent, making sure it goes away when
+ * we go away */
+
+ agent_pid = fork();
+ if (agent_pid < 0)
+ return -errno;
+
+ if (agent_pid != 0) {
+ *pid = agent_pid;
+ return 0;
+ }
+
+ /* In the child:
+ *
+ * Make sure the agent goes away when the parent dies */
+ if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
+ _exit(EXIT_FAILURE);
+
+ /* Check whether our parent died before we were able
+ * to set the death signal */
+ if (getppid() != parent_pid)
+ _exit(EXIT_SUCCESS);
+
+ /* Don't leak fds to the agent */
+ close_all_fds(except, n_except);
+
+ stdout_is_tty = isatty(STDOUT_FILENO);
+ stderr_is_tty = isatty(STDERR_FILENO);
+
+ if (!stdout_is_tty || !stderr_is_tty) {
+ /* Detach from stdout/stderr. and reopen
+ * /dev/tty for them. This is important to
+ * ensure that when systemctl is started via
+ * popen() or a similar call that expects to
+ * read EOF we actually do generate EOF and
+ * not delay this indefinitely by because we
+ * keep an unused copy of stdin around. */
+ fd = open("/dev/tty", O_WRONLY);
+ if (fd < 0) {
+ log_error("Failed to open /dev/tty: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ if (!stdout_is_tty)
+ dup2(fd, STDOUT_FILENO);
+
+ if (!stderr_is_tty)
+ dup2(fd, STDERR_FILENO);
+
+ if (fd > 2)
+ close(fd);
+ }
+
+ /* Count arguments */
+ va_start(ap, path);
+ for (n = 0; va_arg(ap, char*); n++)
+ ;
+ va_end(ap);
+
+ /* Allocate strv */
+ l = alloca(sizeof(char *) * (n + 1));
+
+ /* Fill in arguments */
+ va_start(ap, path);
+ for (i = 0; i <= n; i++)
+ l[i] = va_arg(ap, char*);
+ va_end(ap);
+
+ execv(path, l);
+ _exit(EXIT_FAILURE);
+}
+
+int setrlimit_closest(int resource, const struct rlimit *rlim) {
+ struct rlimit highest, fixed;
+
+ assert(rlim);
+
+ if (setrlimit(resource, rlim) >= 0)
+ return 0;
+
+ if (errno != EPERM)
+ return -errno;
+
+ /* So we failed to set the desired setrlimit, then let's try
+ * to get as close as we can */
+ assert_se(getrlimit(resource, &highest) == 0);
+
+ fixed.rlim_cur = MIN(rlim->rlim_cur, highest.rlim_max);
+ fixed.rlim_max = MIN(rlim->rlim_max, highest.rlim_max);
+
+ if (setrlimit(resource, &fixed) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int getenv_for_pid(pid_t pid, const char *field, char **_value) {
+ char path[sizeof("/proc/")-1+10+sizeof("/environ")], *value = NULL;
+ int r;
+ FILE *f;
+ bool done = false;
+ size_t l;
+
+ assert(field);
+ assert(_value);
+
+ if (pid == 0)
+ pid = getpid();
+
+ snprintf(path, sizeof(path), "/proc/%lu/environ", (unsigned long) pid);
+ char_array_0(path);
+
+ f = fopen(path, "re");
+ if (!f)
+ return -errno;
+
+ l = strlen(field);
+ r = 0;
+
+ do {
+ char line[LINE_MAX];
+ unsigned i;
+
+ for (i = 0; i < sizeof(line)-1; i++) {
+ int c;
+
+ c = getc(f);
+ if (_unlikely_(c == EOF)) {
+ done = true;
+ break;
+ } else if (c == 0)
+ break;
+
+ line[i] = c;
+ }
+ line[i] = 0;
+
+ if (memcmp(line, field, l) == 0 && line[l] == '=') {
+ value = strdup(line + l + 1);
+ if (!value) {
+ r = -ENOMEM;
+ break;
+ }
+
+ r = 1;
+ break;
+ }
+
+ } while (!done);
+
+ fclose(f);
+
+ if (r >= 0)
+ *_value = value;
+
+ return r;
+}
+
+int can_sleep(const char *type) {
+ char *w, *state;
+ size_t l, k;
+ int r;
+ _cleanup_free_ char *p = NULL;
+
+ assert(type);
+
+ r = read_one_line_file("/sys/power/state", &p);
+ if (r < 0)
+ return r == -ENOENT ? 0 : r;
+
+ k = strlen(type);
+ FOREACH_WORD_SEPARATOR(w, l, p, WHITESPACE, state)
+ if (l == k && memcmp(w, type, l) == 0)
+ return true;
+
+ return false;
+}
+
+bool is_valid_documentation_url(const char *url) {
+ assert(url);
+
+ if (startswith(url, "http://") && url[7])
+ return true;
+
+ if (startswith(url, "https://") && url[8])
+ return true;
+
+ if (startswith(url, "file:") && url[5])
+ return true;
+
+ if (startswith(url, "info:") && url[5])
+ return true;
+
+ if (startswith(url, "man:") && url[4])
+ return true;
+
+ return false;
+}
+
+bool in_initrd(void) {
+ static __thread int saved = -1;
+ struct statfs s;
+
+ if (saved >= 0)
+ return saved;
+
+ /* We make two checks here:
+ *
+ * 1. the flag file /etc/initrd-release must exist
+ * 2. the root file system must be a memory file system
+ *
+ * The second check is extra paranoia, since misdetecting an
+ * initrd can have bad bad consequences due the initrd
+ * emptying when transititioning to the main systemd.
+ */
+
+ saved = access("/etc/initrd-release", F_OK) >= 0 &&
+ statfs("/", &s) >= 0 &&
+ (s.f_type == TMPFS_MAGIC || s.f_type == RAMFS_MAGIC);
+
+ return saved;
+}
+
+void warn_melody(void) {
+ _cleanup_close_ int fd = -1;
+
+ fd = open("/dev/console", O_WRONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0)
+ return;
+
+ /* Yeah, this is synchronous. Kinda sucks. But well... */
+
+ ioctl(fd, KIOCSOUND, (int)(1193180/440));
+ usleep(125*USEC_PER_MSEC);
+
+ ioctl(fd, KIOCSOUND, (int)(1193180/220));
+ usleep(125*USEC_PER_MSEC);
+
+ ioctl(fd, KIOCSOUND, (int)(1193180/220));
+ usleep(125*USEC_PER_MSEC);
+
+ ioctl(fd, KIOCSOUND, 0);
+}
+
+int make_console_stdio(void) {
+ int fd, r;
+
+ /* Make /dev/console the controlling terminal and stdin/stdout/stderr */
+
+ fd = acquire_terminal("/dev/console", false, true, true, (usec_t) -1);
+ if (fd < 0) {
+ log_error("Failed to acquire terminal: %s", strerror(-fd));
+ return fd;
+ }
+
+ r = make_stdio(fd);
+ if (r < 0) {
+ log_error("Failed to duplicate terminal fd: %s", strerror(-r));
+ return r;
+ }
+
+ return 0;
+}
+
+int get_home_dir(char **_h) {
+ char *h;
+ const char *e;
+ uid_t u;
+ struct passwd *p;
+
+ assert(_h);
+
+ /* Take the user specified one */
+ e = getenv("HOME");
+ if (e) {
+ h = strdup(e);
+ if (!h)
+ return -ENOMEM;
+
+ *_h = h;
+ return 0;
+ }
+
+ /* Hardcode home directory for root to avoid NSS */
+ u = getuid();
+ if (u == 0) {
+ h = strdup("/root");
+ if (!h)
+ return -ENOMEM;
+
+ *_h = h;
+ return 0;
+ }
+
+ /* Check the database... */
+ errno = 0;
+ p = getpwuid(u);
+ if (!p)
+ return errno ? -errno : -ESRCH;
+
+ if (!path_is_absolute(p->pw_dir))
+ return -EINVAL;
+
+ h = strdup(p->pw_dir);
+ if (!h)
+ return -ENOMEM;
+
+ *_h = h;
+ return 0;
+}
+
+int get_shell(char **_sh) {
+ char *sh;
+ const char *e;
+ uid_t u;
+ struct passwd *p;
+
+ assert(_sh);
+
+ /* Take the user specified one */
+ e = getenv("SHELL");
+ if (e) {
+ sh = strdup(e);
+ if (!sh)
+ return -ENOMEM;
+
+ *_sh = sh;
+ return 0;
+ }
+
+ /* Hardcode home directory for root to avoid NSS */
+ u = getuid();
+ if (u == 0) {
+ sh = strdup("/bin/sh");
+ if (!sh)
+ return -ENOMEM;
+
+ *_sh = sh;
+ return 0;
+ }
+
+ /* Check the database... */
+ errno = 0;
+ p = getpwuid(u);
+ if (!p)
+ return errno ? -errno : -ESRCH;
+
+ if (!path_is_absolute(p->pw_shell))
+ return -EINVAL;
+
+ sh = strdup(p->pw_shell);
+ if (!sh)
+ return -ENOMEM;
+
+ *_sh = sh;
+ return 0;
+}
+
+void freep(void *p) {
+ free(*(void**) p);
+}
+
+void fclosep(FILE **f) {
+ if (*f)
+ fclose(*f);
+}
+
+void closep(int *fd) {
+ if (*fd >= 0)
+ close_nointr_nofail(*fd);
+}
+
+void closedirp(DIR **d) {
+ if (*d)
+ closedir(*d);
+}
+
+void umaskp(mode_t *u) {
+ umask(*u);
+}