/* Call this before doing anything else. Sets up the socket pair * and installs the signal handler */ void FAST_FUNC udhcp_sp_setup(void) { /* was socketpair, but it needs AF_UNIX in kernel */ xpiped_pair(signal_pipe); close_on_exec_on(signal_pipe.rd); close_on_exec_on(signal_pipe.wr); ndelay_on(signal_pipe.wr); bb_signals(0 + (1 << SIGUSR1) + (1 << SIGUSR2) + (1 << SIGTERM) , signal_handler); }
void FAST_FUNC open_transformer(int fd, const char *transform_prog) #endif { struct fd_pair fd_pipe; int pid; xpiped_pair(fd_pipe); pid = BB_MMU ? xfork() : xvfork(); if (pid == 0) { /* Child */ close(fd_pipe.rd); /* we don't want to read from the parent */ // FIXME: error check? #if BB_MMU { transformer_aux_data_t aux; init_transformer_aux_data(&aux); aux.check_signature = check_signature; transformer(&aux, fd, fd_pipe.wr); if (ENABLE_FEATURE_CLEAN_UP) { close(fd_pipe.wr); /* send EOF */ close(fd); } /* must be _exit! bug was actually seen here */ _exit(EXIT_SUCCESS); } #else { char *argv[4]; xmove_fd(fd, 0); xmove_fd(fd_pipe.wr, 1); argv[0] = (char*)transform_prog; argv[1] = (char*)"-cf"; argv[2] = (char*)"-"; argv[3] = NULL; BB_EXECVP(transform_prog, argv); bb_perror_msg_and_die("can't execute '%s'", transform_prog); } #endif /* notreached */ } /* parent process */ close(fd_pipe.wr); /* don't want to write to the child */ xmove_fd(fd_pipe.rd, fd); }
/* Don't inline: vfork scares gcc and pessimizes code */ static void NOINLINE vfork_compressor(int tar_fd, int gzip) { pid_t gzipPid; #if ENABLE_FEATURE_SEAMLESS_GZ && ENABLE_FEATURE_SEAMLESS_BZ2 const char *zip_exec = (gzip == 1) ? "gzip" : "bzip2"; #elif ENABLE_FEATURE_SEAMLESS_GZ const char *zip_exec = "gzip"; #else /* only ENABLE_FEATURE_SEAMLESS_BZ2 */ const char *zip_exec = "bzip2"; #endif // On Linux, vfork never unpauses parent early, although standard // allows for that. Do we want to waste bytes checking for it? #define WAIT_FOR_CHILD 0 volatile int vfork_exec_errno = 0; struct fd_pair gzipDataPipe; #if WAIT_FOR_CHILD struct fd_pair gzipStatusPipe; xpiped_pair(gzipStatusPipe); #endif xpiped_pair(gzipDataPipe); signal(SIGPIPE, SIG_IGN); /* we only want EPIPE on errors */ #if defined(__GNUC__) && __GNUC__ /* Avoid vfork clobbering */ (void) &zip_exec; #endif gzipPid = vfork(); if (gzipPid < 0) bb_perror_msg_and_die("vfork"); if (gzipPid == 0) { /* child */ /* NB: close _first_, then move fds! */ close(gzipDataPipe.wr); #if WAIT_FOR_CHILD close(gzipStatusPipe.rd); /* gzipStatusPipe.wr will close only on exec - * parent waits for this close to happen */ fcntl(gzipStatusPipe.wr, F_SETFD, FD_CLOEXEC); #endif xmove_fd(gzipDataPipe.rd, 0); xmove_fd(tar_fd, 1); /* exec gzip/bzip2 program/applet */ BB_EXECLP(zip_exec, zip_exec, "-f", NULL); vfork_exec_errno = errno; _exit(EXIT_FAILURE); } /* parent */ xmove_fd(gzipDataPipe.wr, tar_fd); close(gzipDataPipe.rd); #if WAIT_FOR_CHILD close(gzipStatusPipe.wr); while (1) { char buf; int n; /* Wait until child execs (or fails to) */ n = full_read(gzipStatusPipe.rd, &buf, 1); if (n < 0 /* && errno == EAGAIN */) continue; /* try it again */ } close(gzipStatusPipe.rd); #endif if (vfork_exec_errno) { errno = vfork_exec_errno; bb_perror_msg_and_die("can't execute '%s'", zip_exec); } }
static int writeTarFile(const int tar_fd, const int verboseFlag, const unsigned long dereferenceFlag, const llist_t *include, const llist_t *exclude, const int gzip) { pid_t gzipPid = 0; int errorFlag = FALSE; struct TarBallInfo tbInfo; tbInfo.hlInfoHead = NULL; fchmod(tar_fd, 0644); tbInfo.tarFd = tar_fd; tbInfo.verboseFlag = verboseFlag; /* Store the stat info for the tarball's file, so * can avoid including the tarball into itself.... */ if (fstat(tbInfo.tarFd, &tbInfo.statBuf) < 0) bb_perror_msg_and_die("cannot stat tar file"); #if ENABLE_FEATURE_TAR_GZIP || ENABLE_FEATURE_TAR_BZIP2 if (gzip) { #if ENABLE_FEATURE_TAR_GZIP && ENABLE_FEATURE_TAR_BZIP2 const char *zip_exec = (gzip == 1) ? "gzip" : "bzip2"; #elif ENABLE_FEATURE_TAR_GZIP const char *zip_exec = "gzip"; #else /* only ENABLE_FEATURE_TAR_BZIP2 */ const char *zip_exec = "bzip2"; #endif // On Linux, vfork never unpauses parent early, although standard // allows for that. Do we want to waste bytes checking for it? #define WAIT_FOR_CHILD 0 volatile int vfork_exec_errno = 0; #if WAIT_FOR_CHILD struct fd_pair gzipStatusPipe; #endif struct fd_pair gzipDataPipe; xpiped_pair(gzipDataPipe); #if WAIT_FOR_CHILD xpiped_pair(gzipStatusPipe); #endif signal(SIGPIPE, SIG_IGN); /* we only want EPIPE on errors */ #if defined(__GNUC__) && __GNUC__ /* Avoid vfork clobbering */ (void) &include; (void) &errorFlag; (void) &zip_exec; #endif gzipPid = vfork(); if (gzipPid < 0) bb_perror_msg_and_die("vfork gzip"); if (gzipPid == 0) { /* child */ /* NB: close _first_, then move fds! */ close(gzipDataPipe.wr); #if WAIT_FOR_CHILD close(gzipStatusPipe.rd); /* gzipStatusPipe.wr will close only on exec - * parent waits for this close to happen */ fcntl(gzipStatusPipe.wr, F_SETFD, FD_CLOEXEC); #endif xmove_fd(gzipDataPipe.rd, 0); xmove_fd(tbInfo.tarFd, 1); /* exec gzip/bzip2 program/applet */ BB_EXECLP(zip_exec, zip_exec, "-f", NULL); vfork_exec_errno = errno; _exit(1); } /* parent */ xmove_fd(gzipDataPipe.wr, tbInfo.tarFd); close(gzipDataPipe.rd); #if WAIT_FOR_CHILD close(gzipStatusPipe.wr); while (1) { char buf; int n; /* Wait until child execs (or fails to) */ n = full_read(gzipStatusPipe.rd, &buf, 1); if (n < 0 /* && errno == EAGAIN */) continue; /* try it again */ } close(gzipStatusPipe.rd); #endif if (vfork_exec_errno) { errno = vfork_exec_errno; bb_perror_msg_and_die("cannot exec %s", zip_exec); } } #endif tbInfo.excludeList = exclude; /* Read the directory/files and iterate over them one at a time */ while (include) { if (!recursive_action(include->data, ACTION_RECURSE | (dereferenceFlag ? ACTION_FOLLOWLINKS : 0), writeFileToTarball, writeFileToTarball, &tbInfo, 0)) { errorFlag = TRUE; } include = include->link; } /* Write two empty blocks to the end of the archive */ memset(block_buf, 0, 2*TAR_BLOCK_SIZE); xwrite(tbInfo.tarFd, block_buf, 2*TAR_BLOCK_SIZE); /* To be pedantically correct, we would check if the tarball * is smaller than 20 tar blocks, and pad it if it was smaller, * but that isn't necessary for GNU tar interoperability, and * so is considered a waste of space */ /* Close so the child process (if any) will exit */ close(tbInfo.tarFd); /* Hang up the tools, close up shop, head home */ if (ENABLE_FEATURE_CLEAN_UP) freeHardLinkInfo(&tbInfo.hlInfoHead); if (errorFlag) bb_error_msg("error exit delayed from previous errors"); if (gzipPid) { #if ENABLE_FEATURE_TAR_GZIP || ENABLE_FEATURE_TAR_BZIP2 int status; if (safe_waitpid(gzipPid, &status, 0) == -1) bb_perror_msg("waitpid"); else if (!WIFEXITED(status) || WEXITSTATUS(status)) /* gzip was killed or has exited with nonzero! */ errorFlag = TRUE; #endif } return errorFlag; }
int runsv_main(int argc UNUSED_PARAM, char **argv) { struct stat s; int fd; int r; char buf[256]; INIT_G(); dir = single_argv(argv); xpiped_pair(selfpipe); close_on_exec_on(selfpipe.rd); close_on_exec_on(selfpipe.wr); ndelay_on(selfpipe.rd); ndelay_on(selfpipe.wr); sig_block(SIGCHLD); bb_signals_recursive_norestart(1 << SIGCHLD, s_child); sig_block(SIGTERM); bb_signals_recursive_norestart(1 << SIGTERM, s_term); xchdir(dir); /* bss: svd[0].pid = 0; */ if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */ if (C_NOOP) svd[0].ctrl = C_NOOP; if (W_UP) svd[0].sd_want = W_UP; /* bss: svd[0].islog = 0; */ /* bss: svd[1].pid = 0; */ gettimeofday_ns(&svd[0].start); if (stat("down", &s) != -1) svd[0].sd_want = W_DOWN; if (stat("log", &s) == -1) { if (errno != ENOENT) warn_cannot("stat ./log"); } else { if (!S_ISDIR(s.st_mode)) { errno = 0; warn_cannot("stat log/down: log is not a directory"); } else { haslog = 1; svd[1].state = S_DOWN; svd[1].ctrl = C_NOOP; svd[1].sd_want = W_UP; svd[1].islog = 1; gettimeofday_ns(&svd[1].start); if (stat("log/down", &s) != -1) svd[1].sd_want = W_DOWN; xpiped_pair(logpipe); close_on_exec_on(logpipe.rd); close_on_exec_on(logpipe.wr); } } if (mkdir("supervise", 0700) == -1) { r = readlink("supervise", buf, sizeof(buf)); if (r != -1) { if (r == sizeof(buf)) fatal2x_cannot("readlink ./supervise", ": name too long"); buf[r] = 0; mkdir(buf, 0700); } else { if ((errno != ENOENT) && (errno != EINVAL)) fatal_cannot("readlink ./supervise"); } } svd[0].fdlock = xopen3("log/supervise/lock"+4, O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600); if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1) fatal_cannot("lock supervise/lock"); close_on_exec_on(svd[0].fdlock); if (haslog) { if (mkdir("log/supervise", 0700) == -1) { r = readlink("log/supervise", buf, 256); if (r != -1) { if (r == 256) fatal2x_cannot("readlink ./log/supervise", ": name too long"); buf[r] = 0; fd = xopen(".", O_RDONLY|O_NDELAY); xchdir("./log"); mkdir(buf, 0700); if (fchdir(fd) == -1) fatal_cannot("change back to service directory"); close(fd); } else { if ((errno != ENOENT) && (errno != EINVAL)) fatal_cannot("readlink ./log/supervise"); } } svd[1].fdlock = xopen3("log/supervise/lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600); if (flock(svd[1].fdlock, LOCK_EX) == -1) fatal_cannot("lock log/supervise/lock"); close_on_exec_on(svd[1].fdlock); } mkfifo("log/supervise/control"+4, 0600); svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY); close_on_exec_on(svd[0].fdcontrol); svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY); close_on_exec_on(svd[0].fdcontrolwrite); update_status(&svd[0]); if (haslog) { mkfifo("log/supervise/control", 0600); svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY); close_on_exec_on(svd[1].fdcontrol); svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY); close_on_exec_on(svd[1].fdcontrolwrite); update_status(&svd[1]); } mkfifo("log/supervise/ok"+4, 0600); fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY); close_on_exec_on(fd); if (haslog) { mkfifo("log/supervise/ok", 0600); fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY); close_on_exec_on(fd); } for (;;) { struct pollfd x[3]; unsigned deadline; char ch; if (haslog) if (!svd[1].pid && svd[1].sd_want == W_UP) startservice(&svd[1]); if (!svd[0].pid) if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH) startservice(&svd[0]); x[0].fd = selfpipe.rd; x[0].events = POLLIN; x[1].fd = svd[0].fdcontrol; x[1].events = POLLIN; /* x[2] is used only if haslog == 1 */ x[2].fd = svd[1].fdcontrol; x[2].events = POLLIN; sig_unblock(SIGTERM); sig_unblock(SIGCHLD); poll(x, 2 + haslog, 3600*1000); sig_block(SIGTERM); sig_block(SIGCHLD); while (read(selfpipe.rd, &ch, 1) == 1) continue; for (;;) { pid_t child; int wstat; child = wait_any_nohang(&wstat); if (!child) break; if ((child == -1) && (errno != EINTR)) break; if (child == svd[0].pid) { svd[0].wstat = wstat; svd[0].pid = 0; pidchanged = 1; svd[0].ctrl &= ~C_TERM; if (svd[0].state != S_FINISH) { fd = open("finish", O_RDONLY|O_NDELAY); if (fd != -1) { close(fd); svd[0].state = S_FINISH; update_status(&svd[0]); continue; } } svd[0].state = S_DOWN; deadline = svd[0].start.tv_sec + 1; gettimeofday_ns(&svd[0].start); update_status(&svd[0]); if (LESS(svd[0].start.tv_sec, deadline)) sleep(1); } if (haslog) { if (child == svd[1].pid) { svd[0].wstat = wstat; svd[1].pid = 0; pidchanged = 1; svd[1].state = S_DOWN; svd[1].ctrl &= ~C_TERM; deadline = svd[1].start.tv_sec + 1; gettimeofday_ns(&svd[1].start); update_status(&svd[1]); if (LESS(svd[1].start.tv_sec, deadline)) sleep(1); } } } /* for (;;) */ if (read(svd[0].fdcontrol, &ch, 1) == 1) ctrl(&svd[0], ch); if (haslog) if (read(svd[1].fdcontrol, &ch, 1) == 1) ctrl(&svd[1], ch); if (sigterm) { ctrl(&svd[0], 'x'); sigterm = 0; } if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) { if (svd[1].pid == 0) _exit(EXIT_SUCCESS); if (svd[1].sd_want != W_EXIT) { svd[1].sd_want = W_EXIT; /* stopservice(&svd[1]); */ update_status(&svd[1]); close(logpipe.wr); close(logpipe.rd); } } } /* for (;;) */ /* not reached */ return 0; }
static int run_script(const char *action) { char *argv[5]; int r; bb_error_msg("executing '%s %s %s'", G.script_name, G.iface, action); #if 1 argv[0] = (char*) G.script_name; argv[1] = (char*) G.iface; argv[2] = (char*) action; argv[3] = (char*) G.extra_arg; argv[4] = NULL; /* r < 0 - can't exec, 0 <= r < 1000 - exited, >1000 - killed by sig (r-1000) */ r = wait4pid(spawn(argv)); bb_error_msg("exit code: %d", r); return (option_mask32 & FLAG_IGNORE_RETVAL) ? 0 : r; #else /* insanity */ struct fd_pair pipe_pair; char buf[256]; int i = 0; xpiped_pair(pipe_pair); pid = vfork(); if (pid < 0) { bb_perror_msg("fork"); return -1; } /* child */ if (pid == 0) { xmove_fd(pipe_pair.wr, 1); xdup2(1, 2); if (pipe_pair.rd > 2) close(pipe_pair.rd); // umask(0022); // Set up a sane umask execlp(G.script_name, G.script_name, G.iface, action, G.extra_arg, NULL); _exit(EXIT_FAILURE); } /* parent */ close(pipe_pair.wr); while (1) { if (bb_got_signal && bb_got_signal != SIGCHLD) { bb_error_msg("killing child"); kill(pid, SIGTERM); bb_got_signal = 0; break; } r = read(pipe_pair.rd, &buf[i], 1); if (buf[i] == '\n' || i == sizeof(buf)-2 || r != 1) { if (r == 1 && buf[i] != '\n') i++; buf[i] = '\0'; if (i > 0) bb_error_msg("client: %s", buf); i = 0; } else { i++; } if (r != 1) break; } close(pipe_pair.rd); wait(&r); if (!WIFEXITED(r) || WEXITSTATUS(r) != 0) { bb_error_msg("program execution failed, return value is %i", WEXITSTATUS(r)); return option_mask32 & FLAG_IGNORE_RETVAL ? 0 : WEXITSTATUS(r); } bb_error_msg("program executed successfully"); return 0; #endif }