Esempio n. 1
0
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;
}
Esempio n. 2
0
File: util.c Progetto: Fresne/i3-1
/*
 * 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;
}
Esempio n. 4
0
/**
 * 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;
}
Esempio n. 5
0
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 */
Esempio n. 6
0
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;
}
Esempio n. 7
0
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;
}
Esempio n. 8
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;
}
Esempio n. 9
0
// 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);
}
Esempio n. 10
0
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);    
}
Esempio n. 11
0
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);
}
Esempio n. 12
0
File: core.c Progetto: ifzz/lem
static inline void
signal_child_watcher_init(void)
{
	ev_child_init(&signal_child_watcher, signal_child_handler, 0, 1);
}
Esempio n. 13
0
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);
}
Esempio n. 14
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;
}
Esempio n. 15
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* 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;
}
Esempio n. 16
0
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);
    }
}
Esempio n. 17
0
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;
}
Esempio n. 18
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);
}