/* * Unblock all signals blocked by finit when starting children */ void sig_unblock(void) { int i; sigset_t nmask; struct sigaction sa; sigemptyset(&nmask); sigaddset(&nmask, SIGCHLD); sigaddset(&nmask, SIGINT); sigaddset(&nmask, SIGPWR); sigaddset(&nmask, SIGSTOP); sigaddset(&nmask, SIGTSTP); sigaddset(&nmask, SIGCONT); sigprocmask(SIG_UNBLOCK, &nmask, NULL); /* Reset signal handlers that were set by the parent process */ for (i = 1; i < NSIG; i++) DFLSIG(sa, i, 0); }
/* Remember: service_enabled() must be called before calling service_start() */ int service_start(svc_t *svc) { int respawn, sd = 0; pid_t pid; sigset_t nmask, omask; if (!svc) return 1; respawn = svc->pid != 0; /* Don't try and start service if it doesn't exist. */ if (!fexist(svc->cmd) && !svc->inetd.cmd) { if (verbose) { char msg[80]; snprintf(msg, sizeof(msg), "Service %s does not exist!", svc->cmd); print_desc("", msg); print_result(1); } return 1; } /* Ignore if finit is SIGSTOP'ed */ if (is_norespawn()) return 0; #ifndef INETD_DISABLED if (svc_is_inetd(svc)) { char ifname[IF_NAMESIZE] = "UNKNOWN"; sd = svc->inetd.watcher.fd; if (svc->inetd.type == SOCK_STREAM) { /* Open new client socket from server socket */ sd = accept(sd, NULL, NULL); if (sd < 0) { FLOG_PERROR("Failed accepting inetd service %d/tcp", svc->inetd.port); return 1; } _d("New client socket %d accepted for inetd service %d/tcp", sd, svc->inetd.port); /* Find ifname by means of getsockname() and getifaddrs() */ inetd_stream_peek(sd, ifname); } else { /* SOCK_DGRAM */ /* Find ifname by means of IP_PKTINFO sockopt --> ifindex + if_indextoname() */ inetd_dgram_peek(sd, ifname); } if (!inetd_is_allowed(&svc->inetd, ifname)) { FLOG_INFO("Service %s on port %d not allowed from interface %s.", svc->inetd.name, svc->inetd.port, ifname); if (svc->inetd.type == SOCK_STREAM) close(sd); return 1; } FLOG_INFO("Starting inetd service %s for requst from iface %s ...", svc->inetd.name, ifname); } else #endif if (verbose) { if (svc_is_daemon(svc)) print_desc("", svc->desc); else if (!respawn) print_desc("Starting ", svc->desc); } /* Block sigchild while forking. */ sigemptyset(&nmask); sigaddset(&nmask, SIGCHLD); sigprocmask(SIG_BLOCK, &nmask, &omask); pid = fork(); sigprocmask(SIG_SETMASK, &omask, NULL); if (pid == 0) { int i = 0; int status; #ifdef ENABLE_STATIC int uid = 0; /* XXX: Fix better warning that dropprivs is disabled. */ #else int uid = getuser(svc->username); #endif struct sigaction sa; char *args[MAX_NUM_SVC_ARGS]; sigemptyset(&nmask); sigaddset(&nmask, SIGCHLD); sigprocmask(SIG_UNBLOCK, &nmask, NULL); /* Reset signal handlers that were set by the parent process */ for (i = 1; i < NSIG; i++) DFLSIG(sa, i, 0); /* Set desired user */ if (uid >= 0) { setuid(uid); /* Set default path for regular users */ if (uid > 0) setenv("PATH", _PATH_DEFPATH, 1); } /* Serve copy of args to process in case it modifies them. */ for (i = 0; i < (MAX_NUM_SVC_ARGS - 1) && svc->args[i][0] != 0; i++) args[i] = svc->args[i]; args[i] = NULL; /* Redirect inetd socket to stdin for service */ if (svc_is_inetd(svc)) { /* sd set previously */ dup2(sd, STDIN_FILENO); close(sd); dup2(STDIN_FILENO, STDOUT_FILENO); dup2(STDIN_FILENO, STDERR_FILENO); } else if (debug) { int fd; char buf[CMD_SIZE] = ""; fd = open(CONSOLE, O_WRONLY | O_APPEND); if (-1 != fd) { dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); close(fd); } for (i = 0; i < MAX_NUM_SVC_ARGS && args[i]; i++) { char arg[MAX_ARG_LEN]; snprintf(arg, sizeof(arg), "%s ", args[i]); if (strlen(arg) < (sizeof(buf) - strlen(buf))) strcat(buf, arg); } _e("%starting %s: %s", respawn ? "Res" : "S", svc->cmd, buf); } if (svc->inetd.cmd) status = svc->inetd.cmd(svc->inetd.type); else status = execv(svc->cmd, args); /* XXX: Maybe use execve() to be able to launch scripts? */ if (svc_is_inetd(svc)) { if (svc->inetd.type == SOCK_STREAM) { close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); } } exit(status); } svc->pid = pid; if (svc_is_inetd(svc)) { if (svc->inetd.type == SOCK_STREAM) close(sd); } else { int result; if (SVC_TYPE_RUN == svc->type) result = WEXITSTATUS(complete(svc->cmd, pid)); else if (!respawn) result = svc->pid > 1 ? 0 : 1; else result = 0; if (verbose) print_result(result); } return 0; }
int run(char *cmd) { int status, result, i = 0; char *args[NUM_ARGS + 1], *arg, *backup; pid_t pid; /* We must create a copy that is possible to modify. */ backup = arg = strdup(cmd); if (!arg) return 1; /* Failed allocating a string to be modified. */ /* Split command line into tokens of an argv[] array. */ args[i++] = strsep(&arg, "\t "); while (arg && i < NUM_ARGS) { /* Handle run("su -c \"dbus-daemon --system\" messagebus"); * => "su", "-c", "\"dbus-daemon --system\"", "messagebus" */ if (*arg == '\'' || *arg == '"') { char *p, delim[2] = " "; delim[0] = arg[0]; args[i++] = arg++; strsep(&arg, delim); p = arg - 1; *p = *delim; *arg++ = 0; } else { args[i++] = strsep(&arg, "\t "); } } args[i] = NULL; #if 0 _e("Splitting: '%s' =>", cmd); for (i = 0; args[i]; i++) _e("\t%s", args[i]); #endif if (i == NUM_ARGS && args[i]) { _e("Command too long: %s", cmd); free(backup); errno = EOVERFLOW; return 1; } pid = fork(); if (0 == pid) { int i; FILE *fp; struct sigaction sa; /* Reset signal handlers that were set by the parent process */ for (i = 1; i < NSIG; i++) DFLSIG(sa, i, 0); /* Always redirect stdio for run() */ fp = fopen("/dev/null", "w"); if (fp) { int fd = fileno(fp); dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); } sig_unblock(); execvp(args[0], args); _exit(1); /* Only if execv() fails. */ } else if (-1 == pid) { _pe("%s", args[0]); free(backup); return -1; } status = complete(args[0], pid); if (-1 == status) { free(backup); return 1; } result = WEXITSTATUS(status); if (WIFEXITED(status)) { _d("Started %s and ended OK: %d", args[0], result); } else if (WIFSIGNALED(status)) { _d("Process %s terminated by signal %d", args[0], WTERMSIG(status)); if (!result) result = 1; /* Must alert callee that the command did complete successfully. * This is necessary since not all programs trap signals and * change their return code accordingly. --Jocke */ } free(backup); return result; }
/* Remember: svc_enabled() must be called before calling svc_start() */ int svc_start(svc_t *svc) { int respawn = svc->pid != 0; pid_t pid; sigset_t nmask, omask; char *args[MAX_NUM_SVC_ARGS]; if (!svc) return 0; /* Don't try and start service if it doesn't exist. */ if (!fexist(svc->cmd)) { char msg[80]; snprintf(msg, sizeof(msg), "Service %s does not exist!", svc->cmd); print_desc("", msg); print_result(1); return 0; } /* Ignore if finit is SIGSTOP'ed */ if (is_norespawn()) return 0; if (!respawn) print_desc("Starting ", svc->desc); /* Block sigchild while forking. */ sigemptyset(&nmask); sigaddset(&nmask, SIGCHLD); sigprocmask(SIG_BLOCK, &nmask, &omask); pid = fork(); sigprocmask(SIG_SETMASK, &omask, NULL); if (pid == 0) { int i = 0; int uid = getuser(svc->username); struct sigaction sa; sigemptyset(&nmask); sigaddset(&nmask, SIGCHLD); sigprocmask(SIG_UNBLOCK, &nmask, NULL); /* Reset signal handlers that were set by the parent process */ for (i = 1; i < NSIG; i++) DFLSIG(sa, i, 0); /* Set desired user */ if (uid >= 0) setuid(uid); /* Serve copy of args to process in case it modifies them. */ for (i = 0; svc->args[i][0] != 0 && i < MAX_NUM_SVC_ARGS; i++) args[i] = svc->args[i]; args[i] = NULL; if (debug) { int fd; char buf[CMD_SIZE] = ""; fd = open(CONSOLE, O_WRONLY | O_APPEND); if (-1 != fd) { dup2(STDOUT_FILENO, fd); dup2(STDERR_FILENO, fd); } for (i = 0; args[i] && i < MAX_NUM_SVC_ARGS; i++) { char arg[MAX_ARG_LEN]; snprintf(arg, sizeof(arg), "%s ", args[i]); if (strlen(arg) < (sizeof(buf) - strlen(buf))) strcat(buf, arg); } _e("%starting %s: %s", respawn ? "Res" : "S", svc->cmd, buf); } /* XXX: Maybe change to use execve() to be able to launch scripts? */ execv(svc->cmd, args); /* Only reach this point if exec() fails. */ exit(0); } svc->pid = pid; if (!respawn) print_result(svc->pid > 1 ? 0 : 1); return 0; }