1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-11 05:18:09 +03:00
samba-mirror/lib/util/util_runcmd.c
Ralph Boehme 37ef28794c Revert "lib/util: make use of tfork in samba_runcmd_send()"
This reverts commit 292e46ab12.

Processes run by tfork will have a parent pid of 1, they won't be childs
of the caller anymore.

When the source4 samba process uses samba_runcmd_send() to launch smbd
and winbindd the resulting process hierarchy becomes:

 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
    1   516   510   510 ?           -1 S      111   0:02 avahi-daemon: running [samba-ad.local]
    1 29209 29209 29209 ?           -1 Ss       0   0:00 ./bin/samba
29209 29210 29209 29209 ?           -1 S        0   0:00  \_ ./bin/samba
29209 29211 29209 29209 ?           -1 S        0   0:00  \_ ./bin/samba
29209 29213 29209 29209 ?           -1 S        0   0:00  \_ ./bin/samba
29209 29215 29209 29209 ?           -1 S        0   0:00  \_ ./bin/samba
29209 29216 29209 29209 ?           -1 R        0   0:00  \_ ./bin/samba
29209 29217 29209 29209 ?           -1 S        0   0:00  \_ ./bin/samba
29209 29218 29209 29209 ?           -1 S        0   0:00  \_ ./bin/samba
29209 29220 29209 29209 ?           -1 S        0   0:00  \_ ./bin/samba
29209 29221 29209 29209 ?           -1 S        0   0:00  \_ ./bin/samba
29209 29222 29209 29209 ?           -1 S        0   0:00  \_ ./bin/samba
29209 29223 29209 29209 ?           -1 S        0   0:00  \_ ./bin/samba
29209 29224 29209 29209 ?           -1 S        0   0:00  \_ ./bin/samba
29209 29225 29209 29209 ?           -1 S        0   0:00  \_ ./bin/samba
    1 29214 29209 29209 ?           -1 S        0   0:00 ./bin/samba
29214 29219 29219 29219 ?           -1 Ss       0   0:00  \_ /home/slow/git/samba/scratch/bin/smbd -D --option=server role check:inhibit=yes --foreground
29219 29236 29219 29219 ?           -1 S        0   0:00      \_ /home/slow/git/samba/scratch/bin/smbd -D --option=server role check:inhibit=yes --foreground
29219 29237 29219 29219 ?           -1 S        0   0:00      \_ /home/slow/git/samba/scratch/bin/smbd -D --option=server role check:inhibit=yes --foreground
29219 29238 29219 29219 ?           -1 S        0   0:00      \_ /home/slow/git/samba/scratch/bin/smbd -D --option=server role check:inhibit=yes --foreground
    1 29228 29209 29209 ?           -1 S        0   0:00 ./bin/samba
29228 29230 29230 29230 ?           -1 Ss       0   0:00  \_ /home/slow/git/samba/scratch/bin/winbindd -D --option=server role check:inhibit=yes --foreground
29230 29239 29230 29230 ?           -1 S        0   0:00      \_ /home/slow/git/samba/scratch/bin/winbindd -D --option=server role check:inhibit=yes --foreground

They will still be in the same process group and session, but just not
be a child or subchild. For childs of the source4 samba process this
might be non desirable.

killing all processes by sending a signal to the main samba process
still works, because a pipe is used between the samba process and the
smbd and winbindd childs. Both watch for EOF on the pipe.

In the output above smbd and winbindd are in their own process group ans
session because they call become_daemon().

See also the discussion in this mailthread:
<https://lists.samba.org/archive/samba-technical/2017-April/120257.html>

Signed-off-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Stefan Metzmacher <metze@samba.org>

Autobuild-User(master): Stefan Metzmacher <metze@samba.org>
Autobuild-Date(master): Sun Apr 30 17:21:05 CEST 2017 on sn-devel-144
2017-04-30 17:21:05 +02:00

341 lines
8.0 KiB
C

/*
Unix SMB/CIFS implementation.
run a child command
Copyright (C) Andrew Tridgell 2010
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
this runs a child command with stdout and stderr going to the Samba
log
*/
#include "includes.h"
#include "system/filesys.h"
#include "../lib/util/tevent_unix.h"
#include "../lib/util/util_runcmd.h"
static int samba_runcmd_state_destructor(struct samba_runcmd_state *state)
{
if (state->pid > 0) {
kill(state->pid, SIGKILL);
waitpid(state->pid, NULL, 0);
state->pid = -1;
}
if (state->fd_stdin != -1) {
close(state->fd_stdin);
}
return 0;
}
static void samba_runcmd_io_handler(struct tevent_context *ev,
struct tevent_fd *fde,
uint16_t flags,
void *private_data);
/*
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->pid = fork();
if (state->pid == (pid_t)-1) {
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);
}
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];
set_blocking(state->fd_stdout, false);
set_blocking(state->fd_stderr, false);
set_blocking(state->fd_stdin, 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);
talloc_set_destructor(state, samba_runcmd_state_destructor);
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);
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_stderr);
return tevent_req_post(req, ev);
}
tevent_fd_set_auto_close(state->fde_stderr);
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;
}
/*
handle stdout/stderr from the child
*/
static void samba_runcmd_io_handler(struct tevent_context *ev,
struct tevent_fd *fde,
uint16_t flags,
void *private_data)
{
struct tevent_req *req = talloc_get_type_abort(private_data,
struct tevent_req);
struct samba_runcmd_state *state = tevent_req_data(req,
struct samba_runcmd_state);
int level;
char *p;
int n, fd;
if (fde == state->fde_stdout) {
level = state->stdout_log_level;
fd = state->fd_stdout;
} else if (fde == state->fde_stderr) {
level = state->stderr_log_level;
fd = state->fd_stderr;
} else {
return;
}
if (!(flags & TEVENT_FD_READ)) {
return;
}
n = read(fd, &state->buf[state->buf_used],
sizeof(state->buf) - state->buf_used);
if (n > 0) {
state->buf_used += n;
} else if (n == 0) {
if (fde == state->fde_stdout) {
talloc_free(fde);
state->fde_stdout = NULL;
}
if (fde == state->fde_stderr) {
talloc_free(fde);
state->fde_stderr = NULL;
}
if (state->fde_stdout == NULL &&
state->fde_stderr == NULL) {
int status;
/* the child has closed both stdout and
* stderr, assume its dead */
pid_t pid = waitpid(state->pid, &status, 0);
if (pid != state->pid) {
if (errno == ECHILD) {
/* this happens when the
parent has set SIGCHLD to
SIG_IGN. In that case we
can only get error
information for the child
via its logging. We should
stop using SIG_IGN on
SIGCHLD in the standard
process model.
*/
DEBUG(0, ("Error in waitpid() unexpectedly got ECHILD "
"for %s child %d - %s, "
"someone has set SIGCHLD to SIG_IGN!\n",
state->arg0, (int)state->pid, strerror(errno)));
tevent_req_error(req, errno);
return;
}
DEBUG(0,("Error in waitpid() for child %s - %s \n",
state->arg0, strerror(errno)));
if (errno == 0) {
errno = ECHILD;
}
tevent_req_error(req, errno);
return;
}
status = WEXITSTATUS(status);
DEBUG(3,("Child %s exited with status %d\n",
state->arg0, status));
if (status != 0) {
tevent_req_error(req, status);
return;
}
tevent_req_done(req);
return;
}
return;
}
while (state->buf_used > 0 &&
(p = (char *)memchr(state->buf, '\n', state->buf_used)) != NULL) {
int n1 = (p - state->buf)+1;
int n2 = n1 - 1;
/* swallow \r from child processes */
if (n2 > 0 && state->buf[n2-1] == '\r') {
n2--;
}
DEBUG(level,("%s: %*.*s\n", state->arg0, n2, n2, state->buf));
memmove(state->buf, p+1, sizeof(state->buf) - n1);
state->buf_used -= n1;
}
/* the buffer could have completely filled - unfortunately we have
no choice but to dump it out straight away */
if (state->buf_used == sizeof(state->buf)) {
DEBUG(level,("%s: %*.*s\n",
state->arg0, state->buf_used,
state->buf_used, state->buf));
state->buf_used = 0;
}
}
int samba_runcmd_recv(struct tevent_req *req, int *perrno)
{
if (tevent_req_is_unix_error(req, perrno)) {
tevent_req_received(req);
return -1;
}
tevent_req_received(req);
return 0;
}