From c80f70390c3763d5d7248979db9542cd05b7cb44 Mon Sep 17 00:00:00 2001 From: Michael Adam Date: Thu, 15 Mar 2012 16:29:27 +0100 Subject: s3:smbd: let smbd/nmbd/winbindd child processes terminate if the parent process died. This applies to all child processes making use of reinit_after_fork(). It is implemented by establishing a pipe between parent and child. The child watches for EOF on the read end of the pipe, indidcating an exited parent. Pair-Programmed-With: Stefan Metzmacher --- source3/include/proto.h | 1 + source3/lib/util.c | 56 +++++++++++++++++++++++++++++++++++++++++++++ source3/nmbd/nmbd.c | 11 +++++++++ source3/smbd/server.c | 13 +++++++++++ source3/winbindd/winbindd.c | 11 +++++++++ 5 files changed, 92 insertions(+) (limited to 'source3') diff --git a/source3/include/proto.h b/source3/include/proto.h index e0d9f3117e..e8a0d42b3c 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -484,6 +484,7 @@ char *unix_clean_name(TALLOC_CTX *ctx, const char *s); char *clean_name(TALLOC_CTX *ctx, const char *s); ssize_t write_data_at_offset(int fd, const char *buffer, size_t N, SMB_OFF_T pos); int set_blocking(int fd, bool set); +NTSTATUS init_before_fork(void); NTSTATUS reinit_after_fork(struct messaging_context *msg_ctx, struct event_context *ev_ctx, bool parent_longlived); diff --git a/source3/lib/util.c b/source3/lib/util.c index 822db43fef..fa2cc9fd83 100644 --- a/source3/lib/util.c +++ b/source3/lib/util.c @@ -356,6 +356,46 @@ ssize_t write_data_at_offset(int fd, const char *buffer, size_t N, SMB_OFF_T pos #endif } +static int reinit_after_fork_pipe[2] = { -1, -1 }; + +NTSTATUS init_before_fork(void) +{ + int ret; + + ret = pipe(reinit_after_fork_pipe); + if (ret == -1) { + NTSTATUS status; + + status = map_nt_error_from_unix_common(errno); + + DEBUG(0, ("Error creating child_pipe: %s\n", + nt_errstr(status))); + + return status; + } + + return NT_STATUS_OK; +} + +/** + * Detect died parent by detecting EOF on the pipe + */ +static void reinit_after_fork_pipe_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, + void *private_data) +{ + char c; + + if (read(reinit_after_fork_pipe[0], &c, 1) != 1) { + /* + * we have reached EOF on stdin, which means the + * parent has exited. Shutdown the server + */ + (void)kill(getpid(), SIGTERM); + } +} + NTSTATUS reinit_after_fork(struct messaging_context *msg_ctx, struct event_context *ev_ctx, @@ -363,6 +403,11 @@ NTSTATUS reinit_after_fork(struct messaging_context *msg_ctx, { NTSTATUS status = NT_STATUS_OK; + if (reinit_after_fork_pipe[1] != -1) { + close(reinit_after_fork_pipe[1]); + reinit_after_fork_pipe[1] = -1; + } + /* Reset the state of the random * number generation system, so * children do not get the same random @@ -380,6 +425,17 @@ NTSTATUS reinit_after_fork(struct messaging_context *msg_ctx, smb_panic(__location__ ": Failed to re-initialise event context"); } + if (reinit_after_fork_pipe[0] != -1) { + struct tevent_fd *fde; + + fde = tevent_add_fd(ev_ctx, ev_ctx /* TALLOC_CTX */, + reinit_after_fork_pipe[0], TEVENT_FD_READ, + reinit_after_fork_pipe_handler, NULL); + if (fde == NULL) { + smb_panic(__location__ ": Failed to add reinit_after_fork pipe event"); + } + } + if (msg_ctx) { /* * For clustering, we need to re-init our ctdbd connection after the diff --git a/source3/nmbd/nmbd.c b/source3/nmbd/nmbd.c index 52d7ed9e4e..eff1eca2f1 100644 --- a/source3/nmbd/nmbd.c +++ b/source3/nmbd/nmbd.c @@ -951,6 +951,17 @@ static bool open_sockets(bool isdaemon, int port) exit(1); } + /* + * Do not initialize the parent-child-pipe before becoming + * a daemon: this is used to detect a died parent in the child + * process. + */ + status = init_before_fork(); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("init_before_fork failed: %s\n", nt_errstr(status))); + exit(1); + } + if (!nmbd_setup_sig_term_handler(msg)) exit(1); if (!nmbd_setup_stdin_handler(msg, !Fork)) diff --git a/source3/smbd/server.c b/source3/smbd/server.c index aa3da1f743..851b4608a7 100644 --- a/source3/smbd/server.c +++ b/source3/smbd/server.c @@ -57,6 +57,8 @@ struct smbd_parent_context { /* the list of current child processes */ struct smbd_child_pid *children; size_t num_children; + /* pipe for detecting death of parent process in child: */ + int child_pipe[2]; struct timed_event *cleanup_te; }; @@ -1231,6 +1233,17 @@ extern void build_options(bool screen); exit(1); } + /* + * Do not initialize the parent-child-pipe before becoming + * a daemon: this is used to detect a died parent in the child + * process. + */ + status = init_before_fork(); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("init_before_fork failed: %s\n", nt_errstr(status))); + exit(1); + } + smbd_server_conn->msg_ctx = msg_ctx; parent = talloc_zero(ev_ctx, struct smbd_parent_context); diff --git a/source3/winbindd/winbindd.c b/source3/winbindd/winbindd.c index d1d36fd007..66e53d9cae 100644 --- a/source3/winbindd/winbindd.c +++ b/source3/winbindd/winbindd.c @@ -1461,6 +1461,17 @@ int main(int argc, char **argv, char **envp) exit(1); } + /* + * Do not initialize the parent-child-pipe before becoming + * a daemon: this is used to detect a died parent in the child + * process. + */ + status = init_before_fork(); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("init_before_fork failed: %s\n", nt_errstr(status))); + exit(1); + } + winbindd_register_handlers(!Fork); status = init_system_info(); -- cgit