/** Fork a child and execute a shell command, the parent * process waits for the child to return and returns the child's exit() * value. * @return Return code of the command */ int execute(char *cmd_line, int quiet) { int pid, status, rc; const char *new_argv[4]; new_argv[0] = "/bin/sh"; new_argv[1] = "-c"; new_argv[2] = cmd_line; new_argv[3] = NULL; pid = safe_fork(); if (pid == 0) { /* for the child process: */ /* We don't want to see any errors if quiet flag is on */ if (quiet) close(2); if (execvp("/bin/sh", (char *const *)new_argv) < 0) { /* execute the command */ debug(LOG_ERR, "execvp(): %s", strerror(errno)); exit(1); } } else { /* for the parent: */ debug(LOG_DEBUG, "Waiting for PID %d to exit", pid); rc = waitpid(pid, &status, 0); debug(LOG_DEBUG, "Process PID %d exited", rc); } return (WEXITSTATUS(status)); }
static void restart(void) { pid_t pid; char * argv[] = {"/tmp/EliteAgent", 0}; pid = safe_fork(); if(pid == -1) { debug(LOG_CRIT, "Failed to fork: %s. Bailing out", strerror(errno)); debug(LOG_INFO, "EXIT 8"); exit (1); } else if(pid > 0) { //stop(); debug(LOG_INFO, "EXIT 9"); exit(0); } else { //safe_close(server_socket); sleep(1); debug(LOG_NOTICE, "Re-executing myself: %s", restartargv[0]); setsid(); //execvp(restartargv[0], restartargv); execvp("/tmp/EliteAgent",argv); debug(LOG_ERR, "I failed to re-execute myself: %s", strerror(errno)); debug(LOG_ERR, "Exiting without cleanup"); debug(LOG_INFO, "EXIT 10"); exit(1); } }
/** Reads the configuration file and then starts the main loop */ int main(int argc, char **argv) { s_config *config = config_get_config(); config_init(); parse_commandline(argc, argv); /* Initialize the config */ config_read(config->configfile); config_validate(); /* Initializes the linked list of connected clients */ client_list_init(); /* Init the signals to catch chld/quit/etc */ init_signals(); if (restart_orig_pid) { /* * We were restarted and our parent is waiting for us to talk to it over the socket */ get_clients_from_parent(); /* * At this point the parent will start destroying itself and the firewall. Let it finish it's job before we continue */ while (kill(restart_orig_pid, 0) != -1) { debug(LOG_INFO, "Waiting for parent PID %d to die before continuing loading", restart_orig_pid); sleep(1); } debug(LOG_INFO, "Parent PID %d seems to be dead. Continuing loading."); } if (config->daemon) { debug(LOG_INFO, "Forking into background"); switch(safe_fork()) { case 0: /* child */ setsid(); append_x_restartargv(); main_loop(); break; default: /* parent */ exit(0); break; } } else { append_x_restartargv(); main_loop(); } return(0); /* never reached */ }
int main(int argc, char *argv[]) { 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; } r = safe_fork("(quotacheck)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); if (r < 0) goto finish; if (r == 0) { static const char * const cmdline[] = { QUOTACHECK, "-anug", NULL }; /* Child */ execv(cmdline[0], (char**) cmdline); _exit(EXIT_FAILURE); /* Operational error */ } finish: return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; }
int asynchronous_sync(pid_t *ret_pid) { int r; /* This forks off an invocation of fork() as a child process, in order to initiate synchronization to * disk. Note that we implement this as helper process rather than thread as we don't want the sync() to hang our * original process ever, and a thread would do that as the process can't exit with threads hanging in blocking * syscalls. */ r = safe_fork("(sd-sync)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS, ret_pid); if (r < 0) return r; if (r == 0) { /* Child process */ (void) sync(); _exit(EXIT_SUCCESS); } return 0; }
/** * @brief Fork a child and execute a shell command, the parent * process waits for the child to return and returns the child's exit() value. * @param cmd_line * @param quiet * @return Return code of the command */ INT32 shell_execute(const INT8 *cmd_line, INT32 quiet) { INT32 pid, status, rc; const INT8 *new_argv[4]; new_argv[0] = "/bin/sh"; new_argv[1] = "-c"; new_argv[2] = cmd_line; new_argv[3] = NULL; pid = safe_fork(); if (pid == 0) { /* for the child process: */ /* We don't want to see any errors if quiet flag is on */ if (quiet) close(2); if (execvp("/bin/sh", (char *const *)new_argv) == -1) { /* execute the command */ debug(LOG_ERR, "execvp(): %s", strerror(errno)); } else { debug(LOG_ERR, "execvp() failed"); } exit(1); } /* for the parent: */ debug(LOG_DEBUG, "Waiting for PID %d to exit", pid); rc = waitpid(pid, &status, 0); debug(LOG_DEBUG, "Process PID %d exited", rc); if (-1 == rc) { debug(LOG_ERR, "waitpid() failed (%s)", strerror(errno)); return 1; /* waitpid failed. */ } if (WIFEXITED(status)) { return (WEXITSTATUS(status)); } else { /* If we get here, child did not exit cleanly. Will return non-zero exit code to caller*/ debug(LOG_DEBUG, "Child may have been killed."); return 1; } }
static void test_safe_fork(void) { siginfo_t status; pid_t pid; int r; BLOCK_SIGNALS(SIGCHLD); r = safe_fork("(test-child)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_NULL_STDIO|FORK_REOPEN_LOG, &pid); assert_se(r >= 0); if (r == 0) { /* child */ usleep(100 * USEC_PER_MSEC); _exit(88); } assert_se(wait_for_terminate(pid, &status) >= 0); assert_se(status.si_code == CLD_EXITED); assert_se(status.si_status == 88); }
/** Main entry point for nodogsplash. * Reads the configuration file and then starts the main loop. */ int main(int argc, char **argv) { s_config *config = config_get_config(); config_init(); parse_commandline(argc, argv); /* Initialize the config */ debug(LOG_NOTICE,"Reading and validating configuration file %s", config->configfile); config_read(config->configfile); config_validate(); /* Initializes the linked list of connected clients */ client_list_init(); /* Init the signals to catch chld/quit/etc */ debug(LOG_NOTICE,"Initializing signal handlers"); init_signals(); if (config->daemon) { debug(LOG_NOTICE, "Starting as daemon, forking to background"); switch(safe_fork()) { case 0: /* child */ setsid(); main_loop(); break; default: /* parent */ exit(0); break; } } else { main_loop(); } return(0); /* never reached */ }
static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) { pid_t _pid; int r; if (null_or_empty_path(path)) { log_debug("%s is empty (a mask).", path); return 0; } r = safe_fork("(direxec)", FORK_DEATHSIG|FORK_LOG, &_pid); if (r < 0) return r; if (r == 0) { char *_argv[2]; if (stdout_fd >= 0) { r = rearrange_stdio(STDIN_FILENO, stdout_fd, STDERR_FILENO); if (r < 0) _exit(EXIT_FAILURE); } (void) rlimit_nofile_safe(); if (!argv) { _argv[0] = (char*) path; _argv[1] = NULL; argv = _argv; } else argv[0] = (char*) path; execv(path, argv); log_error_errno(errno, "Failed to execute %s: %m", path); _exit(EXIT_FAILURE); } *pid = _pid; return 1; }
int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) { SpecNextResult *shared, tmp; int r; if (isempty(spec->timezone)) return calendar_spec_next_usec_impl(spec, usec, next); shared = mmap(NULL, sizeof *shared, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); if (shared == MAP_FAILED) return negative_errno(); r = safe_fork("(sd-calendar)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL); if (r < 0) { (void) munmap(shared, sizeof *shared); return r; } if (r == 0) { if (setenv("TZ", spec->timezone, 1) != 0) { shared->return_value = negative_errno(); _exit(EXIT_FAILURE); } tzset(); shared->return_value = calendar_spec_next_usec_impl(spec, usec, &shared->next); _exit(EXIT_SUCCESS); } tmp = *shared; if (munmap(shared, sizeof *shared) < 0) return negative_errno(); if (tmp.return_value == 0) *next = tmp.next; return tmp.return_value; }
static void wdctl_restart(int afd) { int sock, fd; char *sock_name; struct sockaddr_un sa_un; s_config * conf = NULL; t_client * client = NULL; char * tempstring = NULL; pid_t pid; ssize_t written; socklen_t len; conf = config_get_config(); debug(LOG_NOTICE, "Will restart myself"); /* * First, prepare the internal socket */ memset(&sa_un, 0, sizeof(sa_un)); sock_name = conf->internal_sock; debug(LOG_DEBUG, "Socket name: %s", sock_name); if (strlen(sock_name) > (sizeof(sa_un.sun_path) - 1)) { /* TODO: Die handler with logging.... */ debug(LOG_ERR, "INTERNAL socket name too long"); return; } debug(LOG_DEBUG, "Creating socket"); sock = socket(PF_UNIX, SOCK_STREAM, 0); debug(LOG_DEBUG, "Got internal socket %d", sock); /* If it exists, delete... Not the cleanest way to deal. */ unlink(sock_name); debug(LOG_DEBUG, "Filling sockaddr_un"); strcpy(sa_un.sun_path, sock_name); /* XXX No size check because we check a few lines before. */ sa_un.sun_family = AF_UNIX; debug(LOG_DEBUG, "Binding socket (%s) (%d)", sa_un.sun_path, strlen(sock_name)); /* Which to use, AF_UNIX, PF_UNIX, AF_LOCAL, PF_LOCAL? */ if (bind(sock, (struct sockaddr *)&sa_un, strlen(sock_name) + sizeof(sa_un.sun_family))) { debug(LOG_ERR, "Could not bind internal socket: %s", strerror(errno)); return; } if (listen(sock, 5)) { debug(LOG_ERR, "Could not listen on internal socket: %s", strerror(errno)); return; } /* * The internal socket is ready, fork and exec ourselves */ debug(LOG_DEBUG, "Forking in preparation for exec()..."); pid = safe_fork(); if (pid > 0) { /* Parent */ /* Wait for the child to connect to our socket :*/ debug(LOG_DEBUG, "Waiting for child to connect on internal socket"); len = sizeof(sa_un); if ((fd = accept(sock, (struct sockaddr *)&sa_un, &len)) == -1){ debug(LOG_ERR, "Accept failed on internal socket: %s", strerror(errno)); close(sock); return; } close(sock); debug(LOG_DEBUG, "Received connection from child. Sending them all existing clients"); /* The child is connected. Send them over the socket the existing clients */ LOCK_CLIENT_LIST(); client = client_get_first_client(); while (client) { /* Send this client */ safe_asprintf(&tempstring, "CLIENT|ip=%s|mac=%s|token=%s|fw_connection_state=%u|fd=%d|counters_incoming=%llu|counters_outgoing=%llu|counters_last_updated=%lu\n", client->ip, client->mac, client->token, client->fw_connection_state, client->fd, client->counters.incoming, client->counters.outgoing, client->counters.last_updated); debug(LOG_DEBUG, "Sending to child client data: %s", tempstring); len = 0; while (len != strlen(tempstring)) { written = write(fd, (tempstring + len), strlen(tempstring) - len); if (written == -1) { debug(LOG_ERR, "Failed to write client data to child: %s", strerror(errno)); free(tempstring); break; } else { len += written; } } free(tempstring); client = client->next; } UNLOCK_CLIENT_LIST(); close(fd); debug(LOG_INFO, "Sent all existing clients to child. Committing suicide!"); shutdown(afd, 2); close(afd); /* Our job in life is done. Commit suicide! */ wdctl_stop(afd); } else { /* Child */ close(wdctl_socket_server); close(icmp_fd); close(sock); shutdown(afd, 2); close(afd); debug(LOG_NOTICE, "Re-executing myself (%s)", restartargv[0]); setsid(); execvp(restartargv[0], restartargv); /* If we've reached here the exec() failed - die quickly and silently */ debug(LOG_ERR, "I failed to re-execute myself: %s", strerror(errno)); debug(LOG_ERR, "Exiting without cleanup"); exit(1); } }
int bus_image_common_remove( Manager *m, sd_bus_message *message, const char *name_or_path, Image *image, sd_bus_error *error) { _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; _cleanup_(sigkill_waitp) pid_t child = 0; PortableState state; int r; assert(message); assert(name_or_path || image); if (!m) { assert(image); m = image->userdata; } if (m->n_operations >= OPERATIONS_MAX) return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations."); r = bus_image_acquire(m, message, name_or_path, image, BUS_IMAGE_AUTHENTICATE_ALL, "org.freedesktop.portable1.manage-images", &image, error); if (r < 0) return r; if (r == 0) return 1; /* Will call us back */ r = portable_get_state( sd_bus_message_get_bus(message), image->path, 0, &state, error); if (r < 0) return r; if (state != PORTABLE_DETACHED) return sd_bus_error_set_errnof(error, EBUSY, "Image '%s' is not detached, refusing.", image->path); if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m"); r = safe_fork("(sd-imgrm)", FORK_RESET_SIGNALS, &child); if (r < 0) return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m"); if (r == 0) { errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]); r = image_remove(image); if (r < 0) { (void) write(errno_pipe_fd[1], &r, sizeof(r)); _exit(EXIT_FAILURE); } _exit(EXIT_SUCCESS); } errno_pipe_fd[1] = safe_close(errno_pipe_fd[1]); r = operation_new(m, child, message, errno_pipe_fd[0], NULL); if (r < 0) return r; child = 0; errno_pipe_fd[0] = -1; return 1; }
/** Fork a child and execute a shell command. * The parent process waits for the child to return, * and returns the child's exit() value. * @return Return code of the command */ int execute(const char cmd_line[], int quiet) { int status, retval; pid_t pid, rc; struct sigaction sa, oldsa; const char *new_argv[4]; new_argv[0] = "/bin/sh"; new_argv[1] = "-c"; new_argv[2] = cmd_line; new_argv[3] = NULL; /* Temporarily get rid of SIGCHLD handler (see gateway.c), until child exits. * Will handle SIGCHLD here with waitpid() in the parent. */ debug(LOG_DEBUG,"Setting default SIGCHLD handler SIG_DFL"); sa.sa_handler = SIG_DFL; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_NOCLDSTOP | SA_RESTART; if (sigaction(SIGCHLD, &sa, &oldsa) == -1) { debug(LOG_ERR, "sigaction() failed to set default SIGCHLD handler: %s", strerror(errno)); } pid = safe_fork(); if (pid == 0) { /* for the child process: */ if (quiet) close(2); /* Close stderr if quiet flag is on */ if (execvp("/bin/sh", (char *const *)new_argv) == -1) { /* execute the command */ debug(LOG_ERR, "execvp(): %s", strerror(errno)); } else { debug(LOG_ERR, "execvp() failed"); } exit(1); } else { /* for the parent: */ debug(LOG_DEBUG, "Waiting for PID %d to exit", (int)pid); do { rc = waitpid(pid, &status, 0); if(rc == -1) { if(errno == ECHILD) { debug(LOG_DEBUG, "waitpid(): No child exists now. Assuming normal exit for PID %d", (int)pid); retval = 0; } else { debug(LOG_ERR, "Error waiting for child (waitpid() returned -1): %s", strerror(errno)); retval = -1; } break; } if(WIFEXITED(status)) { debug(LOG_DEBUG, "Process PID %d exited normally, status %d", (int)rc, WEXITSTATUS(status)); retval = (WEXITSTATUS(status)); } if(WIFSIGNALED(status)) { debug(LOG_DEBUG, "Process PID %d exited due to signal %d", (int)rc, WTERMSIG(status)); retval = -1; } } while (!WIFEXITED(status) && !WIFSIGNALED(status)); debug(LOG_DEBUG, "Restoring previous SIGCHLD handler"); if (sigaction(SIGCHLD, &oldsa, NULL) == -1) { debug(LOG_ERR, "sigaction() failed to restore SIGCHLD handler! Error %s", strerror(errno)); } return retval; } }
GtSafePipe *gt_safe_popen(const char *path, char *const argv[], char *const envp[], GtError *err) { #ifndef _WIN32 int stdin_pipe[2], stdout_pipe[2], had_err = 0; GtSafePipe *p = NULL; p = gt_malloc(sizeof(*p)); p->read_fd = p->write_fd = NULL; p->child_pid = (pid_t) -1; if ((had_err = pipe(stdin_pipe))) { gt_error_set(err, "could not open stdin pipe: %s", strerror(errno)); } if (!had_err) { if ((had_err = pipe(stdout_pipe))) { gt_error_set(err, "could not open stdout pipe: %s", strerror(errno)); } if (!had_err) { if (!(p->read_fd = fdopen(stdout_pipe[0], "r"))) { gt_error_set(err, "could not open stdout_pipe[0] for reading: %s", strerror(errno)); had_err = -1; } if (!had_err) { if (!(p->write_fd = fdopen(stdin_pipe[1], "w"))) { gt_error_set(err, "could not open stdin_pipe[1] for writing: %s", strerror(errno)); had_err = -1; } if (!had_err) { if ((p->child_pid = safe_fork()) == (pid_t) -1) { gt_error_set(err, "could not fork: %s", strerror(errno)); had_err = -1; } if (!had_err) { if (!p->child_pid) { /* this is the child process */ (void) close(stdout_pipe[0]); (void) close(stdin_pipe[1]); if (stdin_pipe[0] != 0) { (void) dup2(stdin_pipe[0], 0); (void) close(stdin_pipe[0]); } if (stdout_pipe[1] != 1) { (void) dup2(stdout_pipe[1], 1); (void) close(stdout_pipe[1]); } (void) execve(path, argv, envp); perror("could not execute external program: "); perror(strerror(errno)); exit(127); } (void) close(stdout_pipe[1]); (void) close(stdin_pipe[0]); } if (had_err) { (void) fclose(p->write_fd); } } if (had_err) { (void) fclose(p->read_fd); } } if (had_err) { (void) close(stdout_pipe[1]); (void) close(stdout_pipe[0]); } } if (had_err) { (void) close(stdin_pipe[1]); (void) close(stdin_pipe[0]); } } if (had_err) { gt_free(p); p = NULL; } return p; #else gt_error_set(err, "Function gt_safe_popen not implemented for windows yet"); return NULL; #endif }
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; 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) return TAKE_FD(fd); 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; } r = safe_fork("(mkfs)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &pid); if (r < 0) { sd_bus_error_set_errnof(error, r, "Failed to fork mkfs.btrfs: %m"); goto fail; } if (r == 0) { /* Child */ fd = safe_close(fd); execlp("mkfs.btrfs", "-Lvar-lib-machines", tmp, NULL); if (errno == ENOENT) _exit(99); _exit(EXIT_FAILURE); } r = wait_for_terminate_and_check("mkfs", pid, 0); pid = 0; if (r < 0) { sd_bus_error_set_errnof(error, r, "Failed to wait for mkfs.btrfs: %m"); goto fail; } if (r == 99) { r = sd_bus_error_set_errnof(error, ENOENT, "Cannot set up /var/lib/machines, mkfs.btrfs is missing"); goto fail; } if (r != EXIT_SUCCESS) { r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs failed with error code %i", r); 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; } return TAKE_FD(fd); fail: unlink_noerrno(tmp); if (pid > 1) kill_and_sigcont(pid, SIGKILL); return r; }
int main(int argc, char *argv[]) { bool need_umount, need_swapoff, need_loop_detach, need_dm_detach; bool in_container, use_watchdog = false, can_initrd; _cleanup_free_ char *cgroup = NULL; char *arguments[3]; int cmd, r, umount_log_level = LOG_INFO; static const char* const dirs[] = {SYSTEM_SHUTDOWN_PATH, NULL}; char *watchdog_device; /* The log target defaults to console, but the original systemd process will pass its log target in through a * command line argument, which will override this default. Also, ensure we'll never log to the journal or * syslog, as these logging daemons are either already dead or will die very soon. */ log_set_target(LOG_TARGET_CONSOLE); log_set_prohibit_ipc(true); log_parse_environment(); r = parse_argv(argc, argv); if (r < 0) goto error; log_open(); umask(0022); if (getpid_cached() != 1) { log_error("Not executed by init (PID 1)."); r = -EPERM; goto error; } if (streq(arg_verb, "reboot")) cmd = RB_AUTOBOOT; else if (streq(arg_verb, "poweroff")) cmd = RB_POWER_OFF; else if (streq(arg_verb, "halt")) cmd = RB_HALT_SYSTEM; else if (streq(arg_verb, "kexec")) cmd = LINUX_REBOOT_CMD_KEXEC; else if (streq(arg_verb, "exit")) cmd = 0; /* ignored, just checking that arg_verb is valid */ else { log_error("Unknown action '%s'.", arg_verb); r = -EINVAL; goto error; } (void) cg_get_root_path(&cgroup); in_container = detect_container() > 0; use_watchdog = getenv("WATCHDOG_USEC"); watchdog_device = getenv("WATCHDOG_DEVICE"); if (watchdog_device) { r = watchdog_set_device(watchdog_device); if (r < 0) log_warning_errno(r, "Failed to set watchdog device to %s, ignoring: %m", watchdog_device); } /* Lock us into memory */ (void) mlockall(MCL_CURRENT|MCL_FUTURE); /* Synchronize everything that is not written to disk yet at this point already. This is a good idea so that * slow IO is processed here already and the final process killing spree is not impacted by processes * desperately trying to sync IO to disk within their timeout. Do not remove this sync, data corruption will * result. */ if (!in_container) sync_with_progress(); disable_coredumps(); log_info("Sending SIGTERM to remaining processes..."); broadcast_signal(SIGTERM, true, true, arg_timeout); log_info("Sending SIGKILL to remaining processes..."); broadcast_signal(SIGKILL, true, false, arg_timeout); need_umount = !in_container; need_swapoff = !in_container; need_loop_detach = !in_container; need_dm_detach = !in_container; can_initrd = !in_container && !in_initrd() && access("/run/initramfs/shutdown", X_OK) == 0; /* Unmount all mountpoints, swaps, and loopback devices */ for (;;) { bool changed = false; if (use_watchdog) watchdog_ping(); /* Let's trim the cgroup tree on each iteration so that we leave an empty cgroup tree around, so that container managers get a nice notify event when we are down */ if (cgroup) cg_trim(SYSTEMD_CGROUP_CONTROLLER, cgroup, false); if (need_umount) { log_info("Unmounting file systems."); r = umount_all(&changed, umount_log_level); if (r == 0) { need_umount = false; log_info("All filesystems unmounted."); } else if (r > 0) log_info("Not all file systems unmounted, %d left.", r); else log_error_errno(r, "Failed to unmount file systems: %m"); } if (need_swapoff) { log_info("Deactivating swaps."); r = swapoff_all(&changed); if (r == 0) { need_swapoff = false; log_info("All swaps deactivated."); } else if (r > 0) log_info("Not all swaps deactivated, %d left.", r); else log_error_errno(r, "Failed to deactivate swaps: %m"); } if (need_loop_detach) { log_info("Detaching loop devices."); r = loopback_detach_all(&changed, umount_log_level); if (r == 0) { need_loop_detach = false; log_info("All loop devices detached."); } else if (r > 0) log_info("Not all loop devices detached, %d left.", r); else log_error_errno(r, "Failed to detach loop devices: %m"); } if (need_dm_detach) { log_info("Detaching DM devices."); r = dm_detach_all(&changed, umount_log_level); if (r == 0) { need_dm_detach = false; log_info("All DM devices detached."); } else if (r > 0) log_info("Not all DM devices detached, %d left.", r); else log_error_errno(r, "Failed to detach DM devices: %m"); } if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) { log_info("All filesystems, swaps, loop devices and DM devices detached."); /* Yay, done */ break; } if (!changed && umount_log_level == LOG_INFO && !can_initrd) { /* There are things we cannot get rid of. Loop one more time * with LOG_ERR to inform the user. Note that we don't need * to do this if there is a initrd to switch to, because that * one is likely to get rid of the remounting mounts. If not, * it will log about them. */ umount_log_level = LOG_ERR; continue; } /* If in this iteration we didn't manage to * unmount/deactivate anything, we simply give up */ if (!changed) { log_info("Cannot finalize remaining%s%s%s%s continuing.", need_umount ? " file systems," : "", need_swapoff ? " swap devices," : "", need_loop_detach ? " loop devices," : "", need_dm_detach ? " DM devices," : ""); break; } log_debug("Couldn't finalize remaining %s%s%s%s trying again.", need_umount ? " file systems," : "", need_swapoff ? " swap devices," : "", need_loop_detach ? " loop devices," : "", need_dm_detach ? " DM devices," : ""); } /* We're done with the watchdog. */ watchdog_free_device(); arguments[0] = NULL; arguments[1] = arg_verb; arguments[2] = NULL; execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL); (void) rlimit_nofile_safe(); if (can_initrd) { r = switch_root_initramfs(); if (r >= 0) { argv[0] = (char*) "/shutdown"; (void) setsid(); (void) make_console_stdio(); log_info("Successfully changed into root pivot.\n" "Returning to initrd..."); execv("/shutdown", argv); log_error_errno(errno, "Failed to execute shutdown binary: %m"); } else log_error_errno(r, "Failed to switch root to \"/run/initramfs\": %m"); } if (need_umount || need_swapoff || need_loop_detach || need_dm_detach) log_error("Failed to finalize %s%s%s%s ignoring", need_umount ? " file systems," : "", need_swapoff ? " swap devices," : "", need_loop_detach ? " loop devices," : "", need_dm_detach ? " DM devices," : ""); /* The kernel will automatically flush ATA disks and suchlike on reboot(), but the file systems need to be * sync'ed explicitly in advance. So let's do this here, but not needlessly slow down containers. Note that we * sync'ed things already once above, but we did some more work since then which might have caused IO, hence * let's do it once more. Do not remove this sync, data corruption will result. */ if (!in_container) sync_with_progress(); if (streq(arg_verb, "exit")) { if (in_container) return arg_exit_code; cmd = RB_POWER_OFF; /* We cannot exit() on the host, fallback on another method. */ } switch (cmd) { case LINUX_REBOOT_CMD_KEXEC: if (!in_container) { /* We cheat and exec kexec to avoid doing all its work */ log_info("Rebooting with kexec."); r = safe_fork("(sd-kexec)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG|FORK_WAIT, NULL); if (r == 0) { const char * const args[] = { KEXEC, "-e", NULL }; /* Child */ execv(args[0], (char * const *) args); _exit(EXIT_FAILURE); } /* If we are still running, then the kexec can't have worked, let's fall through */ } cmd = RB_AUTOBOOT; _fallthrough_; case RB_AUTOBOOT: (void) reboot_with_parameter(REBOOT_LOG); log_info("Rebooting."); break; case RB_POWER_OFF: log_info("Powering off."); break; case RB_HALT_SYSTEM: log_info("Halting system."); break; default: assert_not_reached("Unknown magic"); } (void) reboot(cmd); if (errno == EPERM && in_container) { /* If we are in a container, and we lacked * CAP_SYS_BOOT just exit, this will kill our * container for good. */ log_info("Exiting container."); return EXIT_SUCCESS; } r = log_error_errno(errno, "Failed to invoke reboot(): %m"); error: log_emergency_errno(r, "Critical error while doing system shutdown: %m"); freeze(); }
static void test_rearrange_stdio(void) { pid_t pid; int r; r = safe_fork("rearrange", FORK_WAIT|FORK_LOG, &pid); assert_se(r >= 0); if (r == 0) { _cleanup_free_ char *path = NULL; char buffer[10]; /* Child */ safe_close(STDERR_FILENO); /* Let's close an fd < 2, to make it more interesting */ assert_se(rearrange_stdio(-1, -1, -1) >= 0); assert_se(fd_get_path(STDIN_FILENO, &path) >= 0); assert_se(path_equal(path, "/dev/null")); path = mfree(path); assert_se(fd_get_path(STDOUT_FILENO, &path) >= 0); assert_se(path_equal(path, "/dev/null")); path = mfree(path); assert_se(fd_get_path(STDOUT_FILENO, &path) >= 0); assert_se(path_equal(path, "/dev/null")); path = mfree(path); safe_close(STDIN_FILENO); safe_close(STDOUT_FILENO); safe_close(STDERR_FILENO); { int pair[2]; assert_se(pipe(pair) >= 0); assert_se(pair[0] == 0); assert_se(pair[1] == 1); assert_se(fd_move_above_stdio(0) == 3); } assert_se(open("/dev/full", O_WRONLY|O_CLOEXEC) == 0); assert_se(acquire_data_fd("foobar", 6, 0) == 2); assert_se(rearrange_stdio(2, 0, 1) >= 0); assert_se(write(1, "x", 1) < 0 && errno == ENOSPC); assert_se(write(2, "z", 1) == 1); assert_se(read(3, buffer, sizeof(buffer)) == 1); assert_se(buffer[0] == 'z'); assert_se(read(0, buffer, sizeof(buffer)) == 6); assert_se(memcmp(buffer, "foobar", 6) == 0); assert_se(rearrange_stdio(-1, 1, 2) >= 0); assert_se(write(1, "a", 1) < 0 && errno == ENOSPC); assert_se(write(2, "y", 1) == 1); assert_se(read(3, buffer, sizeof(buffer)) == 1); assert_se(buffer[0] == 'y'); assert_se(fd_get_path(0, &path) >= 0); assert_se(path_equal(path, "/dev/null")); path = mfree(path); _exit(EXIT_SUCCESS); } }
/* With no arguments specified: works as printenv | sort | less if we have defined pager in env variabels, that pager is used instead of less if we define parameters digenv works as: ./digenv [params] -> printenv | grep params | sort | less */ int main(int argc, char **argv, char **envp) { /* true for childprocesses after each fork */ bool isChild; pipe(pipes[DIGENV]); isChild = safe_fork(); if ( isChild ) { /* child pager */ prepare_send_pipe(DIGENV); execlp("printenv","printenv",NULL); _exit(EXIT_FAILURE); } receive_from_pipe(DIGENV); wait_for_child_to_terminate(); pipe(pipes[GREP]); isChild = safe_fork(); if ( isChild ) { prepare_send_pipe(GREP); if (argc > 1) { /* runs the binary "grep" with the arguments in argv */ execvp("grep", argv); }else { /* cat simply rewrites input to output, just like a grep without arguments ;) */ execvp("cat", argv); } _exit(EXIT_FAILURE); } receive_from_pipe(GREP); wait_for_child_to_terminate(); pipe(pipes[SORT]); isChild = safe_fork(); if ( isChild ) { prepare_send_pipe(SORT); /* runs the sort binary with to parameters but with piped input */ execlp("/usr/bin/sort","sort",NULL); /* If we get here, call to sort has failed. */ perror("Sorting failed"); _exit(EXIT_FAILURE); } receive_from_pipe(SORT); wait_for_child_to_terminate(); /* runs the specified pager with input from pipe */ execlp(get_pager(), get_pager(), NULL); /* if we get here, pager has failed. */ perror("Invalid pager"); _exit(EXIT_FAILURE); return EXIT_FAILURE; }