int main(int argc, char *argv[]) { static const char * const cmdline[] = { QUOTACHECK, "-anug", NULL }; pid_t pid; int r; if (argc > 1) { log_error("This program takes no arguments."); return EXIT_FAILURE; } log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); umask(0022); r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0); if (r < 0) log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); test_files(); if (!arg_force) { if (arg_skip) return EXIT_SUCCESS; if (access("/run/systemd/quotacheck", F_OK) < 0) return EXIT_SUCCESS; } pid = fork(); if (pid < 0) { r = log_error_errno(errno, "fork(): %m"); goto finish; } if (pid == 0) { /* Child */ (void) reset_all_signal_handlers(); (void) reset_signal_mask(); assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); execv(cmdline[0], (char**) cmdline); _exit(1); /* Operational error */ } r = wait_for_terminate_and_warn("quotacheck", pid, true); finish: return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; }
int show_man_page(const char *desc, bool null_stdio) { const char *args[4] = { "man", NULL, NULL, NULL }; char *e = NULL; pid_t pid; size_t k; int r; siginfo_t status; k = strlen(desc); if (desc[k-1] == ')') e = strrchr(desc, '('); if (e) { char *page = NULL, *section = NULL; page = strndupa(desc, e - desc); section = strndupa(e + 1, desc + k - e - 2); args[1] = section; args[2] = page; } else args[1] = desc; pid = fork(); if (pid < 0) return log_error_errno(errno, "Failed to fork: %m"); if (pid == 0) { /* Child */ (void) reset_all_signal_handlers(); (void) reset_signal_mask(); if (null_stdio) { r = make_null_stdio(); if (r < 0) { log_error_errno(r, "Failed to kill stdio: %m"); _exit(EXIT_FAILURE); } } execvp(args[0], (char**) args); log_error_errno(errno, "Failed to execute man: %m"); _exit(EXIT_FAILURE); } r = wait_for_terminate(pid, &status); if (r < 0) return r; log_debug("Exit code %i status %i", status.si_code, status.si_status); return status.si_status; }
static int spawn_getent(const char *database, const char *key, pid_t *rpid) { int pipe_fds[2]; pid_t pid; assert(database); assert(key); assert(rpid); if (pipe2(pipe_fds, O_CLOEXEC) < 0) return log_error_errno(errno, "Failed to allocate pipe: %m"); pid = fork(); if (pid < 0) return log_error_errno(errno, "Failed to fork getent child: %m"); else if (pid == 0) { int nullfd; char *empty_env = NULL; if (dup3(pipe_fds[1], STDOUT_FILENO, 0) < 0) _exit(EXIT_FAILURE); if (pipe_fds[0] > 2) safe_close(pipe_fds[0]); if (pipe_fds[1] > 2) safe_close(pipe_fds[1]); nullfd = open("/dev/null", O_RDWR); if (nullfd < 0) _exit(EXIT_FAILURE); if (dup3(nullfd, STDIN_FILENO, 0) < 0) _exit(EXIT_FAILURE); if (dup3(nullfd, STDERR_FILENO, 0) < 0) _exit(EXIT_FAILURE); if (nullfd > 2) safe_close(nullfd); (void) reset_all_signal_handlers(); (void) reset_signal_mask(); close_all_fds(NULL, 0); execle("/usr/bin/getent", "getent", database, key, NULL, &empty_env); execle("/bin/getent", "getent", database, key, NULL, &empty_env); _exit(EXIT_FAILURE); } pipe_fds[1] = safe_close(pipe_fds[1]); *rpid = pid; return pipe_fds[0]; }
static int found_override(const char *top, const char *bottom) { _cleanup_free_ char *dest = NULL; int k; pid_t pid; assert(top); assert(bottom); if (null_or_empty_path(top) > 0) return notify_override_masked(top, bottom); k = readlink_malloc(top, &dest); if (k >= 0) { if (equivalent(dest, bottom) > 0) return notify_override_equivalent(top, bottom); else return notify_override_redirected(top, bottom); } k = notify_override_overridden(top, bottom); if (!arg_diff) return k; putchar('\n'); fflush(stdout); pid = fork(); if (pid < 0) return log_error_errno(errno, "Failed to fork off diff: %m"); else if (pid == 0) { (void) reset_all_signal_handlers(); (void) reset_signal_mask(); assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); execlp("diff", "diff", "-us", "--", bottom, top, NULL); log_error_errno(errno, "Failed to execute diff: %m"); _exit(EXIT_FAILURE); } wait_for_terminate_and_warn("diff", pid, false); putchar('\n'); return k; }
static int fork_wait(const char* const cmdline[]) { pid_t pid; pid = fork(); if (pid < 0) return log_error_errno(errno, "fork(): %m"); if (pid == 0) { /* Child */ (void) reset_all_signal_handlers(); (void) reset_signal_mask(); assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); execv(cmdline[0], (char**) cmdline); log_error_errno(errno, "Failed to execute %s: %m", cmdline[0]); _exit(EXIT_FAILURE); /* Operational error */ } return wait_for_terminate_and_warn(cmdline[0], pid, false); }
static int fsck_progress_socket(void) { static const union sockaddr_union sa = { .un.sun_family = AF_UNIX, .un.sun_path = "/run/systemd/fsck.progress", }; int fd, r; fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) return log_warning_errno(errno, "socket(): %m"); if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { r = log_full_errno(errno == ECONNREFUSED || errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno, "Failed to connect to progress socket %s, ignoring: %m", sa.un.sun_path); safe_close(fd); return r; } return fd; } int main(int argc, char *argv[]) { _cleanup_close_pair_ int progress_pipe[2] = { -1, -1 }; _cleanup_(sd_device_unrefp) sd_device *dev = NULL; const char *device, *type; bool root_directory; siginfo_t status; struct stat st; int r; pid_t pid; if (argc > 2) { log_error("This program expects one or no arguments."); return EXIT_FAILURE; } log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); umask(0022); r = parse_proc_cmdline(parse_proc_cmdline_item); if (r < 0) log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m"); test_files(); if (!arg_force && arg_skip) { r = 0; goto finish; } if (argc > 1) { device = argv[1]; if (stat(device, &st) < 0) { r = log_error_errno(errno, "Failed to stat %s: %m", device); goto finish; } if (!S_ISBLK(st.st_mode)) { log_error("%s is not a block device.", device); r = -EINVAL; goto finish; } r = sd_device_new_from_devnum(&dev, 'b', st.st_rdev); if (r < 0) { log_error_errno(r, "Failed to detect device %s: %m", device); goto finish; } root_directory = false; } else { struct timespec times[2]; /* Find root device */ if (stat("/", &st) < 0) { r = log_error_errno(errno, "Failed to stat() the root directory: %m"); goto finish; } /* Virtual root devices don't need an fsck */ if (major(st.st_dev) == 0) { log_debug("Root directory is virtual or btrfs, skipping check."); r = 0; goto finish; } /* check if we are already writable */ times[0] = st.st_atim; times[1] = st.st_mtim; if (utimensat(AT_FDCWD, "/", times, 0) == 0) { log_info("Root directory is writable, skipping check."); r = 0; goto finish; } r = sd_device_new_from_devnum(&dev, 'b', st.st_dev); if (r < 0) { log_error_errno(r, "Failed to detect root device: %m"); goto finish; } r = sd_device_get_devname(dev, &device); if (r < 0) { log_error_errno(r, "Failed to detect device node of root directory: %m"); goto finish; } root_directory = true; } r = sd_device_get_property_value(dev, "ID_FS_TYPE", &type); if (r >= 0) { r = fsck_exists(type); if (r < 0) log_warning_errno(r, "Couldn't detect if fsck.%s may be used for %s, proceeding: %m", type, device); else if (r == 0) { log_info("fsck.%s doesn't exist, not checking file system on %s.", type, device); goto finish; } } if (arg_show_progress) { if (pipe(progress_pipe) < 0) { r = log_error_errno(errno, "pipe(): %m"); goto finish; } } pid = fork(); if (pid < 0) { r = log_error_errno(errno, "fork(): %m"); goto finish; } if (pid == 0) { char dash_c[sizeof("-C")-1 + DECIMAL_STR_MAX(int) + 1]; int progress_socket = -1; const char *cmdline[9]; int i = 0; /* Child */ (void) reset_all_signal_handlers(); (void) reset_signal_mask(); assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); /* Close the reading side of the progress pipe */ progress_pipe[0] = safe_close(progress_pipe[0]); /* Try to connect to a progress management daemon, if there is one */ progress_socket = fsck_progress_socket(); if (progress_socket >= 0) { /* If this worked we close the progress pipe early, and just use the socket */ progress_pipe[1] = safe_close(progress_pipe[1]); xsprintf(dash_c, "-C%i", progress_socket); } else if (progress_pipe[1] >= 0) { /* Otherwise if we have the progress pipe to our own local handle, we use it */ xsprintf(dash_c, "-C%i", progress_pipe[1]); } else dash_c[0] = 0; cmdline[i++] = "/sbin/fsck"; cmdline[i++] = arg_repair; cmdline[i++] = "-T"; /* * Since util-linux v2.25 fsck uses /run/fsck/<diskname>.lock files. * The previous versions use flock for the device and conflict with * udevd, see https://bugs.freedesktop.org/show_bug.cgi?id=79576#c5 */ cmdline[i++] = "-l"; if (!root_directory) cmdline[i++] = "-M"; if (arg_force) cmdline[i++] = "-f"; if (!isempty(dash_c)) cmdline[i++] = dash_c; cmdline[i++] = device; cmdline[i++] = NULL; execv(cmdline[0], (char**) cmdline); _exit(FSCK_OPERATIONAL_ERROR); } progress_pipe[1] = safe_close(progress_pipe[1]); (void) process_progress(progress_pipe[0]); progress_pipe[0] = -1; r = wait_for_terminate(pid, &status); if (r < 0) { log_error_errno(r, "waitid(): %m"); goto finish; } if (status.si_code != CLD_EXITED || (status.si_status & ~1)) { if (status.si_code == CLD_KILLED || status.si_code == CLD_DUMPED) log_error("fsck terminated by signal %s.", signal_to_string(status.si_status)); else if (status.si_code == CLD_EXITED) log_error("fsck failed with error code %i.", status.si_status); else log_error("fsck failed due to unknown reason."); r = -EINVAL; if (status.si_code == CLD_EXITED && (status.si_status & FSCK_SYSTEM_SHOULD_REBOOT) && root_directory) /* System should be rebooted. */ start_target(SPECIAL_REBOOT_TARGET, "replace-irreversibly"); else if (status.si_code == CLD_EXITED && (status.si_status & (FSCK_SYSTEM_SHOULD_REBOOT | FSCK_ERRORS_LEFT_UNCORRECTED))) /* Some other problem */ start_target(SPECIAL_EMERGENCY_TARGET, "replace"); else { log_warning("Ignoring error."); r = 0; } } else r = 0; if (status.si_code == CLD_EXITED && (status.si_status & FSCK_ERROR_CORRECTED)) (void) touch("/run/systemd/quotacheck"); finish: return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; }
int pager_open(bool no_pager, bool jump_to_end) { _cleanup_close_pair_ int fd[2] = { -1, -1 }; const char *pager; pid_t parent_pid; if (no_pager) return 0; if (pager_pid > 0) return 1; if (terminal_is_dumb()) return 0; pager = getenv("SYSTEMD_PAGER"); if (!pager) pager = getenv("PAGER"); /* If the pager is explicitly turned off, honour it */ if (pager && STR_IN_SET(pager, "", "cat")) return 0; /* Determine and cache number of columns before we spawn the * pager so that we get the value from the actual tty */ (void) columns(); if (pipe(fd) < 0) return log_error_errno(errno, "Failed to create pager pipe: %m"); parent_pid = getpid(); pager_pid = fork(); if (pager_pid < 0) return log_error_errno(errno, "Failed to fork pager: %m"); /* In the child start the pager */ if (pager_pid == 0) { const char* less_opts, *less_charset; (void) reset_all_signal_handlers(); (void) reset_signal_mask(); (void) dup2(fd[0], STDIN_FILENO); safe_close_pair(fd); /* Initialize a good set of less options */ less_opts = getenv("SYSTEMD_LESS"); if (!less_opts) less_opts = "FRSXMK"; if (jump_to_end) less_opts = strjoina(less_opts, " +G"); setenv("LESS", less_opts, 1); /* Initialize a good charset for less. This is * particularly important if we output UTF-8 * characters. */ less_charset = getenv("SYSTEMD_LESSCHARSET"); if (!less_charset && is_locale_utf8()) less_charset = "utf-8"; if (less_charset) setenv("LESSCHARSET", less_charset, 1); /* Make sure the pager 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); if (pager) { execlp(pager, pager, NULL); execl("/bin/sh", "sh", "-c", pager, NULL); } /* Debian's alternatives command for pagers is * called 'pager'. Note that we do not call * sensible-pagers here, since that is just a * shell script that implements a logic that * is similar to this one anyway, but is * Debian-specific. */ execlp("pager", "pager", NULL); execlp("less", "less", NULL); execlp("more", "more", NULL); pager_fallback(); /* not reached */ } /* Return in the parent */ if (dup2(fd[1], STDOUT_FILENO) < 0) return log_error_errno(errno, "Failed to duplicate pager pipe: %m"); if (dup2(fd[1], STDERR_FILENO) < 0) return log_error_errno(errno, "Failed to duplicate pager pipe: %m"); return 1; }
int import_fork_tar_x(const char *path, pid_t *ret) { _cleanup_close_pair_ int pipefd[2] = { -1, -1 }; pid_t pid; int r; assert(path); assert(ret); if (pipe2(pipefd, O_CLOEXEC) < 0) return log_error_errno(errno, "Failed to create pipe for tar: %m"); pid = fork(); if (pid < 0) return log_error_errno(errno, "Failed to fork off tar: %m"); if (pid == 0) { int null_fd; uint64_t retain = (1ULL << CAP_CHOWN) | (1ULL << CAP_FOWNER) | (1ULL << CAP_FSETID) | (1ULL << CAP_MKNOD) | (1ULL << CAP_SETFCAP) | (1ULL << CAP_DAC_OVERRIDE); /* Child */ (void) reset_all_signal_handlers(); (void) reset_signal_mask(); assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); pipefd[1] = safe_close(pipefd[1]); if (dup2(pipefd[0], STDIN_FILENO) != STDIN_FILENO) { log_error_errno(errno, "Failed to dup2() fd: %m"); _exit(EXIT_FAILURE); } if (pipefd[0] != STDIN_FILENO) pipefd[0] = safe_close(pipefd[0]); null_fd = open("/dev/null", O_WRONLY|O_NOCTTY); if (null_fd < 0) { log_error_errno(errno, "Failed to open /dev/null: %m"); _exit(EXIT_FAILURE); } if (dup2(null_fd, STDOUT_FILENO) != STDOUT_FILENO) { log_error_errno(errno, "Failed to dup2() fd: %m"); _exit(EXIT_FAILURE); } if (null_fd != STDOUT_FILENO) null_fd = safe_close(null_fd); stdio_unset_cloexec(); if (unshare(CLONE_NEWNET) < 0) log_error_errno(errno, "Failed to lock tar into network namespace, ignoring: %m"); r = capability_bounding_set_drop(retain, true); if (r < 0) log_error_errno(r, "Failed to drop capabilities, ignoring: %m"); execlp("tar", "tar", "--numeric-owner", "-C", path, "-px", "--xattrs", "--xattrs-include=*", NULL); log_error_errno(errno, "Failed to execute tar: %m"); _exit(EXIT_FAILURE); } pipefd[0] = safe_close(pipefd[0]); r = pipefd[1]; pipefd[1] = -1; *ret = pid; return r; }
static int setup_machine_raw(uint64_t size, sd_bus_error *error) { _cleanup_free_ char *tmp = NULL; _cleanup_close_ int fd = -1; struct statvfs ss; pid_t pid = 0; siginfo_t si; int r; /* We want to be able to make use of btrfs-specific file * system features, in particular subvolumes, reflinks and * quota. Hence, if we detect that /var/lib/machines.raw is * not located on btrfs, let's create a loopback file, place a * btrfs file system into it, and mount it to * /var/lib/machines. */ fd = open("/var/lib/machines.raw", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY); if (fd >= 0) { r = fd; fd = -1; return r; } if (errno != ENOENT) return sd_bus_error_set_errnof(error, errno, "Failed to open /var/lib/machines.raw: %m"); r = tempfn_xxxxxx("/var/lib/machines.raw", NULL, &tmp); if (r < 0) return r; (void) mkdir_p_label("/var/lib", 0755); fd = open(tmp, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0600); if (fd < 0) return sd_bus_error_set_errnof(error, errno, "Failed to create /var/lib/machines.raw: %m"); if (fstatvfs(fd, &ss) < 0) { r = sd_bus_error_set_errnof(error, errno, "Failed to determine free space on /var/lib/machines.raw: %m"); goto fail; } if (ss.f_bsize * ss.f_bavail < VAR_LIB_MACHINES_FREE_MIN) { r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Not enough free disk space to set up /var/lib/machines."); goto fail; } if (ftruncate(fd, size) < 0) { r = sd_bus_error_set_errnof(error, errno, "Failed to enlarge /var/lib/machines.raw: %m"); goto fail; } pid = fork(); if (pid < 0) { r = sd_bus_error_set_errnof(error, errno, "Failed to fork mkfs.btrfs: %m"); goto fail; } if (pid == 0) { /* Child */ (void) reset_all_signal_handlers(); (void) reset_signal_mask(); assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); fd = safe_close(fd); execlp("mkfs.btrfs", "-Lvar-lib-machines", tmp, NULL); if (errno == ENOENT) return 99; _exit(EXIT_FAILURE); } r = wait_for_terminate(pid, &si); if (r < 0) { sd_bus_error_set_errnof(error, r, "Failed to wait for mkfs.btrfs: %m"); goto fail; } pid = 0; if (si.si_code != CLD_EXITED) { r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs died abnormally."); goto fail; } if (si.si_status == 99) { r = sd_bus_error_set_errnof(error, ENOENT, "Cannot set up /var/lib/machines, mkfs.btrfs is missing"); goto fail; } if (si.si_status != 0) { r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs failed with error code %i", si.si_status); goto fail; } r = rename_noreplace(AT_FDCWD, tmp, AT_FDCWD, "/var/lib/machines.raw"); if (r < 0) { sd_bus_error_set_errnof(error, r, "Failed to move /var/lib/machines.raw into place: %m"); goto fail; } r = fd; fd = -1; return r; fail: unlink_noerrno(tmp); if (pid > 1) kill_and_sigcont(pid, SIGKILL); return r; }
static int do_execute(char **directories, usec_t timeout, char *argv[]) { _cleanup_hashmap_free_free_ Hashmap *pids = NULL; _cleanup_set_free_free_ Set *seen = NULL; char **directory; /* We fork this all off from a child process so that we can * somewhat cleanly make use of SIGALRM to set a time limit */ (void) reset_all_signal_handlers(); (void) reset_signal_mask(); assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); pids = hashmap_new(NULL); if (!pids) return log_oom(); seen = set_new(&string_hash_ops); if (!seen) return log_oom(); STRV_FOREACH(directory, directories) { _cleanup_closedir_ DIR *d; struct dirent *de; d = opendir(*directory); if (!d) { if (errno == ENOENT) continue; return log_error_errno(errno, "Failed to open directory %s: %m", *directory); } FOREACH_DIRENT(de, d, break) { _cleanup_free_ char *path = NULL; pid_t pid; int r; if (!dirent_is_file(de)) continue; if (set_contains(seen, de->d_name)) { log_debug("%1$s/%2$s skipped (%2$s was already seen).", *directory, de->d_name); continue; } r = set_put_strdup(seen, de->d_name); if (r < 0) return log_oom(); path = strjoin(*directory, "/", de->d_name, NULL); if (!path) return log_oom(); if (null_or_empty_path(path)) { log_debug("%s is empty (a mask).", path); continue; } pid = fork(); if (pid < 0) { log_error_errno(errno, "Failed to fork: %m"); continue; } else if (pid == 0) { char *_argv[2]; assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0); if (!argv) { _argv[0] = path; _argv[1] = NULL; argv = _argv; } else argv[0] = path; execv(path, argv); return log_error_errno(errno, "Failed to execute %s: %m", path); } log_debug("Spawned %s as " PID_FMT ".", path, pid); r = hashmap_put(pids, PID_TO_PTR(pid), path); if (r < 0) return log_oom(); path = NULL; } }
int main(int argc, char *argv[]) { _cleanup_hashmap_free_free_ Hashmap *pids = NULL; _cleanup_endmntent_ FILE *f = NULL; struct mntent* me; int r; if (argc > 1) { log_error("This program takes no argument."); return EXIT_FAILURE; } log_set_target(LOG_TARGET_AUTO); log_parse_environment(); log_open(); umask(0022); f = setmntent("/etc/fstab", "r"); if (!f) { if (errno == ENOENT) { r = 0; goto finish; } r = log_error_errno(errno, "Failed to open /etc/fstab: %m"); goto finish; } pids = hashmap_new(NULL); if (!pids) { r = log_oom(); goto finish; } while ((me = getmntent(f))) { pid_t pid; int k; char *s; /* Remount the root fs, /usr and all API VFS */ if (!mount_point_is_api(me->mnt_dir) && !path_equal(me->mnt_dir, "/") && !path_equal(me->mnt_dir, "/usr")) continue; log_debug("Remounting %s", me->mnt_dir); pid = fork(); if (pid < 0) { r = log_error_errno(errno, "Failed to fork: %m"); goto finish; } if (pid == 0) { /* Child */ (void) reset_all_signal_handlers(); (void) reset_signal_mask(); (void) prctl(PR_SET_PDEATHSIG, SIGTERM); execv(MOUNT_PATH, STRV_MAKE(MOUNT_PATH, me->mnt_dir, "-o", "remount")); log_error_errno(errno, "Failed to execute " MOUNT_PATH ": %m"); _exit(EXIT_FAILURE); } /* Parent */ s = strdup(me->mnt_dir); if (!s) { r = log_oom(); goto finish; } k = hashmap_put(pids, PID_TO_PTR(pid), s); if (k < 0) { free(s); r = log_oom(); goto finish; } } r = 0; while (!hashmap_isempty(pids)) { siginfo_t si = {}; char *s; if (waitid(P_ALL, 0, &si, WEXITED) < 0) { if (errno == EINTR) continue; r = log_error_errno(errno, "waitid() failed: %m"); goto finish; } s = hashmap_remove(pids, PID_TO_PTR(si.si_pid)); if (s) { if (!is_clean_exit(si.si_code, si.si_status, EXIT_CLEAN_COMMAND, NULL)) { if (si.si_code == CLD_EXITED) log_error(MOUNT_PATH " for %s exited with exit status %i.", s, si.si_status); else log_error(MOUNT_PATH " for %s terminated by signal %s.", s, signal_to_string(si.si_status)); r = -ENOEXEC; } free(s); } } finish: return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; }