X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Futil.c;h=b2baa1ba29aacb067bf02ad64d3d14badc87ea94;hp=f5ee29897f519d39071dd665ae650640025b6d3d;hb=0a27cf3f32403f48059396cb43ad25d0a12ef64b;hpb=e5ebf783cb8e353bf1e07b34ac344bd4883a4ec2 diff --git a/src/util.c b/src/util.c index f5ee29897..b2baa1ba2 100644 --- a/src/util.c +++ b/src/util.c @@ -59,6 +59,7 @@ #include "strv.h" #include "label.h" #include "exit-status.h" +#include "hashmap.h" bool streq_ptr(const char *a, const char *b) { @@ -250,7 +251,7 @@ int parse_boolean(const char *v) { } int parse_pid(const char *s, pid_t* ret_pid) { - unsigned long ul; + unsigned long ul = 0; pid_t pid; int r; @@ -495,6 +496,9 @@ int write_one_line_file(const char *fn, const char *line) { goto finish; } + if (!endswith(line, "\n")) + fputc('\n', f); + r = 0; finish: fclose(f); @@ -679,6 +683,73 @@ fail: return r; } +int load_env_file( + const char *fname, + char ***rl) { + + FILE *f; + char **m = 0; + int r; + + assert(fname); + assert(rl); + + if (!(f = fopen(fname, "re"))) + return -errno; + + while (!feof(f)) { + char l[LINE_MAX], *p, *u; + char **t; + + if (!fgets(l, sizeof(l), f)) { + if (feof(f)) + break; + + r = -errno; + goto finish; + } + + p = strstrip(l); + + if (!*p) + continue; + + if (strchr(COMMENTS, *p)) + continue; + + if (!(u = normalize_env_assignment(p))) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + t = strv_append(m, u); + free(u); + + if (!t) { + log_error("Out of memory"); + r = -ENOMEM; + goto finish; + } + + strv_free(m); + m = t; + } + + r = 0; + + *rl = m; + m = NULL; + +finish: + if (f) + fclose(f); + + strv_free(m); + + return r; +} + char *truncate_nl(char *s) { assert(s); @@ -1744,8 +1815,9 @@ int close_all_fds(const int except[], unsigned n_except) { if (ignore_file(de->d_name)) continue; - if ((r = safe_atoi(de->d_name, &fd)) < 0) - goto finish; + if (safe_atoi(de->d_name, &fd) < 0) + /* Let's better ignore this, just in case */ + continue; if (fd < 3) continue; @@ -1768,16 +1840,13 @@ int close_all_fds(const int except[], unsigned n_except) { continue; } - if ((r = close_nointr(fd)) < 0) { + if (close_nointr(fd) < 0) { /* Valgrind has its own FD and doesn't want to have it closed */ - if (errno != EBADF) - goto finish; + if (errno != EBADF && r == 0) + r = -errno; } } - r = 0; - -finish: closedir(d); return r; } @@ -2130,8 +2199,32 @@ finish: int open_terminal(const char *name, int mode) { int fd, r; + unsigned c = 0; + + /* + * If a TTY is in the process of being closed opening it might + * cause EIO. This is horribly awful, but unlikely to be + * changed in the kernel. Hence we work around this problem by + * retrying a couple of times. + * + * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245 + */ + + for (;;) { + if ((fd = open(name, mode)) >= 0) + break; + + if (errno != EIO) + return -errno; + + if (c >= 20) + return -errno; + + usleep(50 * USEC_PER_MSEC); + c++; + } - if ((fd = open(name, mode)) < 0) + if (fd < 0) return -errno; if ((r = isatty(fd)) < 0) { @@ -2605,31 +2698,13 @@ int make_stdio(int fd) { return 0; } -bool is_clean_exit(int code, int status) { +int make_null_stdio(void) { + int null_fd; - if (code == CLD_EXITED) - return status == 0; - - /* If a daemon does not implement handlers for some of the - * signals that's not considered an unclean shutdown */ - if (code == CLD_KILLED) - return - status == SIGHUP || - status == SIGINT || - status == SIGTERM || - status == SIGPIPE; - - return false; -} - -bool is_clean_exit_lsb(int code, int status) { - - if (is_clean_exit(code, status)) - return true; + if ((null_fd = open("/dev/null", O_RDWR|O_NOCTTY)) < 0) + return -errno; - return - code == CLD_EXITED && - (status == EXIT_NOTINSTALLED || status == EXIT_NOTCONFIGURED); + return make_stdio(null_fd); } bool is_device_path(const char *path) { @@ -2763,28 +2838,139 @@ char* getlogname_malloc(void) { return name; } -int getttyname_malloc(char **r) { - char path[PATH_MAX], *p, *c; +int getttyname_malloc(int fd, char **r) { + char path[PATH_MAX], *c; int k; assert(r); - if ((k = ttyname_r(STDIN_FILENO, path, sizeof(path))) != 0) + if ((k = ttyname_r(fd, path, sizeof(path))) != 0) return -k; char_array_0(path); - p = path; - if (startswith(path, "/dev/")) - p += 5; - - if (!(c = strdup(p))) + if (!(c = strdup(startswith(path, "/dev/") ? path + 5 : path))) return -ENOMEM; *r = c; return 0; } +int getttyname_harder(int fd, char **r) { + int k; + char *s; + + if ((k = getttyname_malloc(fd, &s)) < 0) + return k; + + if (streq(s, "tty")) { + free(s); + return get_ctty(r, NULL); + } + + *r = s; + return 0; +} + +int get_ctty_devnr(dev_t *d) { + int k; + char line[256], *p; + unsigned long ttynr; + FILE *f; + + if (!(f = fopen("/proc/self/stat", "r"))) + return -errno; + + if (!(fgets(line, sizeof(line), f))) { + k = -errno; + fclose(f); + return k; + } + + fclose(f); + + if (!(p = strrchr(line, ')'))) + return -EIO; + + p++; + + if (sscanf(p, " " + "%*c " /* state */ + "%*d " /* ppid */ + "%*d " /* pgrp */ + "%*d " /* session */ + "%lu ", /* ttynr */ + &ttynr) != 1) + return -EIO; + + *d = (dev_t) ttynr; + return 0; +} + +int get_ctty(char **r, dev_t *_devnr) { + int k; + char fn[128], *s, *b, *p; + dev_t devnr; + + assert(r); + + if ((k = get_ctty_devnr(&devnr)) < 0) + return k; + + snprintf(fn, sizeof(fn), "/dev/char/%u:%u", major(devnr), minor(devnr)); + char_array_0(fn); + + if ((k = readlink_malloc(fn, &s)) < 0) { + + if (k != -ENOENT) + return k; + + /* This is an ugly hack */ + if (major(devnr) == 136) { + if (asprintf(&b, "pts/%lu", (unsigned long) minor(devnr)) < 0) + return -ENOMEM; + + *r = b; + if (_devnr) + *_devnr = devnr; + + return 0; + } + + /* Probably something like the ptys which have no + * symlink in /dev/char. Let's return something + * vaguely useful. */ + + if (!(b = strdup(fn + 5))) + return -ENOMEM; + + *r = b; + if (_devnr) + *_devnr = devnr; + + return 0; + } + + if (startswith(s, "/dev/")) + p = s + 5; + else if (startswith(s, "../")) + p = s + 3; + else + p = s; + + b = strdup(p); + free(s); + + if (!b) + return -ENOMEM; + + *r = b; + if (_devnr) + *_devnr = devnr; + + return 0; +} + static int rm_rf_children(int fd, bool only_dirs) { DIR *d; int ret = 0; @@ -2970,80 +3156,158 @@ void status_printf(const char *format, ...) { } void status_welcome(void) { + char *pretty_name = NULL, *ansi_color = NULL; + const char *const_pretty = NULL, *const_color = NULL; + int r; -#if defined(TARGET_FEDORA) - char *r; + if ((r = parse_env_file("/etc/os-release", NEWLINE, + "PRETTY_NAME", &pretty_name, + "ANSI_COLOR", &ansi_color, + NULL)) < 0) { - if (read_one_line_file("/etc/system-release", &r) < 0) - return; + if (r != -ENOENT) + log_warning("Failed to read /etc/os-release: %s", strerror(-r)); + } - truncate_nl(r); +#if defined(TARGET_FEDORA) + if (!pretty_name) { + if ((r = read_one_line_file("/etc/system-release", &pretty_name)) < 0) { - /* This tries to mimic the color magic the old Red Hat sysinit - * script did. */ + if (r != -ENOENT) + log_warning("Failed to read /etc/system-release: %s", strerror(-r)); + } else + truncate_nl(pretty_name); + } - if (startswith(r, "Red Hat")) - status_printf("Welcome to \x1B[0;31m%s\x1B[0m!\n", r); /* Red for RHEL */ - else if (startswith(r, "Fedora")) - status_printf("Welcome to \x1B[0;34m%s\x1B[0m!\n", r); /* Blue for Fedora */ - else - status_printf("Welcome to %s!\n", r); + if (!ansi_color && pretty_name) { - free(r); + /* This tries to mimic the color magic the old Red Hat sysinit + * script did. */ + + if (startswith(pretty_name, "Red Hat")) + const_color = "0;31"; /* Red for RHEL */ + else if (startswith(pretty_name, "Fedora")) + const_color = "0;34"; /* Blue for Fedora */ + } #elif defined(TARGET_SUSE) - char *r; - if (read_one_line_file("/etc/SuSE-release", &r) < 0) - return; + if (!pretty_name) { + if ((r = read_one_line_file("/etc/SuSE-release", &pretty_name)) < 0) { - truncate_nl(r); + if (r != -ENOENT) + log_warning("Failed to read /etc/SuSE-release: %s", strerror(-r)); + } else + truncate_nl(pretty_name); + } - status_printf("Welcome to \x1B[0;32m%s\x1B[0m!\n", r); /* Green for SUSE */ - free(r); + if (!ansi_color) + const_color = "0;32"; /* Green for openSUSE */ #elif defined(TARGET_GENTOO) - char *r; - if (read_one_line_file("/etc/gentoo-release", &r) < 0) - return; + if (!pretty_name) { + if ((r = read_one_line_file("/etc/gentoo-release", &pretty_name)) < 0) { - truncate_nl(r); + if (r != -ENOENT) + log_warning("Failed to read /etc/gentoo-release: %s", strerror(-r)); + } else + truncate_nl(pretty_name); + } - status_printf("Welcome to \x1B[1;34m%s\x1B[0m!\n", r); /* Light Blue for Gentoo */ + if (!ansi_color) + const_color = "1;34"; /* Light Blue for Gentoo */ + +#elif defined(TARGET_ALTLINUX) + + if (!pretty_name) { + if ((r = read_one_line_file("/etc/altlinux-release", &pretty_name)) < 0) { + + if (r != -ENOENT) + log_warning("Failed to read /etc/altlinux-release: %s", strerror(-r)); + } else + truncate_nl(pretty_name); + } + + if (!ansi_color) + const_color = "0;36"; /* Cyan for ALTLinux */ - free(r); #elif defined(TARGET_DEBIAN) - char *r; - if (read_one_line_file("/etc/debian_version", &r) < 0) - return; + if (!pretty_name) { + char *version; - truncate_nl(r); + if ((r = read_one_line_file("/etc/debian_version", &version)) < 0) { - status_printf("Welcome to Debian \x1B[1;31m%s\x1B[0m!\n", r); /* Light Red for Debian */ + if (r != -ENOENT) + log_warning("Failed to read /etc/debian_version: %s", strerror(-r)); + } else { + truncate_nl(version); + pretty_name = strappend("Debian ", version); + free(version); + + if (!pretty_name) + log_warning("Failed to allocate Debian version string."); + } + } + + if (!ansi_color) + const_color = "1;31"; /* Light Red for Debian */ - free(r); #elif defined(TARGET_UBUNTU) - char *desc = NULL; - char *codename = NULL; - if (parse_env_file("/etc/lsb-release", NEWLINE, - "DISTRIB_DESCRIPTION", &desc, - "DISTRIB_CODENAME", &codename, NULL) < 0) - return; - if (desc && codename) - /* Light Red for Ubuntu */ - status_printf("Welcome to \x1B[1;31m%s\x1B[0m (%s)\n", - desc, codename); - free(desc); - free(codename); -#elif defined(TARGET_ARCH) - status_printf("Welcome to \x1B[1;36mArch Linux\x1B[0m!\n"); /* Cyan for Arch */ -#else -#warning "You probably should add a welcome text logic here." + if ((r = parse_env_file("/etc/lsb-release", NEWLINE, + "DISTRIB_DESCRIPTION", &pretty_name, + NULL)) < 0) { + + if (r != -ENOENT) + log_warning("Failed to read /etc/lsb-release: %s", strerror(-r)); + } + + if (!ansi_color) + const_color = "0;33"; /* Orange/Brown for Ubuntu */ + +#elif defined(TARGET_MANDRIVA) + + if (!pretty_name) { + char *s, *p; + + if ((r = read_one_line_file("/etc/mandriva-release", &s) < 0)) { + if (r != -ENOENT) + log_warning("Failed to read /etc/mandriva-release: %s", strerror(-r)); + } else { + p = strstr(s, " release "); + if (p) { + *p = '\0'; + p += 9; + p[strcspn(p, " ")] = '\0'; + + /* This corresponds to standard rc.sysinit */ + if (asprintf(&pretty_name, "%s\x1B[0;39m %s", s, p) > 0) + const_color = "1;36"; + else + log_warning("Failed to allocate Mandriva version string."); + } else + log_warning("Failed to parse /etc/mandriva-release"); + free(s); + } + } + #endif + + if (!pretty_name && !const_pretty) + const_pretty = "Linux"; + + if (!ansi_color && !const_color) + const_color = "1"; + + status_printf("\nWelcome to \x1B[%sm%s\x1B[0m!\n\n", + const_color ? const_color : ansi_color, + const_pretty ? const_pretty : pretty_name); + + free(ansi_color); + free(pretty_name); } char *replace_env(const char *format, char **env) { @@ -3280,6 +3544,44 @@ char *unquote(const char *s, const char* quotes) { return strdup(s); } +char *normalize_env_assignment(const char *s) { + char *name, *value, *p, *r; + + p = strchr(s, '='); + + if (!p) { + if (!(r = strdup(s))) + return NULL; + + return strstrip(r); + } + + if (!(name = strndup(s, p - s))) + return NULL; + + if (!(p = strdup(p+1))) { + free(name); + return NULL; + } + + value = unquote(strstrip(p), QUOTES); + free(p); + + if (!value) { + free(p); + free(name); + return NULL; + } + + if (asprintf(&r, "%s=%s", name, value) < 0) + r = NULL; + + free(value); + free(name); + + return r; +} + int wait_for_terminate(pid_t pid, siginfo_t *status) { assert(pid >= 1); assert(status); @@ -3314,7 +3616,7 @@ int wait_for_terminate_and_warn(const char *name, pid_t pid) { if (status.si_code == CLD_EXITED) { if (status.si_status != 0) { log_warning("%s failed with error code %i.", name, status.si_status); - return -EPROTO; + return status.si_status; } log_debug("%s succeeded.", name); @@ -3333,6 +3635,12 @@ int wait_for_terminate_and_warn(const char *name, pid_t pid) { } void freeze(void) { + + /* Make sure nobody waits for us on a socket anymore */ + close_all_fds(NULL, 0); + + sync(); + for (;;) pause(); } @@ -3349,8 +3657,19 @@ bool null_or_empty(struct stat *st) { return false; } -DIR *xopendirat(int fd, const char *name) { - return fdopendir(openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)); +DIR *xopendirat(int fd, const char *name, int flags) { + int nfd; + DIR *d; + + if ((nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags)) < 0) + return NULL; + + if (!(d = fdopendir(nfd))) { + close_nointr_nofail(nfd); + return NULL; + } + + return d; } int signal_from_string_try_harder(const char *s) { @@ -3364,170 +3683,435 @@ int signal_from_string_try_harder(const char *s) { return signo; } -int ask_password_tty(const char *message, usec_t until, const char *flag_file, char **_passphrase) { - struct termios old_termios, new_termios; - char passphrase[LINE_MAX]; - size_t p = 0; - int r, ttyfd = -1, notify = -1; - struct pollfd pollfd[2]; - bool reset_tty = false; - enum { - POLL_TTY, - POLL_INOTIFY - }; +void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) { - assert(message); - assert(_passphrase); + assert(f); + assert(name); + assert(t); - if (flag_file) { - if ((notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) { - r = -errno; - goto finish; - } + if (!dual_timestamp_is_set(t)) + return; - if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0) { - r = -errno; - goto finish; - } + fprintf(f, "%s=%llu %llu\n", + name, + (unsigned long long) t->realtime, + (unsigned long long) t->monotonic); +} + +void dual_timestamp_deserialize(const char *value, dual_timestamp *t) { + unsigned long long a, b; + + assert(value); + assert(t); + + if (sscanf(value, "%lli %llu", &a, &b) != 2) + log_debug("Failed to parse finish timestamp value %s", value); + else { + t->realtime = a; + t->monotonic = b; } +} - if ((ttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC)) >= 0) { +char *fstab_node_to_udev_node(const char *p) { + char *dn, *t, *u; + int r; - if (tcgetattr(ttyfd, &old_termios) < 0) { - r = -errno; - goto finish; - } + /* FIXME: to follow udev's logic 100% we need to leave valid + * UTF8 chars unescaped */ - loop_write(ttyfd, "\x1B[1m", 4, false); - loop_write(ttyfd, message, strlen(message), false); - loop_write(ttyfd, ": ", 2, false); - loop_write(ttyfd, "\x1B[0m", 4, false); + if (startswith(p, "LABEL=")) { - new_termios = old_termios; - new_termios.c_lflag &= ~(ICANON|ECHO); - new_termios.c_cc[VMIN] = 1; - new_termios.c_cc[VTIME] = 0; + if (!(u = unquote(p+6, "\"\'"))) + return NULL; - if (tcsetattr(ttyfd, TCSADRAIN, &new_termios) < 0) { - r = -errno; - goto finish; - } + t = xescape(u, "/ "); + free(u); + + if (!t) + return NULL; - reset_tty = true; + r = asprintf(&dn, "/dev/disk/by-label/%s", t); + free(t); + + if (r < 0) + return NULL; + + return dn; } - zero(pollfd); + if (startswith(p, "UUID=")) { - pollfd[POLL_TTY].fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO; - pollfd[POLL_TTY].events = POLLIN; - pollfd[POLL_INOTIFY].fd = notify; - pollfd[POLL_INOTIFY].events = POLLIN; + if (!(u = unquote(p+5, "\"\'"))) + return NULL; - for (;;) { - char c; - int sleep_for = -1, k; - ssize_t n; + t = xescape(u, "/ "); + free(u); - if (until > 0) { - usec_t y; + if (!t) + return NULL; - y = now(CLOCK_MONOTONIC); + r = asprintf(&dn, "/dev/disk/by-uuid/%s", t); + free(t); - if (y > until) { - r = -ETIMEDOUT; - goto finish; - } + if (r < 0) + return NULL; - sleep_for = (int) ((until - y) / USEC_PER_MSEC); - } + return dn; + } - if (flag_file) - if (access(flag_file, F_OK) < 0) { - r = -errno; - goto finish; - } + return strdup(p); +} - if ((k = poll(pollfd, notify > 0 ? 2 : 1, sleep_for)) < 0) { +void filter_environ(const char *prefix) { + int i, j; + assert(prefix); - if (errno == EINTR) - continue; + if (!environ) + return; - r = -errno; - goto finish; - } else if (k == 0) { - r = -ETIMEDOUT; - goto finish; + for (i = 0, j = 0; environ[i]; i++) { + + if (startswith(environ[i], prefix)) + continue; + + environ[j++] = environ[i]; + } + + environ[j] = NULL; +} + +bool tty_is_vc(const char *tty) { + assert(tty); + + if (startswith(tty, "/dev/")) + tty += 5; + + return startswith(tty, "tty") && + tty[3] >= '0' && tty[3] <= '9'; +} + +const char *default_term_for_tty(const char *tty) { + char *active = NULL; + const char *term; + + assert(tty); + + if (startswith(tty, "/dev/")) + tty += 5; + + /* Resolve where /dev/console is pointing when determining + * TERM */ + if (streq(tty, "console")) + if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) { + truncate_nl(active); + + /* If multiple log outputs are configured the + * last one is what /dev/console points to */ + if ((tty = strrchr(active, ' '))) + tty++; + else + tty = active; } - if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0) - flush_fd(notify); + term = tty_is_vc(tty) ? "TERM=linux" : "TERM=vt100"; + free(active); - if (pollfd[POLL_TTY].revents == 0) - continue; + return term; +} - if ((n = read(ttyfd >= 0 ? ttyfd : STDIN_FILENO, &c, 1)) < 0) { +/* Returns a short identifier for the various VM implementations */ +int detect_vm(const char **id) { - if (errno == EINTR || errno == EAGAIN) - continue; +#if defined(__i386__) || defined(__x86_64__) - r = -errno; - goto finish; + /* Both CPUID and DMI are x86 specific interfaces... */ - } else if (n == 0) - break; + static const char *const dmi_vendors[] = { + "/sys/class/dmi/id/sys_vendor", + "/sys/class/dmi/id/board_vendor", + "/sys/class/dmi/id/bios_vendor" + }; - if (c == '\n') - break; - else if (c == 21) { + static const char dmi_vendor_table[] = + "QEMU\0" "qemu\0" + /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */ + "VMware\0" "vmware\0" + "VMW\0" "vmware\0" + "Microsoft Corporation\0" "microsoft\0" + "innotek GmbH\0" "oracle\0" + "Xen\0" "xen\0" + "Bochs\0" "bochs\0"; + + static const char cpuid_vendor_table[] = + "XenVMMXenVMM\0" "xen\0" + "KVMKVMKVM\0" "kvm\0" + /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */ + "VMwareVMware\0" "vmware\0" + /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */ + "Microsoft Hv\0" "microsoft\0"; + + uint32_t eax, ecx; + union { + uint32_t sig32[3]; + char text[13]; + } sig; + unsigned i; + const char *j, *k; + bool hypervisor; + + /* http://lwn.net/Articles/301888/ */ + zero(sig); + +#if defined (__i386__) +#define REG_a "eax" +#define REG_b "ebx" +#elif defined (__amd64__) +#define REG_a "rax" +#define REG_b "rbx" +#endif - while (p > 0) { - p--; + /* First detect whether there is a hypervisor */ + eax = 1; + __asm__ __volatile__ ( + /* ebx/rbx is being used for PIC! */ + " push %%"REG_b" \n\t" + " cpuid \n\t" + " pop %%"REG_b" \n\t" - if (ttyfd >= 0) - loop_write(ttyfd, "\b \b", 3, false); - } + : "=a" (eax), "=c" (ecx) + : "0" (eax) + ); + + hypervisor = !!(ecx & ecx & 0x80000000U); + + if (hypervisor) { + + /* There is a hypervisor, see what it is */ + eax = 0x40000000U; + __asm__ __volatile__ ( + /* ebx/rbx is being used for PIC! */ + " push %%"REG_b" \n\t" + " cpuid \n\t" + " mov %%ebx, %1 \n\t" + " pop %%"REG_b" \n\t" + + : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2]) + : "0" (eax) + ); + + NULSTR_FOREACH_PAIR(j, k, cpuid_vendor_table) + if (streq(sig.text, j)) { - } else if (c == '\b' || c == 127) { - if (p > 0) { - p--; + if (id) + *id = k; - if (ttyfd >= 0) - loop_write(ttyfd, "\b \b", 3, false); + return 1; } - } else { - passphrase[p++] = c; + } + + for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) { + char *s; + int r; + const char *found = NULL; + + if ((r = read_one_line_file(dmi_vendors[i], &s)) < 0) { + if (r != -ENOENT) + return r; + + continue; + } + + NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table) + if (startswith(s, j)) + found = k; + free(s); + + if (found) { + if (id) + *id = found; - if (ttyfd >= 0) - loop_write(ttyfd, "*", 1, false); + return 1; } } - if (ttyfd >= 0) - loop_write(ttyfd, "\n", 1, false); + if (hypervisor) { + if (id) + *id = "other"; - passphrase[p] = 0; + return 1; + } - if (!(*_passphrase = strdup(passphrase))) { - r = -ENOMEM; +#endif + return 0; +} + +/* Returns a short identifier for the various VM/container implementations */ +int detect_virtualization(const char **id) { + int r; + + /* Unfortunately most of these operations require root access + * in one way or another */ + if (geteuid() != 0) + return -EPERM; + + if ((r = running_in_chroot()) > 0) { + if (id) + *id = "chroot"; + + return r; + } + + /* /proc/vz exists in container and outside of the container, + * /proc/bc only outside of the container. */ + if (access("/proc/vz", F_OK) >= 0 && + access("/proc/bc", F_OK) < 0) { + + if (id) + *id = "openvz"; + + return 1; + } + + return detect_vm(id); +} + +void execute_directory(const char *directory, DIR *d, char *argv[]) { + DIR *_d = NULL; + struct dirent *de; + Hashmap *pids = NULL; + + assert(directory); + + /* Executes all binaries in a directory in parallel and waits + * until all they all finished. */ + + if (!d) { + if (!(_d = opendir(directory))) { + + if (errno == ENOENT) + return; + + log_error("Failed to enumerate directory %s: %m", directory); + return; + } + + d = _d; + } + + if (!(pids = hashmap_new(trivial_hash_func, trivial_compare_func))) { + log_error("Failed to allocate set."); goto finish; } - r = 0; + while ((de = readdir(d))) { + char *path; + pid_t pid; + int k; -finish: - if (notify >= 0) - close_nointr_nofail(notify); + if (ignore_file(de->d_name)) + continue; + + if (de->d_type != DT_REG && + de->d_type != DT_LNK && + de->d_type != DT_UNKNOWN) + continue; + + if (asprintf(&path, "%s/%s", directory, de->d_name) < 0) { + log_error("Out of memory"); + continue; + } + + if ((pid = fork()) < 0) { + log_error("Failed to fork: %m"); + free(path); + continue; + } + + if (pid == 0) { + char *_argv[2]; + /* Child */ + + if (!argv) { + _argv[0] = path; + _argv[1] = NULL; + argv = _argv; + } else + if (!argv[0]) + argv[0] = path; + + execv(path, argv); + + log_error("Failed to execute %s: %m", path); + _exit(EXIT_FAILURE); + } + + log_debug("Spawned %s as %lu", path, (unsigned long) pid); + + if ((k = hashmap_put(pids, UINT_TO_PTR(pid), path)) < 0) { + log_error("Failed to add PID to set: %s", strerror(-k)); + free(path); + } + } + + while (!hashmap_isempty(pids)) { + siginfo_t si; + char *path; + + zero(si); + if (waitid(P_ALL, 0, &si, WEXITED) < 0) { - if (ttyfd >= 0) { - if (reset_tty) - tcsetattr(ttyfd, TCSADRAIN, &old_termios); + if (errno == EINTR) + continue; - close_nointr_nofail(ttyfd); + log_error("waitid() failed: %m"); + goto finish; + } + + if ((path = hashmap_remove(pids, UINT_TO_PTR(si.si_pid)))) { + if (!is_clean_exit(si.si_code, si.si_status)) { + if (si.si_code == CLD_EXITED) + log_error("%s exited with exit status %i.", path, si.si_status); + else + log_error("%s terminated by signal %s.", path, signal_to_string(si.si_status)); + } else + log_debug("%s exited successfully.", path); + + free(path); + } } +finish: + if (_d) + closedir(_d); + + if (pids) + hashmap_free_free(pids); +} + +int kill_and_sigcont(pid_t pid, int sig) { + int r; + + r = kill(pid, sig) < 0 ? -errno : 0; + + if (r >= 0) + kill(pid, SIGCONT); + return r; } +bool nulstr_contains(const char*nulstr, const char *needle) { + const char *i; + + if (!nulstr) + return false; + + NULSTR_FOREACH(i, nulstr) + if (streq(i, needle)) + return true; + + return false; +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime",