int setup_workers(struct ev_loop* loop, struct listener_s* listener) { char* ip = listener->argv[1]; uint16_t port = (uint16_t)atoi(listener->argv[2]); int listen_fd; #ifndef SO_REUSEPORT listen_fd = start_listen(ip, port); #endif pid_t pid; int i; struct worker_s* workers = listener->workers; for (i = 0; i < listener->worker_count; i++) { #ifdef SO_REUSEPORT listen_fd = start_listen(ip, port); #endif workers[i].listen_fd = listen_fd; workers[i].worker_id = i; workers[i].listener = listener; pid = spawn_worker(&workers[i]); if (pid < 0) { return -1; } workers[i].pid = pid; ev_child_init(&workers[i].cwatcher, exit_cb, pid, 0); ev_child_start(loop, &workers[i].cwatcher); } return 0; }
/* * Starts an i3-nagbar instance with the given parameters. Takes care of * handling SIGCHLD and killing i3-nagbar when i3 exits. * * The resulting PID will be stored in *nagbar_pid and can be used with * kill_nagbar() to kill the bar later on. * */ void start_nagbar(pid_t *nagbar_pid, char *argv[]) { if (*nagbar_pid != -1) { DLOG("i3-nagbar already running (PID %d), not starting again.\n", *nagbar_pid); return; } *nagbar_pid = fork(); if (*nagbar_pid == -1) { warn("Could not fork()"); return; } /* child */ if (*nagbar_pid == 0) exec_i3_utility("i3-nagbar", argv); DLOG("Starting i3-nagbar with PID %d\n", *nagbar_pid); /* parent */ /* install a child watcher */ ev_child *child = smalloc(sizeof(ev_child)); ev_child_init(child, &nagbar_exited, *nagbar_pid, 0); child->data = nagbar_pid; ev_child_start(main_loop, child); /* install a cleanup watcher (will be called when i3 exits and i3-nagbar is * still running) */ ev_cleanup *cleanup = smalloc(sizeof(ev_cleanup)); ev_cleanup_init(cleanup, nagbar_cleanup); cleanup->data = nagbar_pid; ev_cleanup_start(main_loop, cleanup); }
static guint watch_child_full (MilterEventLoop *loop, gint priority, GPid pid, GChildWatchFunc function, gpointer data, GDestroyNotify notify) { guint id; ev_child *watcher; ChildWatcherPrivate *watcher_priv; MilterLibevEventLoopPrivate *priv; priv = MILTER_LIBEV_EVENT_LOOP_GET_PRIVATE(loop); watcher_priv = g_new0(ChildWatcherPrivate, 1); watcher_priv->function = function; watcher = g_new0(ev_child, 1); watcher->data = watcher_priv; id = add_watcher(MILTER_LIBEV_EVENT_LOOP(loop), (ev_watcher *)watcher, WATCHER_STOP_FUNC(ev_child_stop), NULL, notify, data); ev_child_init(watcher, child_func, pid, FALSE); ev_child_start(priv->ev_loop, watcher); return id; }
/** * Create a new child object. Arguments: * 1 - callback function. * 2 - pid number (0 for any pid) * 3 - trace (either false - only activate the watcher when the process * terminates or true - additionally activate the watcher when the * process is stopped or continued). * * @see watcher_new() * * [+1, -0, ?] */ static int child_new(lua_State* L) { int pid = luaL_checkint(L, 2); int trace = luaL_checkbool(L, 3); ev_child* child; child = (ev_child*)watcher_new(L, sizeof(ev_child), CHILD_MT); ev_child_init(child, &child_cb, pid, trace); return 1; }
void start_process(procnanny_t *pn, process_t *process) { program_t *program = pn_proc_program(process); program->state_cb = pn_proc_state_cb; pn_proc_start(process); ev_child *newwatcher = calloc(1, sizeof(ev_child)); ev_child_init(newwatcher, child_proc_cb, pn_proc_pid(process), 0); ev_child_start(pn->loop, newwatcher); newwatcher->data = process; pn_proc_watch_io(pn->loop, process); //publish_proc_event(process, "starting"); struct proc_data *procdata = process->data; procdata->running_state_timer = calloc(1, sizeof(ev_timer)); procdata->running_state_timer->data = process; ev_timer_init(procdata->running_state_timer, running_state_timer_cb, program->minimum_run_duration, program->minimum_run_duration); ev_timer_start(pn->loop, procdata->running_state_timer); } /* start_process */
flux_watcher_t *flux_child_watcher_create (flux_reactor_t *r, int pid, bool trace, flux_watcher_f cb, void *arg) { struct watcher_ops ops = { .start = child_start, .stop = child_stop, .destroy = child_destroy, }; flux_watcher_t *w; ev_child *cw; if (!ev_is_default_loop (r->loop)) { errno = EINVAL; return NULL; } cw = xzmalloc (sizeof (*cw)); ev_child_init (cw, child_cb, pid, trace ? 1 : 0); w = flux_watcher_create (r, cw, ops, CHILD_SIG, cb, arg); cw->data = w; return w; }
int main (void) { // use the default event loop unless you have special needs struct ev_loop *loop = EV_DEFAULT; /* OR ev_default_loop(0) */ pid_t pid = fork (); if (pid < 0) { // error perror("fork()"); exit(EXIT_FAILURE); } else if (pid == 0) { // child // the forked child executes here sleep(1); exit (EXIT_SUCCESS); } else { // parent ev_child_init (&cw, child_cb, pid, 0); ev_child_start (EV_DEFAULT_ &cw); } // now wait for events to arrive ev_run (loop, 0); // break was called, so exit return 0; }
int uv_spawn(uv_loop_t* loop, uv_process_t* process, uv_process_options_t options) { /* * Save environ in the case that we get it clobbered * by the child process. */ char** save_our_env = environ; int stdin_pipe[2] = { -1, -1 }; int stdout_pipe[2] = { -1, -1 }; int stderr_pipe[2] = { -1, -1 }; #if SPAWN_WAIT_EXEC int signal_pipe[2] = { -1, -1 }; struct pollfd pfd; #endif int status; pid_t pid; int flags; uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS); loop->counters.process_init++; process->exit_cb = options.exit_cb; if (options.stdin_stream && uv__process_init_pipe(options.stdin_stream, stdin_pipe, 0)) { goto error; } if (options.stdout_stream && uv__process_init_pipe(options.stdout_stream, stdout_pipe, 0)) { goto error; } if (options.stderr_stream && uv__process_init_pipe(options.stderr_stream, stderr_pipe, 0)) { goto error; } /* This pipe is used by the parent to wait until * the child has called `execve()`. We need this * to avoid the following race condition: * * if ((pid = fork()) > 0) { * kill(pid, SIGTERM); * } * else if (pid == 0) { * execve("/bin/cat", argp, envp); * } * * The parent sends a signal immediately after forking. * Since the child may not have called `execve()` yet, * there is no telling what process receives the signal, * our fork or /bin/cat. * * To avoid ambiguity, we create a pipe with both ends * marked close-on-exec. Then, after the call to `fork()`, * the parent polls the read end until it sees POLLHUP. */ #if SPAWN_WAIT_EXEC if (uv__make_pipe(signal_pipe, UV__F_NONBLOCK)) goto error; #endif pid = fork(); if (pid == -1) { #if SPAWN_WAIT_EXEC uv__close(signal_pipe[0]); uv__close(signal_pipe[1]); #endif environ = save_our_env; goto error; } if (pid == 0) { if (stdin_pipe[0] >= 0) { uv__close(stdin_pipe[1]); dup2(stdin_pipe[0], STDIN_FILENO); } else { /* Reset flags that might be set by Node */ uv__cloexec(STDIN_FILENO, 0); uv__nonblock(STDIN_FILENO, 0); } if (stdout_pipe[1] >= 0) { uv__close(stdout_pipe[0]); dup2(stdout_pipe[1], STDOUT_FILENO); } else { /* Reset flags that might be set by Node */ uv__cloexec(STDOUT_FILENO, 0); uv__nonblock(STDOUT_FILENO, 0); } if (stderr_pipe[1] >= 0) { uv__close(stderr_pipe[0]); dup2(stderr_pipe[1], STDERR_FILENO); } else { /* Reset flags that might be set by Node */ uv__cloexec(STDERR_FILENO, 0); uv__nonblock(STDERR_FILENO, 0); } if (options.cwd && chdir(options.cwd)) { perror("chdir()"); _exit(127); } environ = options.env; execvp(options.file, options.args); perror("execvp()"); _exit(127); /* Execution never reaches here. */ } /* Parent. */ /* Restore environment. */ environ = save_our_env; #if SPAWN_WAIT_EXEC /* POLLHUP signals child has exited or execve()'d. */ uv__close(signal_pipe[1]); do { pfd.fd = signal_pipe[0]; pfd.events = POLLIN|POLLHUP; pfd.revents = 0; errno = 0, status = poll(&pfd, 1, -1); } while (status == -1 && (errno == EINTR || errno == ENOMEM)); assert((status == 1) && "poll() on pipe read end failed"); uv__close(signal_pipe[0]); #endif process->pid = pid; ev_child_init(&process->child_watcher, uv__chld, pid, 0); ev_child_start(process->loop->ev, &process->child_watcher); process->child_watcher.data = process; if (stdin_pipe[1] >= 0) { assert(options.stdin_stream); assert(stdin_pipe[0] >= 0); uv__close(stdin_pipe[0]); uv__nonblock(stdin_pipe[1], 1); flags = UV_WRITABLE | (options.stdin_stream->ipc ? UV_READABLE : 0); uv__stream_open((uv_stream_t*)options.stdin_stream, stdin_pipe[1], flags); } if (stdout_pipe[0] >= 0) { assert(options.stdout_stream); assert(stdout_pipe[1] >= 0); uv__close(stdout_pipe[1]); uv__nonblock(stdout_pipe[0], 1); flags = UV_READABLE | (options.stdout_stream->ipc ? UV_WRITABLE : 0); uv__stream_open((uv_stream_t*)options.stdout_stream, stdout_pipe[0], flags); } if (stderr_pipe[0] >= 0) { assert(options.stderr_stream); assert(stderr_pipe[1] >= 0); uv__close(stderr_pipe[1]); uv__nonblock(stderr_pipe[0], 1); flags = UV_READABLE | (options.stderr_stream->ipc ? UV_WRITABLE : 0); uv__stream_open((uv_stream_t*)options.stderr_stream, stderr_pipe[0], flags); } return 0; error: uv__set_sys_error(process->loop, errno); uv__close(stdin_pipe[0]); uv__close(stdin_pipe[1]); uv__close(stdout_pipe[0]); uv__close(stdout_pipe[1]); uv__close(stderr_pipe[0]); uv__close(stderr_pipe[1]); return -1; }
// Handle input from an HTTP socket. void process_http_cb(socket_t *socket) { int used; connection_t *ptr = ilist_fetch(connections, socket->fd); if (ptr == NULL) { printf("%d: Tried to get from list, but got NULL.\n", socket->fd); return; } if (ptr->is_websocket) { // Try to decode a frame. frame_t *f = read_frame(socket->rbuf, socket->rbuf_len, &used); if (f == NULL) { return; } // TODO: Consume instead. bcopy(socket->rbuf, socket->rbuf + used, socket->rbuf_len - used); socket->rbuf_len -= used; char *p = malloc(f->len + 1); strncpy(p, f->payload, f->len); p[f->len] = 0; cmd_t *cmd = parse_msg(p); if (cmd != NULL && cmd->type == CMD_EXECUTE) { process_t *p = execute_cmd((cmd_execute_t *)cmd); ilist_insert(processes, p); sock_pid_t *sp = malloc(sizeof(sock_pid_t)); sp->socket = socket; sp->pid = p; // Write new_process message? char format[] = "{\"newProcess\" : {" "\"pid\" : %d, \"command\" : \"%s\"," "\"startTime\" : %ld," "\"requestId\" : %d}}"; char str[1000]; struct timeval tp; gettimeofday(&tp, NULL); long int time = tp.tv_sec * 1000 + tp.tv_usec; sprintf(str, format, p->pid, ((cmd_execute_t*)cmd)->command_str, time, ((cmd_execute_t*)cmd)->request_id); frame_t *frame = malloc(sizeof(frame_t *)); frame->fin = 1; frame->len = strlen(str); frame->opcode = WS_OP_BIN; frame->payload = str; int fr_len; char *fr_str = write_frame(frame, &fr_len); socket_write(socket, fr_str, fr_len); socket_new(p->out, &process_child_out_cb, sp, socket); ev_child *child_watcher = (struct ev_child*) malloc (sizeof(struct ev_child)); ev_child_init (child_watcher, child_cb, p->pid, 1); child_watcher->data = socket; ev_child_start(loop, child_watcher); } free(f->payload); free(f); free(p); return; } request_t *req = parse_request(socket->rbuf, socket->rbuf_len, &used); if (req) { printf("%d: New Request: %s %s\n", socket->fd, req->method, req->uri); // Take it out of the read buffer. // TODO: Consume bcopy(socket->rbuf, socket->rbuf + used, socket->rbuf_len - used); socket->rbuf_len -= used; // Got a request! int upgraded = try_upgrade(req, ptr); if (upgraded) { printf("%d: Upgraded to WebSocket!\n", socket->fd); } else if (!strcmp(req->uri, "/")) { serve_file(socket, "static/index.html"); } else if (!strcmp(req->uri, "/tnrl.js")) { serve_file(socket, "static/tnrl.js"); } else if (!strcmp(req->uri, "/tnrl.css")) { serve_file(socket, "static/tnrl.css"); } else if (!strcmp(req->uri, "/favicon.ico")) { serve_file(socket, "static/favicon.ico"); } else { // Unknown URL? char *str = "HTTP/1.1 404 Not Found\r\n\r\n"; socket_write(socket, str, strlen(str)); } request_delete(req); } //printf("%s", buf); }
void feng_start_child_watcher(struct feng *srv) { cw.data = srv; ev_child_init (&cw, child_terminate_cb, 0, 0); ev_child_start (srv->loop, &cw); }
int main(int argc, char *argv[]) { static struct option long_options[] = { {"getmonitors_reply", required_argument, 0, 0}, {0, 0, 0, 0}, }; char *options_string = ""; int opt; int option_index = 0; while ((opt = getopt_long(argc, argv, options_string, long_options, &option_index)) != -1) { switch (opt) { case 0: if (strcmp(long_options[option_index].name, "getmonitors_reply") == 0) { must_read_reply(optarg); } break; default: exit(EXIT_FAILURE); } } if (optind >= argc) { errx(EXIT_FAILURE, "syntax: %s [options] <command>\n", argv[0]); } int fd = socket(AF_LOCAL, SOCK_STREAM, 0); if (fd == -1) { err(EXIT_FAILURE, "socket(AF_UNIX)"); } if (fcntl(fd, F_SETFD, FD_CLOEXEC)) { warn("Could not set FD_CLOEXEC"); } struct sockaddr_un addr; memset(&addr, 0, sizeof(struct sockaddr_un)); addr.sun_family = AF_UNIX; int i; bool bound = false; for (i = 0; i < 100; i++) { /* XXX: The path to X11 sockets differs on some platforms (e.g. Trusted * Solaris, HPUX), but since libxcb doesn’t provide a function to * generate the path, we’ll just have to hard-code it for now. */ snprintf(addr.sun_path, sizeof(addr.sun_path), "/tmp/.X11-unix/X%d", i); if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) == -1) { warn("bind(%s)", addr.sun_path); } else { bound = true; /* Let the user know bind() was successful, so that they know the * error messages can be disregarded. */ fprintf(stderr, "Successfuly bound to %s\n", addr.sun_path); sun_path = sstrdup(addr.sun_path); break; } } if (!bound) { err(EXIT_FAILURE, "bind()"); } atexit(cleanup_socket); /* This program will be started for each testcase which requires it, so we * expect precisely one connection. */ if (listen(fd, 1) == -1) { err(EXIT_FAILURE, "listen()"); } pid_t child = fork(); if (child == -1) { err(EXIT_FAILURE, "fork()"); } if (child == 0) { char *display; sasprintf(&display, ":%d", i); setenv("DISPLAY", display, 1); free(display); char **child_args = argv + optind; execvp(child_args[0], child_args); err(EXIT_FAILURE, "exec()"); } struct ev_loop *loop = ev_default_loop(0); ev_child cw; ev_child_init(&cw, child_cb, child, 0); ev_child_start(loop, &cw); ev_io watcher; ev_io_init(&watcher, uds_connection_cb, fd, EV_READ); ev_io_start(loop, &watcher); ev_run(loop, 0); }
static inline void signal_child_watcher_init(void) { ev_child_init(&signal_child_watcher, signal_child_handler, 0, 1); }
int main(int argc, char** argv) { dmn_init_log("gdnsd_extmon_helper", true); // start up syslog IFF it appears the daemon // was *not* started via "startfg". Regular // start/restart would have /dev/null'd the // standard descriptors before forking us off. if(!isatty(0)) dmn_start_syslog(); // Bail out early if we don't have the right argument // count, and try to tell the user not to run us // if stderr happens to be hooked up to a terminal if(argc != 5) { fprintf(stderr, "This binary is not for human execution!\n"); abort(); } // open stderr logging connection using passed fd dmn_log_set_alt_stderr(atoi(argv[2])); // regardless, we seal off stdin now. We don't need it, // and this way we don't have to deal with it when // execv()-ing child commands later. if(!freopen("/dev/null", "r", stdin)) dmn_log_fatal("Cannot open /dev/null: %s", dmn_strerror(errno)); if(!strcmp(argv[1], "Y")) dmn_set_debug(true); else if(!strcmp(argv[1], "N")) dmn_set_debug(false); else log_fatal("Invalid debug argument on cmdline: '%s'!", argv[1]); // these are the main communication pipes to the daemon/plugin plugin_read_fd = atoi(argv[3]); plugin_write_fd = atoi(argv[4]); if(plugin_read_fd < 3 || plugin_read_fd > 1000 || plugin_write_fd < 3 || plugin_write_fd > 1000) log_fatal("Invalid pipe descriptors!"); // CLOEXEC the direct lines to the main plugin/daemon, // so that child scripts can't screw with them. if(fcntl(plugin_read_fd, F_SETFD, FD_CLOEXEC)) log_fatal("Failed to set FD_CLOEXEC on plugin read fd: %s", dmn_strerror(errno)); if(fcntl(plugin_write_fd, F_SETFD, FD_CLOEXEC)) log_fatal("Failed to set FD_CLOEXEC on plugin write fd: %s", dmn_strerror(errno)); if(emc_read_exact(plugin_read_fd, "HELO")) log_fatal("Failed to read HELO from plugin"); if(emc_write_string(plugin_write_fd, "HELO_ACK", 8)) log_fatal("Failed to write HELO_ACK to plugin"); uint8_t ccount_buf[7]; if(emc_read_nbytes(plugin_read_fd, 7, ccount_buf) || strncmp((char*)ccount_buf, "CMDS:", 5)) log_fatal("Failed to read command count from plugin"); num_mons = ((unsigned)ccount_buf[5] << 8) + ccount_buf[6]; if(!num_mons) log_fatal("Received command count of zero from plugin"); mons = calloc(num_mons, sizeof(mon_t)); if(emc_write_string(plugin_write_fd, "CMDS_ACK", 8)) log_fatal("Failed to write CMDS_ACK to plugin"); // Note, it's merely a happy coincidence that our mons[] // indices exactly match cmd->idx numbers. Always use // the cmd->idx numbers as the official index when talking // to the main daemon! for(unsigned i = 0; i < num_mons; i++) { mons[i].cmd = emc_read_command(plugin_read_fd); if(!mons[i].cmd) log_fatal("Failed to read command %u from plugin", i); if(i != mons[i].cmd->idx) log_fatal("BUG: plugin index issues, %u vs %u", i, mons[i].cmd->idx); if(emc_write_string(plugin_write_fd, "CMD_ACK", 7)) log_fatal("Failed to write CMD_ACK for command %u to plugin", i); } if(emc_read_exact(plugin_read_fd, "END_CMDS")) log_fatal("Failed to read END_CMDS from plugin"); if(emc_write_string(plugin_write_fd, "END_CMDS_ACK", 12)) log_fatal("Failed to write END_CMDS_ACK to plugin"); // done with the serial setup, close the readpipe and go nonblocking on write for eventloop... close(plugin_read_fd); if(unlikely(fcntl(plugin_write_fd, F_SETFL, (fcntl(plugin_write_fd, F_GETFL, 0)) | O_NONBLOCK) == -1)) log_fatal("Failed to set O_NONBLOCK on pipe: %s", logf_errno()); // init results-sending queue sendq_init(); // Set up libev error callback ev_set_syserr_cb(&syserr_for_ev); // Construct the default loop for the main thread struct ev_loop* def_loop = ev_default_loop(EVFLAG_AUTO); if(!def_loop) log_fatal("Could not initialize the default libev loop"); ev_set_timeout_collect_interval(def_loop, 0.1); ev_set_io_collect_interval(def_loop, 0.01); // set up primary read/write watchers on the pipe to the daemon's plugin plugin_read_watcher = malloc(sizeof(ev_io)); plugin_write_watcher = malloc(sizeof(ev_io)); ev_io_init(plugin_write_watcher, plugin_write_cb, plugin_write_fd, EV_WRITE); ev_set_priority(plugin_write_watcher, 1); // set up interval watchers for each monitor, initially for immediate firing // for the daemon's monitoring init cycle, then repeating every interval. for(unsigned i = 0; i < num_mons; i++) { mon_t* this_mon = &mons[i]; this_mon->interval_timer = malloc(sizeof(ev_timer)); ev_timer_init(this_mon->interval_timer, mon_interval_cb, 0., this_mon->cmd->interval); this_mon->interval_timer->data = this_mon; ev_set_priority(this_mon->interval_timer, 0); ev_timer_start(def_loop, this_mon->interval_timer); // initialize the other watchers in the mon_t here as well, // but do not start them (the interval callback starts them each interval) this_mon->cmd_timeout = malloc(sizeof(ev_timer)); ev_timer_init(this_mon->cmd_timeout, mon_timeout_cb, 0, 0); ev_set_priority(this_mon->cmd_timeout, -1); this_mon->cmd_timeout->data = this_mon; this_mon->child_watcher = malloc(sizeof(ev_child)); ev_child_init(this_mon->child_watcher, mon_child_cb, 0, 0); this_mon->child_watcher->data = this_mon; } log_info("gdnsd_extmon_helper running"); // shut off stderr output from here out... dmn_log_close_alt_stderr(); ev_run(def_loop, 0); log_info("gdnsd_extmon_helper terminating"); // kill -9 on any extant child procs for(unsigned i = 0; i < num_mons; i++) if(mons[i].cmd_pid) kill(mons[i].cmd_pid, SIGKILL); // Bye! exit(0); }
int main(int argc, char **argv) { GOptionContext *context; GError *error = NULL; gint res; context = g_option_context_new("<application> [app arguments]"); g_option_context_add_main_entries(context, entries, NULL); g_option_context_set_summary(context, PACKAGE_DESC); if (!g_option_context_parse (context, &argc, &argv, &error)) { g_printerr("Option parsing failed: %s\n", error->message); return -1; } if (opts.show_version) { g_printerr(PACKAGE_DESC); g_printerr("\nBuild-Date: " __DATE__ " " __TIME__ "\n"); return 0; } if (!opts.app || !opts.app[0]) { g_printerr("Missing application\n"); return -2; } if (opts.forks < 1) { g_printerr("Invalid forks argument: %i\n", opts.forks); return -3; } if (opts.retry < 1) { g_printerr("Invalid retry argument: %i\n", opts.retry); return -4; } if (opts.retry_timeout_ms < 0) { g_printerr("Invalid timeout argument: %i\n", opts.retry_timeout_ms); return -5; } data *d = g_slice_new0(data); d->children = (child*) g_slice_alloc0(sizeof(child) * opts.forks); d->running = 0; d->shutdown = FALSE; d->return_status = 0; d->loop = ev_default_loop(0); #define WATCH_SIG(x) do { ev_signal_init(&d->sig##x, forward_sig_cb, SIG##x); d->sig##x.data = d; ev_signal_start(d->loop, &d->sig##x); ev_unref(d->loop); } while (0) #define WATCH_TERM_SIG(x) do { ev_signal_init(&d->sig##x, terminate_forward_sig_cb, SIG##x); d->sig##x.data = d; ev_signal_start(d->loop, &d->sig##x); ev_unref(d->loop); } while (0) #define UNWATCH_SIG(x) do { ev_ref(d->loop); ev_signal_stop(d->loop, &d->sig##x); } while (0) WATCH_TERM_SIG(HUP); WATCH_TERM_SIG(INT); WATCH_TERM_SIG(QUIT); WATCH_TERM_SIG(TERM); WATCH_TERM_SIG(USR1); WATCH_SIG(USR2); for (gint i = 0; i < opts.forks; i++) { d->children[i].d = d; d->children[i].id = i; d->children[i].pid = -1; d->children[i].tries = 0; d->children[i].watcher.data = &d->children[i]; ev_child_init(&d->children[i].watcher, child_died, -1, 0); spawn(&d->children[i]); } ev_loop(d->loop, 0); res = d->return_status; g_slice_free1(sizeof(child) * opts.forks, d->children); g_slice_free(data, d); UNWATCH_SIG(HUP); UNWATCH_SIG(INT); UNWATCH_SIG(QUIT); UNWATCH_SIG(TERM); UNWATCH_SIG(USR1); UNWATCH_SIG(USR2); return res; }
int uv_spawn(uv_loop_t* loop, uv_process_t* process, uv_process_options_t options) { /* * Save environ in the case that we get it clobbered * by the child process. */ char** save_our_env = environ; int* pipes = malloc(2 * options.stdio_count * sizeof(int)); #if SPAWN_WAIT_EXEC int signal_pipe[2] = { -1, -1 }; struct pollfd pfd; #endif int status; pid_t pid; int i; if (pipes == NULL) { errno = ENOMEM; goto error; } assert(options.file != NULL); assert(!(options.flags & ~(UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS | UV_PROCESS_DETACHED | UV_PROCESS_SETGID | UV_PROCESS_SETUID))); uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS); loop->counters.process_init++; uv__handle_start(process); process->exit_cb = options.exit_cb; /* Init pipe pairs */ for (i = 0; i < options.stdio_count; i++) { pipes[i * 2] = -1; pipes[i * 2 + 1] = -1; } /* Create socketpairs/pipes, or use raw fd */ for (i = 0; i < options.stdio_count; i++) { if (uv__process_init_stdio(&options.stdio[i], pipes + i * 2, i != 0)) { goto error; } } /* This pipe is used by the parent to wait until * the child has called `execve()`. We need this * to avoid the following race condition: * * if ((pid = fork()) > 0) { * kill(pid, SIGTERM); * } * else if (pid == 0) { * execve("/bin/cat", argp, envp); * } * * The parent sends a signal immediately after forking. * Since the child may not have called `execve()` yet, * there is no telling what process receives the signal, * our fork or /bin/cat. * * To avoid ambiguity, we create a pipe with both ends * marked close-on-exec. Then, after the call to `fork()`, * the parent polls the read end until it sees POLLHUP. */ #if SPAWN_WAIT_EXEC if (uv__make_pipe(signal_pipe, UV__F_NONBLOCK)) goto error; #endif pid = fork(); if (pid == -1) { #if SPAWN_WAIT_EXEC close(signal_pipe[0]); close(signal_pipe[1]); #endif environ = save_our_env; goto error; } if (pid == 0) { /* Child */ if (options.flags & UV_PROCESS_DETACHED) { setsid(); } /* Dup fds */ for (i = 0; i < options.stdio_count; i++) { /* * stdin has swapped ends of pipe * (it's the only one readable stream) */ int close_fd = i == 0 ? pipes[i * 2 + 1] : pipes[i * 2]; int use_fd = i == 0 ? pipes[i * 2] : pipes[i * 2 + 1]; if (use_fd >= 0) { close(close_fd); dup2(use_fd, i); } else { /* Reset flags that might be set by Node */ uv__cloexec(i, 0); uv__nonblock(i, 0); } } if (options.cwd && chdir(options.cwd)) { perror("chdir()"); _exit(127); } if ((options.flags & UV_PROCESS_SETGID) && setgid(options.gid)) { perror("setgid()"); _exit(127); } if ((options.flags & UV_PROCESS_SETUID) && setuid(options.uid)) { perror("setuid()"); _exit(127); } environ = options.env; execvp(options.file, options.args); perror("execvp()"); _exit(127); /* Execution never reaches here. */ } /* Parent. */ /* Restore environment. */ environ = save_our_env; #if SPAWN_WAIT_EXEC /* POLLHUP signals child has exited or execve()'d. */ close(signal_pipe[1]); do { pfd.fd = signal_pipe[0]; pfd.events = POLLIN|POLLHUP; pfd.revents = 0; errno = 0, status = poll(&pfd, 1, -1); } while (status == -1 && (errno == EINTR || errno == ENOMEM)); assert((status == 1) && "poll() on pipe read end failed"); close(signal_pipe[0]); #endif process->pid = pid; ev_child_init(&process->child_watcher, uv__chld, pid, 0); ev_child_start(process->loop->ev, &process->child_watcher); process->child_watcher.data = process; for (i = 0; i < options.stdio_count; i++) { if (uv__process_open_stream(&options.stdio[i], pipes + i * 2, i == 0)) { int j; /* Close all opened streams */ for (j = 0; j < i; j++) { uv__process_close_stream(&options.stdio[j]); } goto error; } } free(pipes); return 0; error: uv__set_sys_error(process->loop, errno); for (i = 0; i < options.stdio_count; i++) { close(pipes[i * 2]); close(pipes[i * 2 + 1]); } free(pipes); return -1; }
int main(int argc, char** argv) { // Bail out early if we don't have the right argument // count, and try to tell the user not to run us // if stderr happens to be hooked up to a terminal if(argc != 5) { fprintf(stderr, "This binary is not for human execution!\n"); exit(99); } bool debug = false; if(!strcmp(argv[1], "Y")) debug = true; bool use_syslog = false; if(!strcmp(argv[2], "S")) use_syslog = true; dmn_init1(debug, true, use_syslog, "gdnsd_extmon_helper"); // Note that gdnsd_initialize() would be standard here, but extmon_helper // is special: it doesn't actually make use of most of libgdnsd, just the // very basic compiler, allocator, and libdmn logging bits. // regardless, we seal off stdin now. We don't need it, // and this way we don't have to deal with it when // execv()-ing child commands later. if(!freopen("/dev/null", "r", stdin)) dmn_log_fatal("Cannot open /dev/null: %s", dmn_logf_strerror(errno)); // Also unconditionally unset NOTIFY_SOCKET here so that children // don't get any ideas about talking to systemd on our behalf. // (we're done using it in this process for libdmn stuff at this point) unsetenv("NOTIFY_SOCKET"); // these are the main communication pipes to the daemon/plugin plugin_read_fd = atoi(argv[3]); plugin_write_fd = atoi(argv[4]); if(plugin_read_fd < 3 || plugin_read_fd > 1000 || plugin_write_fd < 3 || plugin_write_fd > 1000) log_fatal("Invalid pipe descriptors!"); if(emc_read_exact(plugin_read_fd, "HELO")) log_fatal("Failed to read HELO from plugin"); if(emc_write_string(plugin_write_fd, "HELO_ACK", 8)) log_fatal("Failed to write HELO_ACK to plugin"); uint8_t ccount_buf[7]; if(emc_read_nbytes(plugin_read_fd, 7, ccount_buf) || strncmp((char*)ccount_buf, "CMDS:", 5)) log_fatal("Failed to read command count from plugin"); num_mons = ((unsigned)ccount_buf[5] << 8) + ccount_buf[6]; if(!num_mons) log_fatal("Received command count of zero from plugin"); mons = xcalloc(num_mons, sizeof(mon_t)); if(emc_write_string(plugin_write_fd, "CMDS_ACK", 8)) log_fatal("Failed to write CMDS_ACK to plugin"); // Note, it's merely a happy coincidence that our mons[] // indices exactly match cmd->idx numbers. Always use // the cmd->idx numbers as the official index when talking // to the main daemon! for(unsigned i = 0; i < num_mons; i++) { mons[i].cmd = emc_read_command(plugin_read_fd); if(!mons[i].cmd) log_fatal("Failed to read command %u from plugin", i); if(i != mons[i].cmd->idx) log_fatal("BUG: plugin index issues, %u vs %u", i, mons[i].cmd->idx); if(emc_write_string(plugin_write_fd, "CMD_ACK", 7)) log_fatal("Failed to write CMD_ACK for command %u to plugin", i); } if(emc_read_exact(plugin_read_fd, "END_CMDS")) log_fatal("Failed to read END_CMDS from plugin"); if(emc_write_string(plugin_write_fd, "END_CMDS_ACK", 12)) log_fatal("Failed to write END_CMDS_ACK to plugin"); // done with the serial setup, close the readpipe and go nonblocking on write for eventloop... close(plugin_read_fd); if(fcntl(plugin_write_fd, F_SETFL, (fcntl(plugin_write_fd, F_GETFL, 0)) | O_NONBLOCK) == -1) log_fatal("Failed to set O_NONBLOCK on pipe: %s", dmn_logf_errno()); // CLOEXEC the write fd so child scripts can't mess with it if(fcntl(plugin_write_fd, F_SETFD, FD_CLOEXEC)) log_fatal("Failed to set FD_CLOEXEC on plugin write fd: %s", dmn_logf_strerror(errno)); // init results-sending queue sendq_init(); // Set up libev error callback ev_set_syserr_cb(&syserr_for_ev); // Construct the default loop for the main thread struct ev_loop* def_loop = ev_default_loop(EVFLAG_AUTO); if(!def_loop) log_fatal("Could not initialize the default libev loop"); // Catch SIGINT/TERM/HUP, and do not let them prevent loop exit sigterm_watcher = xmalloc(sizeof(ev_signal)); sigint_watcher = xmalloc(sizeof(ev_signal)); sighup_watcher = xmalloc(sizeof(ev_signal)); ev_signal_init(sigterm_watcher, sig_cb, SIGTERM); ev_signal_init(sigint_watcher, sig_cb, SIGINT); ev_signal_init(sighup_watcher, sig_cb, SIGHUP); ev_signal_start(def_loop, sigterm_watcher); ev_signal_start(def_loop, sigint_watcher); ev_signal_start(def_loop, sighup_watcher); ev_unref(def_loop); ev_unref(def_loop); ev_unref(def_loop); // set up primary read/write watchers on the pipe to the daemon's plugin plugin_write_watcher = xmalloc(sizeof(ev_io)); ev_io_init(plugin_write_watcher, plugin_write_cb, plugin_write_fd, EV_WRITE); ev_set_priority(plugin_write_watcher, 1); // set up interval watchers for each monitor, initially for immediate firing // for the daemon's monitoring init cycle, then repeating every interval. for(unsigned i = 0; i < num_mons; i++) { mon_t* this_mon = &mons[i]; this_mon->interval_timer = xmalloc(sizeof(ev_timer)); ev_timer_init(this_mon->interval_timer, mon_interval_cb, 0., this_mon->cmd->interval); this_mon->interval_timer->data = this_mon; ev_set_priority(this_mon->interval_timer, 0); ev_timer_start(def_loop, this_mon->interval_timer); // initialize the other watchers in the mon_t here as well, // but do not start them (the interval callback starts them each interval) this_mon->cmd_timeout = xmalloc(sizeof(ev_timer)); ev_timer_init(this_mon->cmd_timeout, mon_timeout_cb, 0, 0); ev_set_priority(this_mon->cmd_timeout, -1); this_mon->cmd_timeout->data = this_mon; this_mon->child_watcher = xmalloc(sizeof(ev_child)); ev_child_init(this_mon->child_watcher, mon_child_cb, 0, 0); this_mon->child_watcher->data = this_mon; } log_info("gdnsd_extmon_helper running"); ev_run(def_loop, 0); // graceful shutdown should have cleared out children, but // the hard kill/wait below is for (a) ungraceful shutdown // on unexpected pipe close and (b) anything else going wrong // during graceful shutdown. bool needs_wait = false; for(unsigned i = 0; i < num_mons; i++) { if(mons[i].cmd_pid) { log_debug("not-so-graceful shutdown: sending SIGKILL to %li", (long)mons[i].cmd_pid); kill(mons[i].cmd_pid, SIGKILL); needs_wait = true; } } if(needs_wait) { unsigned i = 500; // 5s for OS to give us all the SIGKILL'd zombies while(i--) { pid_t wprv = waitpid(-1, NULL, WNOHANG); if(wprv < 0) { if(errno == ECHILD) break; else log_fatal("waitpid(-1, NULL, WNOHANG) failed: %s", dmn_logf_errno()); } if(wprv) log_debug("not-so-graceful shutdown: waitpid reaped %li", (long)wprv); const struct timespec ms_10 = { 0, 10000000 }; nanosleep(&ms_10, NULL); } } // Bye! if(killed_by) { log_info("gdnsd_extmon_helper exiting gracefully due to signal %i", killed_by); #ifdef COVERTEST_EXIT exit(0); #else raise(killed_by); #endif } else { log_info("gdnsd_extmon_helper exiting un-gracefully"); exit(42); } }
int main (int argc, char **argv) { struct ev_loop *loop; struct timeval tv[2]; char dirname[10]; char *shmdata; int rundir, rundirs[NUM_PROCS], sharedir; int pagesize; int i, j; // use the default event loop unless you have special needs loop = ev_default_loop (EVBACKEND_EPOLL); //EVFLAG_AUTO); rundir = get_rundir(); pagesize = getpagesize(); // Prepare shared data directory if ((mkdirat(rundir, SHARE_DIR_NAME, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) != 0) { fprintf(stderr, "could not make share directory: %s\n", strerror(errno)); return -1; } if ((sharedir = openat(rundir, SHARE_DIR_NAME, O_DIRECTORY)) < 0) { fprintf(stderr, "could not open share directory: %s\n", strerror(errno)); return -1; } // Prepare worker rundirs for (i=0; i<NUM_PROCS; ++i) { sprintf(dirname, "%d", i); if ((mkdirat(rundir, dirname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) != 0) { fprintf(stderr, "worker %d: could not make runtime directory: %s\n", i, strerror(errno)); return -1; } if ((rundirs[i] = openat(rundir, dirname, O_DIRECTORY)) < 0) { fprintf(stderr, "worker %d: could not open runtime directory: %s\n", i, strerror(errno)); return -1; } if ((mkfifoat(rundirs[i], "inputfile", S_IRUSR | S_IWUSR)) != 0) { fprintf(stderr, "%s: could not create FIFO: %s\n", "inputfile", strerror(errno)); } } // Memory map some data; for (j=0; j<NUM_SHMS; ++j) { // Maybe just use ids for shared SHM names shms[j] = openat(sharedir, "dbfile", O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); ftruncate(shms[j], pagesize); shmdata = mmap((caddr_t)0, pagesize, PROT_WRITE, MAP_SHARED, shms[j], 0); strcpy(shmdata, "Very important DB data."); // Now "share" it for (i=0; i<NUM_PROCS; ++i) { linkat(sharedir, "dbfile", rundirs[i], "dbfile", 0); } } //ev_set_timeout_collect_interval (loop, 0.0001); //ev_set_io_collect_interval (loop, 0.0001); // Start children for (i=0; i<NUM_PROCS; ++i) { pid_t pid = fork(); if (pid == 0) { // Child fchdir(rundirs[i]); if (execle(argv[1], gnu_basename(argv[1]), "outputfile", "dbfile", "inputfile", NULL, NULL)) { fputs("Could not exec: ", stderr); fputs(strerror(errno), stderr); fputc('\n', stderr); exit(EXIT_FAILURE); } } else if (pid > 0) { // Parent } } // Initialize watchers for (i=0; i<NUM_PROCS; ++i) { ev_io *wio = &input_watcher[i]; fifos[i] = openat(rundirs[i], "inputfile", O_WRONLY); wio->data = (void*)i; ev_io_init(wio, input_cb, fifos[i], EV_WRITE); ev_io_start(loop, wio); } ev_child *wchld = &child_watcher; ev_child_init(wchld, child_cb, 0, 1); ev_child_start(loop, wchld); // now wait for events to arrive gettimeofday(tv+0, NULL); ev_loop (loop, 0); gettimeofday(tv+1, NULL); // unloop was called, so exit long t = (tv[1].tv_sec - tv[0].tv_sec) * 1000000 + (tv[1].tv_usec - tv[0].tv_usec); printf("\nTime taken: %lfs (%lfms average)\n\n", ((double)t) / 1000000.0, ((double)t) * NUM_PROCS / NUM_JOBS); // Hang up /* puts("Closing pipes..."); for (i=0; i<NUM_PROCS; ++i) { close(fifos[i]); } */ puts("Waiting for children processes to terminate..."); pid_t pid; do { pid = wait(NULL); if(pid == -1 && errno != ECHILD) { perror("Error during wait()"); abort(); } } while (pid > 0); // Cleanup shms puts("Cleaning SHMs..."); for (j=0; j<NUM_SHMS; ++j) { ftruncate(shms[j], 0); close(shms[i]); } // Finally... puts("Done."); return 0; }
struct worker * worker_start(EV_P_ struct session *session) { struct worker *w; pid_t pid; int stdin_fds [2] = {-1, -1}; int stdout_fds[2] = {-1, -1}; int stderr_fds[2] = {-1, -1}; int msgin_fds [2] = {-1, -1}; int msgout_fds[2] = {-1, -1}; #if WORKER_TIMINGS uint64_t _start = now_us(); #endif if ((w = calloc(1, sizeof(*w))) == NULL) { goto fail; } w->session = session; writeq_init(&w->stdin_writeq); writeq_init(&w->msgin_writeq); if (pipe(stdin_fds ) < 0 || pipe(stdout_fds) < 0 || pipe(stderr_fds) < 0 || pipe(msgin_fds ) < 0 || pipe(msgout_fds) < 0) { LOG_ERRNO("pipe()"); goto fail; } maxfd_update(stdin_fds [0]); maxfd_update(stdin_fds [1]); maxfd_update(stdout_fds[0]); maxfd_update(stdout_fds[1]); maxfd_update(stderr_fds[0]); maxfd_update(stderr_fds[1]); maxfd_update(msgin_fds [0]); maxfd_update(msgin_fds [1]); maxfd_update(msgout_fds[0]); maxfd_update(msgout_fds[1]); pid = fork(); if (pid < 0) { LOG_ERRNO("fork()"); goto fail; } if (pid == 0) { /* child. */ if (dup2(stdin_fds [0], 0) < 0 || dup2(stdout_fds[1], 1) < 0 || dup2(stderr_fds[1], 2) < 0 || dup2(msgin_fds [0], 3) < 0 || dup2(msgout_fds[1], 4) < 0) { exit(EXIT_FAILURE); } maxfd_closeall(5); pyenv_child_after_fork(); exit(EXIT_SUCCESS); } else { /* parent. */ close(stdin_fds [0]); close(stdout_fds[1]); close(stderr_fds[1]); close(msgin_fds [0]); close(msgout_fds[1]); set_fd_nonblocking(stdin_fds [1]); set_fd_nonblocking(stdout_fds[0]); set_fd_nonblocking(stderr_fds[0]); set_fd_nonblocking(msgin_fds [1]); set_fd_nonblocking(msgout_fds[0]); ev_child_init(&w->child_watcher, worker_exited_cb, pid, 0); ev_child_start(EV_A_ &w->child_watcher); ev_io_init(&w->stdin_w , worker_write_stdin_cb, stdin_fds [1], EV_WRITE); ev_io_init(&w->stdout_w, worker_read_stdout_cb, stdout_fds[0], EV_READ); ev_io_init(&w->stderr_w, worker_read_stderr_cb, stderr_fds[0], EV_READ); ev_io_init(&w->msgin_w , worker_write_msgin_cb, msgin_fds [1], EV_WRITE); ev_io_init(&w->msgout_w, worker_read_msgout_cb, msgout_fds[0], EV_READ); ev_io_start(EV_A_ &w->stdout_w); ev_io_start(EV_A_ &w->stderr_w); ev_io_start(EV_A_ &w->msgout_w); LOGF(3, "=== %d: worker started, fds=[%d, %d, %d, %d, %d]\n", worker_pid(w), stdin_fds[1], stdout_fds[0], stderr_fds[0], msgin_fds[1], msgout_fds[0]); w->f_alive = true; } #if WORKER_TIMINGS worker_start_time += now_us() - _start; worker_start_calls++; #endif return w; fail: close(stdin_fds [0]); close(stdin_fds [1]); close(stdout_fds[0]); close(stdout_fds[1]); close(stderr_fds[0]); close(stderr_fds[1]); close(msgin_fds [0]); close(msgin_fds [1]); close(msgout_fds[0]); close(msgout_fds[1]); free(w); return NULL; }
/** * Initialize the object * @param pid * @param trace */ void StatusWatcher::initialize(pid_t pid, bool trace) { // initialize the signal watcher ev_child_init(&_watcher, onStatusChange, pid, trace ? 1 : 0); }