void li_event_child_init(liEventLoop *loop, liEventChild *child, liEventCallback callback, int pid) { memset(child, 0, sizeof(*child)); child->base.type = LI_EVT_CHILD; child->base.keep_loop_alive = 1; child->base.callback = callback; ev_init(&child->libevmess.w, NULL); ev_child_set(&child->libevmess.child, pid, 0); ev_set_cb(&child->libevmess.child, event_child_cb); if (NULL != loop) li_event_attach(loop, child); li_event_start(child); }
static void spawn(child* c) { pid_t pid; if (c->tries++ > opts.retry) { g_printerr("Child[%i] died to often, not forking again\n", c->id); return; } switch (pid = fork()) { case -1: g_printerr("Fatal Error: Couldn't fork child[%i]: %s\n", c->id, g_strerror(errno)); if (0 == c->d->running) { g_printerr("No child running and fork failed -> exit\n"); c->d->return_status = -100; ev_unloop(c->d->loop, EVUNLOOP_ALL); } /* Do not retry... */ break; case 0: /* child */ /* Need to reset the signal mask; signal actions don't need to be reset * according to libev documentation: * http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#The_special_problem_of_inheritance_o */ { sigset_t set; sigemptyset(&set); sigprocmask(SIG_SETMASK, &set, NULL); } execv(opts.app[0], opts.app); g_printerr("Exec failed: %s\n", g_strerror(errno)); exit(errno); break; default: c->pid = pid; c->d->running++; c->last_spawn = ev_now(c->d->loop); ev_child_set(&c->watcher, c->pid, 0); ev_child_start(c->d->loop, &c->watcher); break; } }
void exit_cb(struct ev_loop* loop, struct ev_child* cwatcher, int status) { struct worker_s* worker = container_of(cwatcher, struct worker_s, cwatcher); ev_child_stop(loop, cwatcher); err("worker[pid:%d] exit with status:%d, stop_moniter:%d", worker->pid, cwatcher->rstatus, worker->listener->stop_moniter); struct timeval tv; gettimeofday(&tv, 0); if (worker->listener->stop_moniter || 2 > ((int)tv.tv_sec - worker->starttime)) { return; } worker->pid = spawn_worker(worker); if (-1 == worker->pid) { err("spawn worker failed, worker_id:%d", worker->worker_id); exit(EXIT_FAILURE); } err("worker %d restart, new pid: %d", worker->worker_id, worker->pid); ev_child_set(cwatcher, worker->pid, 0); ev_child_start(loop, cwatcher); }
static void mon_interval_cb(struct ev_loop* loop, ev_timer* w, int revents V_UNUSED) { dmn_assert(loop); dmn_assert(w); dmn_assert(revents == EV_TIMER); mon_t* this_mon = w->data; dmn_assert(!this_mon->result_pending); this_mon->cmd_pid = fork(); if(this_mon->cmd_pid == -1) log_fatal("fork() failed: %s", dmn_strerror(errno)); if(!this_mon->cmd_pid) { // child // technically, we could go ahead and close off stdout/stderr // here for the "startfg" case, but why bother? If the user // is debugging via startfg they might want to see this crap anyways. execv(this_mon->cmd->args[0], (char* const *)this_mon->cmd->args); log_fatal("execv(%s, ...) failed: %s", this_mon->cmd->args[0], dmn_strerror(errno)); } this_mon->result_pending = true; ev_timer_set(this_mon->cmd_timeout, this_mon->cmd->timeout, 0); ev_timer_start(loop, this_mon->cmd_timeout); ev_child_set(this_mon->child_watcher, this_mon->cmd_pid, 0); ev_child_start(loop, this_mon->child_watcher); }
static void mon_interval_cb(struct ev_loop* loop, ev_timer* w, int revents V_UNUSED) { dmn_assert(loop); dmn_assert(w); dmn_assert(revents == EV_TIMER); mon_t* this_mon = w->data; dmn_assert(!this_mon->result_pending); if (this_mon->cmd->max_proc > 0 && num_proc >= this_mon->cmd->max_proc) { // If more than max_proc processes are running, reschedule excess // checks to run 0.1 seconds later. After a few passes, this will // smooth the schedule out to prevent a thundering herd. ev_timer_stop(loop, this_mon->interval_timer); ev_timer_set(this_mon->interval_timer, 0.1, this_mon->cmd->interval); ev_timer_start(loop, this_mon->interval_timer); return; } // Before forking, block all signals and save the old mask // to avoid a race condition where local sighandlers execute // in the child between fork and exec(). sigset_t all_sigs; sigfillset(&all_sigs); sigset_t saved_mask; sigemptyset(&saved_mask); if(pthread_sigmask(SIG_SETMASK, &all_sigs, &saved_mask)) log_fatal("pthread_sigmask() failed"); this_mon->cmd_pid = fork(); if(this_mon->cmd_pid == -1) log_fatal("fork() failed: %s", dmn_logf_strerror(errno)); if(!this_mon->cmd_pid) { // child // reset all signal handlers to default before unblocking struct sigaction defaultme; sigemptyset(&defaultme.sa_mask); defaultme.sa_handler = SIG_DFL; defaultme.sa_flags = 0; // we really don't care about error retvals here for(int i = 0; i < NSIG; i++) (void)sigaction(i, &defaultme, NULL); // unblock all sigset_t no_sigs; sigemptyset(&no_sigs); if(pthread_sigmask(SIG_SETMASK, &no_sigs, NULL)) log_fatal("pthread_sigmask() failed"); // technically, we could go ahead and close off stdout/stderr // here for the "startfg" case, but why bother? If the user // is debugging via startfg they might want to see this crap anyways. execv(this_mon->cmd->args[0], this_mon->cmd->args); log_fatal("execv(%s, ...) failed: %s", this_mon->cmd->args[0], dmn_logf_strerror(errno)); } num_proc++; // restore previous signal mask from before fork in parent if(pthread_sigmask(SIG_SETMASK, &saved_mask, NULL)) log_fatal("pthread_sigmask() failed"); this_mon->result_pending = true; ev_timer_set(this_mon->cmd_timeout, this_mon->cmd->timeout, 0); ev_timer_start(loop, this_mon->cmd_timeout); ev_child_set(this_mon->child_watcher, this_mon->cmd_pid, 0); ev_child_start(loop, this_mon->child_watcher); }