/* called to create a new server task */ static void onefork_new_task(struct tevent_context *ev, struct loadparm_context *lp_ctx, const char *service_name, void (*new_task_fn)(struct tevent_context *, struct loadparm_context *lp_ctx, struct server_id , void *), void *private_data) { pid_t pid; pid = fork(); if (pid != 0) { /* parent or error code ... go back to the event loop */ return; } pid = getpid(); if (tevent_re_initialise(ev) != 0) { smb_panic("Failed to re-initialise tevent after fork"); } setproctitle("task %s server_id[%d]", service_name, (int)pid); onefork_reload_after_fork(); /* setup this new connection: process will bind to it's sockets etc */ new_task_fn(ev, lp_ctx, cluster_id(pid, 0), private_data); event_loop_wait(ev); talloc_free(ev); exit(0); }
static PyObject *py_tevent_context_reinitialise(TeventContext_Object *self) { int ret = tevent_re_initialise(self->ev); if (ret != 0) { PyErr_SetNone(PyExc_RuntimeError); return NULL; } Py_RETURN_NONE; }
/* called to create a new server task */ static void standard_new_task(struct tevent_context *ev, struct loadparm_context *lp_ctx, const char *service_name, void (*new_task)(struct tevent_context *, struct loadparm_context *lp_ctx, struct server_id , void *), void *private_data) { pid_t pid; pid = fork(); if (pid != 0) { /* parent or error code ... go back to the event loop */ return; } pid = getpid(); /* this will free all the listening sockets and all state that is not associated with this new connection */ if (tevent_re_initialise(ev) != 0) { smb_panic("Failed to re-initialise tevent after fork"); } /* ldb/tdb need special fork handling */ ldb_wrap_fork_hook(); tevent_add_fd(ev, ev, child_pipe[0], TEVENT_FD_READ, standard_pipe_handler, NULL); close(child_pipe[1]); /* Ensure that the forked children do not expose identical random streams */ set_need_random_reseed(); setproctitle("task %s server_id[%d]", service_name, (int)pid); /* setup this new task. Cluster ID is PID based for this process modal */ new_task(ev, lp_ctx, cluster_id(pid, 0), private_data); /* we can't return to the top level here, as that event context is gone, so we now process events in the new event context until there are no more to process */ event_loop_wait(ev); talloc_free(ev); exit(0); }
/* called to create a new server task */ static void standard_new_task(struct tevent_context *ev, struct loadparm_context *lp_ctx, const char *service_name, void (*new_task)(struct tevent_context *, struct loadparm_context *lp_ctx, struct server_id , void *), void *private_data) { pid_t pid; NTSTATUS status; struct standard_child_state *state; struct tevent_fd *fde = NULL; struct tevent_signal *se = NULL; state = setup_standard_child_pipe(ev, service_name); if (state == NULL) { return; } pid = fork(); if (pid != 0) { close(state->to_parent_fd); state->to_parent_fd = -1; if (pid > 0) { state->pid = pid; } else { TALLOC_FREE(state); } /* parent or error code ... go back to the event loop */ return; } /* this leaves state->to_parent_fd open */ TALLOC_FREE(state); pid = getpid(); /* this will free all the listening sockets and all state that is not associated with this new connection */ if (tevent_re_initialise(ev) != 0) { smb_panic("Failed to re-initialise tevent after fork"); } /* ldb/tdb need special fork handling */ ldb_wrap_fork_hook(); /* Must be done after a fork() to reset messaging contexts. */ status = imessaging_reinit_all(); if (!NT_STATUS_IS_OK(status)) { smb_panic("Failed to re-initialise imessaging after fork"); } fde = tevent_add_fd(ev, ev, child_pipe[0], TEVENT_FD_READ, standard_pipe_handler, NULL); if (fde == NULL) { smb_panic("Failed to add fd handler after fork"); } if (child_pipe[1] != -1) { close(child_pipe[1]); child_pipe[1] = -1; } se = tevent_add_signal(ev, ev, SIGHUP, 0, sighup_signal_handler, NULL); if (se == NULL) { smb_panic("Failed to add SIGHUP handler after fork"); } se = tevent_add_signal(ev, ev, SIGTERM, 0, sigterm_signal_handler, NULL); if (se == NULL) { smb_panic("Failed to add SIGTERM handler after fork"); } setproctitle("task %s server_id[%d]", service_name, (int)pid); /* setup this new task. Cluster ID is PID based for this process model */ new_task(ev, lp_ctx, cluster_id(pid, 0), private_data); /* we can't return to the top level here, as that event context is gone, so we now process events in the new event context until there are no more to process */ tevent_loop_wait(ev); talloc_free(ev); exit(0); }
/* called when a listening socket becomes readable. */ static void standard_accept_connection(struct tevent_context *ev, struct loadparm_context *lp_ctx, struct socket_context *sock, void (*new_conn)(struct tevent_context *, struct loadparm_context *, struct socket_context *, struct server_id , void *), void *private_data) { NTSTATUS status; struct socket_context *sock2; pid_t pid; struct socket_address *c, *s; struct standard_child_state *state; struct tevent_fd *fde = NULL; struct tevent_signal *se = NULL; state = setup_standard_child_pipe(ev, NULL); if (state == NULL) { return; } /* accept an incoming connection. */ status = socket_accept(sock, &sock2); if (!NT_STATUS_IS_OK(status)) { DEBUG(0,("standard_accept_connection: accept: %s\n", nt_errstr(status))); /* this looks strange, but is correct. We need to throttle things until the system clears enough resources to handle this new socket */ sleep(1); close(state->to_parent_fd); state->to_parent_fd = -1; TALLOC_FREE(state); return; } pid = fork(); if (pid != 0) { close(state->to_parent_fd); state->to_parent_fd = -1; if (pid > 0) { state->pid = pid; } else { TALLOC_FREE(state); } /* parent or error code ... */ talloc_free(sock2); /* go back to the event loop */ return; } /* this leaves state->to_parent_fd open */ TALLOC_FREE(state); pid = getpid(); /* This is now the child code. We need a completely new event_context to work with */ if (tevent_re_initialise(ev) != 0) { smb_panic("Failed to re-initialise tevent after fork"); } /* this will free all the listening sockets and all state that is not associated with this new connection */ talloc_free(sock); /* we don't care if the dup fails, as its only a select() speed optimisation */ socket_dup(sock2); /* tdb needs special fork handling */ ldb_wrap_fork_hook(); /* Must be done after a fork() to reset messaging contexts. */ status = imessaging_reinit_all(); if (!NT_STATUS_IS_OK(status)) { smb_panic("Failed to re-initialise imessaging after fork"); } fde = tevent_add_fd(ev, ev, child_pipe[0], TEVENT_FD_READ, standard_pipe_handler, NULL); if (fde == NULL) { smb_panic("Failed to add fd handler after fork"); } if (child_pipe[1] != -1) { close(child_pipe[1]); child_pipe[1] = -1; } se = tevent_add_signal(ev, ev, SIGHUP, 0, sighup_signal_handler, NULL); if (se == NULL) { smb_panic("Failed to add SIGHUP handler after fork"); } se = tevent_add_signal(ev, ev, SIGTERM, 0, sigterm_signal_handler, NULL); if (se == NULL) { smb_panic("Failed to add SIGTERM handler after fork"); } /* setup the process title */ c = socket_get_peer_addr(sock2, ev); s = socket_get_my_addr(sock2, ev); if (s && c) { setproctitle("conn c[%s:%u] s[%s:%u] server_id[%d]", c->addr, c->port, s->addr, s->port, (int)pid); } talloc_free(c); talloc_free(s); /* setup this new connection. Cluster ID is PID based for this process model */ new_conn(ev, lp_ctx, sock2, cluster_id(pid, 0), private_data); /* we can't return to the top level here, as that event context is gone, so we now process events in the new event context until there are no more to process */ tevent_loop_wait(ev); talloc_free(ev); exit(0); }
/* run a command as a child process, with a timeout. any stdout/stderr from the child will appear in the Samba logs with the specified log levels */ struct tevent_req *samba_runcmd_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct timeval endtime, int stdout_log_level, int stderr_log_level, const char * const *argv0, ...) { struct tevent_req *req; struct samba_runcmd_state *state; int p1[2], p2[2], p3[2]; char **argv; va_list ap; if (argv0 == NULL) { return NULL; } req = tevent_req_create(mem_ctx, &state, struct samba_runcmd_state); if (req == NULL) { return NULL; } state->stdout_log_level = stdout_log_level; state->stderr_log_level = stderr_log_level; state->fd_stdin = -1; state->arg0 = talloc_strdup(state, argv0[0]); if (tevent_req_nomem(state->arg0, req)) { return tevent_req_post(req, ev); } if (pipe(p1) != 0) { tevent_req_error(req, errno); return tevent_req_post(req, ev); } if (pipe(p2) != 0) { close(p1[0]); close(p1[1]); tevent_req_error(req, errno); return tevent_req_post(req, ev); } if (pipe(p3) != 0) { close(p1[0]); close(p1[1]); close(p2[0]); close(p2[1]); tevent_req_error(req, errno); return tevent_req_post(req, ev); } state->tfork = tfork_create(); if (state->tfork == NULL) { close(p1[0]); close(p1[1]); close(p2[0]); close(p2[1]); close(p3[0]); close(p3[1]); tevent_req_error(req, errno); return tevent_req_post(req, ev); } state->pid = tfork_child_pid(state->tfork); if (state->pid != 0) { /* the parent */ close(p1[1]); close(p2[1]); close(p3[0]); state->fd_stdout = p1[0]; state->fd_stderr = p2[0]; state->fd_stdin = p3[1]; state->fd_status = tfork_event_fd(state->tfork); set_blocking(state->fd_stdout, false); set_blocking(state->fd_stderr, false); set_blocking(state->fd_stdin, false); set_blocking(state->fd_status, false); smb_set_close_on_exec(state->fd_stdin); smb_set_close_on_exec(state->fd_stdout); smb_set_close_on_exec(state->fd_stderr); smb_set_close_on_exec(state->fd_status); tevent_req_set_cleanup_fn(req, samba_runcmd_cleanup_fn); state->fde_stdout = tevent_add_fd(ev, state, state->fd_stdout, TEVENT_FD_READ, samba_runcmd_io_handler, req); if (tevent_req_nomem(state->fde_stdout, req)) { close(state->fd_stdout); close(state->fd_stderr); close(state->fd_status); return tevent_req_post(req, ev); } tevent_fd_set_auto_close(state->fde_stdout); state->fde_stderr = tevent_add_fd(ev, state, state->fd_stderr, TEVENT_FD_READ, samba_runcmd_io_handler, req); if (tevent_req_nomem(state->fde_stdout, req)) { close(state->fd_stdout); close(state->fd_stderr); close(state->fd_status); return tevent_req_post(req, ev); } tevent_fd_set_auto_close(state->fde_stderr); state->fde_status = tevent_add_fd(ev, state, state->fd_status, TEVENT_FD_READ, samba_runcmd_io_handler, req); if (tevent_req_nomem(state->fde_stdout, req)) { close(state->fd_stdout); close(state->fd_stderr); close(state->fd_status); return tevent_req_post(req, ev); } tevent_fd_set_auto_close(state->fde_status); if (!timeval_is_zero(&endtime)) { tevent_req_set_endtime(req, ev, endtime); } return req; } /* the child */ close(p1[0]); close(p2[0]); close(p3[1]); close(0); close(1); close(2); /* we want to ensure that all of the network sockets we had open are closed */ tevent_re_initialise(ev); /* setup for logging to go to the parents debug log */ dup2(p3[0], 0); dup2(p1[1], 1); dup2(p2[1], 2); close(p1[1]); close(p2[1]); close(p3[0]); argv = str_list_copy(state, discard_const_p(const char *, argv0)); if (!argv) { fprintf(stderr, "Out of memory in child\n"); _exit(255); } va_start(ap, argv0); while (1) { const char **l; char *arg = va_arg(ap, char *); if (arg == NULL) break; l = discard_const_p(const char *, argv); l = str_list_add(l, arg); if (l == NULL) { fprintf(stderr, "Out of memory in child\n"); _exit(255); } argv = discard_const_p(char *, l); } va_end(ap); (void)execvp(state->arg0, argv); fprintf(stderr, "Failed to exec child - %s\n", strerror(errno)); _exit(255); return NULL; }
/* called when a listening socket becomes readable. */ static void standard_accept_connection(struct tevent_context *ev, struct loadparm_context *lp_ctx, struct socket_context *sock, void (*new_conn)(struct tevent_context *, struct loadparm_context *, struct socket_context *, struct server_id , void *), void *private_data) { NTSTATUS status; struct socket_context *sock2; pid_t pid; struct socket_address *c, *s; /* accept an incoming connection. */ status = socket_accept(sock, &sock2); if (!NT_STATUS_IS_OK(status)) { DEBUG(0,("standard_accept_connection: accept: %s\n", nt_errstr(status))); /* this looks strange, but is correct. We need to throttle things until the system clears enough resources to handle this new socket */ sleep(1); return; } pid = fork(); if (pid != 0) { /* parent or error code ... */ talloc_free(sock2); /* go back to the event loop */ return; } pid = getpid(); /* This is now the child code. We need a completely new event_context to work with */ if (tevent_re_initialise(ev) != 0) { smb_panic("Failed to re-initialise tevent after fork"); } /* this will free all the listening sockets and all state that is not associated with this new connection */ talloc_free(sock); /* we don't care if the dup fails, as its only a select() speed optimisation */ socket_dup(sock2); /* tdb needs special fork handling */ ldb_wrap_fork_hook(); tevent_add_fd(ev, ev, child_pipe[0], TEVENT_FD_READ, standard_pipe_handler, NULL); close(child_pipe[1]); /* Ensure that the forked children do not expose identical random streams */ set_need_random_reseed(); /* setup the process title */ c = socket_get_peer_addr(sock2, ev); s = socket_get_my_addr(sock2, ev); if (s && c) { setproctitle("conn c[%s:%u] s[%s:%u] server_id[%d]", c->addr, c->port, s->addr, s->port, (int)pid); } talloc_free(c); talloc_free(s); /* setup this new connection. Cluster ID is PID based for this process modal */ new_conn(ev, lp_ctx, sock2, cluster_id(pid, 0), private_data); /* we can't return to the top level here, as that event context is gone, so we now process events in the new event context until there are no more to process */ event_loop_wait(ev); talloc_free(ev); exit(0); }
/* * called to create a new server task */ static void prefork_new_task( struct tevent_context *ev, struct loadparm_context *lp_ctx, const char *service_name, void (*new_task_fn)(struct tevent_context *, struct loadparm_context *lp_ctx, struct server_id , void *, void *), void *private_data, const struct service_details *service_details, int from_parent_fd) { pid_t pid; struct tfork* t = NULL; int i, num_children; struct tevent_context *ev2; t = tfork_create(); if (t == NULL) { smb_panic("failure in tfork\n"); } pid = tfork_child_pid(t); if (pid != 0) { struct tevent_fd *fde = NULL; int fd = tfork_event_fd(t); /* Register a pipe handler that gets called when the prefork * master process terminates. */ fde = tevent_add_fd(ev, ev, fd, TEVENT_FD_READ, prefork_child_pipe_handler, t); if (fde == NULL) { smb_panic("Failed to add child pipe handler, " "after fork"); } tevent_fd_set_auto_close(fde); return; } pid = getpid(); setproctitle("task[%s] pre-fork master", service_name); /* * this will free all the listening sockets and all state that * is not associated with this new connection */ if (tevent_re_initialise(ev) != 0) { smb_panic("Failed to re-initialise tevent after fork"); } prefork_reload_after_fork(); setup_handlers(ev, from_parent_fd); if (service_details->inhibit_pre_fork) { new_task_fn(ev, lp_ctx, cluster_id(pid, 0), private_data, NULL); /* The task does not support pre-fork */ tevent_loop_wait(ev); TALLOC_FREE(ev); exit(0); } /* * This is now the child code. We need a completely new event_context * to work with */ ev2 = s4_event_context_init(NULL); /* setup this new connection: process will bind to it's sockets etc * * While we can use ev for the child, which has been re-initialised * above we must run the new task under ev2 otherwise the children would * be listening on the sockets. Also we don't want the top level * process accepting and handling requests, it's responsible for * monitoring and controlling the child work processes. */ new_task_fn(ev2, lp_ctx, cluster_id(pid, 0), private_data, NULL); { int default_children; default_children = lpcfg_prefork_children(lp_ctx); num_children = lpcfg_parm_int(lp_ctx, NULL, "prefork children", service_name, default_children); } if (num_children == 0) { DBG_WARNING("Number of pre-fork children for %s is zero, " "NO worker processes will be started for %s\n", service_name, service_name); } DBG_NOTICE("Forking %d %s worker processes\n", num_children, service_name); /* We are now free to spawn some worker processes */ for (i=0; i < num_children; i++) { struct tfork* w = NULL; w = tfork_create(); if (w == NULL) { smb_panic("failure in tfork\n"); } pid = tfork_child_pid(w); if (pid != 0) { struct tevent_fd *fde = NULL; int fd = tfork_event_fd(w); fde = tevent_add_fd(ev, ev, fd, TEVENT_FD_READ, prefork_child_pipe_handler, w); if (fde == NULL) { smb_panic("Failed to add child pipe handler, " "after fork"); } tevent_fd_set_auto_close(fde); } else { /* tfork uses malloc */ free(w); TALLOC_FREE(ev); setproctitle("task[%s] pre-forked worker", service_name); prefork_reload_after_fork(); setup_handlers(ev2, from_parent_fd); tevent_loop_wait(ev2); talloc_free(ev2); exit(0); } } /* Don't listen on the sockets we just gave to the children */ tevent_loop_wait(ev); TALLOC_FREE(ev); /* We need to keep ev2 until we're finished for the messaging to work */ TALLOC_FREE(ev2); exit(0); }
static bool test_messaging_overflow(struct torture_context *tctx) { struct imessaging_context *msg_ctx; ssize_t nwritten, nread; pid_t child; char c = 0; int up_pipe[2], down_pipe[2]; int i, ret, child_status; ret = pipe(up_pipe); torture_assert(tctx, ret == 0, "pipe failed"); ret = pipe(down_pipe); torture_assert(tctx, ret == 0, "pipe failed"); child = fork(); if (child < 0) { torture_fail(tctx, "fork failed"); } if (child == 0) { ret = tevent_re_initialise(tctx->ev); torture_assert(tctx, ret == 0, "tevent_re_initialise failed"); msg_ctx = imessaging_init(tctx, tctx->lp_ctx, cluster_id(getpid(), 0), tctx->ev); torture_assert(tctx, msg_ctx != NULL, "imessaging_init failed"); do { nwritten = write(up_pipe[1], &c, 1); } while ((nwritten == -1) && (errno == EINTR)); ret = close(down_pipe[1]); torture_assert(tctx, ret == 0, "close failed"); do { nread = read(down_pipe[0], &c, 1); } while ((nread == -1) && (errno == EINTR)); exit(0); } do { nread = read(up_pipe[0], &c, 1); } while ((nread == -1) && (errno == EINTR)); msg_ctx = imessaging_init(tctx, tctx->lp_ctx, cluster_id(getpid(), 0), tctx->ev); torture_assert(tctx, msg_ctx != NULL, "imessaging_init failed"); for (i=0; i<1000; i++) { NTSTATUS status; status = imessaging_send(msg_ctx, cluster_id(child, 0), MSG_PING, NULL); torture_assert_ntstatus_ok(tctx, status, "imessaging_send failed"); } tevent_loop_once(tctx->ev); talloc_free(msg_ctx); ret = close(down_pipe[1]); torture_assert(tctx, ret == 0, "close failed"); ret = waitpid(child, &child_status, 0); torture_assert(tctx, ret == child, "wrong child exited"); torture_assert(tctx, child_status == 0, "child failed"); poll(NULL, 0, 500); return true; }