2010-02-17 10:18:05 +03:00
/*
2016-04-20 08:10:41 +03:00
Unix SMB / CIFS implementation .
2010-02-17 10:18:05 +03:00
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
*/
2021-01-14 12:10:04 +03:00
# include "replace.h"
2010-02-17 10:18:05 +03:00
# include "system/filesys.h"
2019-02-02 15:09:37 +03:00
# include <tevent.h>
2021-01-14 12:10:04 +03:00
# include "lib/util/samba_util.h"
# include "lib/util/debug.h"
2011-02-27 22:45:42 +03:00
# include "../lib/util/tevent_unix.h"
2017-05-16 19:36:03 +03:00
# include "../lib/util/tfork.h"
# include "../lib/util/sys_rw.h"
2010-02-17 10:18:05 +03:00
2019-02-02 15:09:37 +03:00
struct samba_runcmd_state {
int stdout_log_level ;
int stderr_log_level ;
struct tevent_fd * fde_stdout ;
struct tevent_fd * fde_stderr ;
struct tevent_fd * fde_status ;
int fd_stdin , fd_stdout , fd_stderr , fd_status ;
char * arg0 ;
pid_t pid ;
struct tfork * tfork ;
char buf [ 1024 ] ;
uint16_t buf_used ;
} ;
2017-09-29 14:07:53 +03:00
static void samba_runcmd_cleanup_fn ( struct tevent_req * req ,
enum tevent_req_state req_state )
2010-02-17 10:18:05 +03:00
{
2017-09-29 14:07:53 +03:00
struct samba_runcmd_state * state = tevent_req_data (
req , struct samba_runcmd_state ) ;
2017-09-29 13:45:24 +03:00
if ( state - > tfork ! = NULL ) {
tfork_destroy ( & state - > tfork ) ;
2010-02-17 10:18:05 +03:00
}
2017-09-29 13:45:24 +03:00
state - > pid = - 1 ;
2012-12-12 22:15:58 +04:00
if ( state - > fd_stdin ! = - 1 ) {
close ( state - > fd_stdin ) ;
2017-09-29 14:06:08 +03:00
state - > fd_stdin = - 1 ;
2012-12-12 22:15:58 +04:00
}
2010-02-17 10:18:05 +03:00
}
2019-02-02 14:58:57 +03:00
int samba_runcmd_export_stdin ( struct tevent_req * req )
{
struct samba_runcmd_state * state = tevent_req_data ( req ,
struct samba_runcmd_state ) ;
int ret = state - > fd_stdin ;
state - > fd_stdin = - 1 ;
return ret ;
}
2010-02-24 14:43:45 +03:00
static void samba_runcmd_io_handler ( struct tevent_context * ev ,
struct tevent_fd * fde ,
uint16_t flags ,
void * private_data ) ;
2010-02-17 10:18:05 +03:00
/*
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
*/
2010-02-24 14:43:45 +03:00
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 , . . . )
2010-02-17 10:18:05 +03:00
{
2010-02-24 14:43:45 +03:00
struct tevent_req * req ;
struct samba_runcmd_state * state ;
2011-11-30 07:07:51 +04:00
int p1 [ 2 ] , p2 [ 2 ] , p3 [ 2 ] ;
2010-02-17 10:18:05 +03:00
char * * argv ;
va_list ap ;
2014-03-14 00:30:52 +04:00
if ( argv0 = = NULL ) {
return NULL ;
}
2010-02-24 14:43:45 +03:00
req = tevent_req_create ( mem_ctx , & state ,
struct samba_runcmd_state ) ;
if ( req = = NULL ) {
return NULL ;
}
2010-02-17 10:18:05 +03:00
2010-02-24 14:43:45 +03:00
state - > stdout_log_level = stdout_log_level ;
state - > stderr_log_level = stderr_log_level ;
2012-12-12 22:15:58 +04:00
state - > fd_stdin = - 1 ;
2010-02-17 10:18:05 +03:00
2010-02-24 14:43:45 +03:00
state - > arg0 = talloc_strdup ( state , argv0 [ 0 ] ) ;
if ( tevent_req_nomem ( state - > arg0 , req ) ) {
return tevent_req_post ( req , ev ) ;
}
2010-02-17 10:18:05 +03:00
if ( pipe ( p1 ) ! = 0 ) {
2010-02-24 14:43:45 +03:00
tevent_req_error ( req , errno ) ;
return tevent_req_post ( req , ev ) ;
2010-02-17 10:18:05 +03:00
}
if ( pipe ( p2 ) ! = 0 ) {
close ( p1 [ 0 ] ) ;
close ( p1 [ 1 ] ) ;
2010-02-24 14:43:45 +03:00
tevent_req_error ( req , errno ) ;
return tevent_req_post ( req , ev ) ;
2010-02-17 10:18:05 +03:00
}
2011-11-30 07:07:51 +04:00
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 ) ;
}
2010-02-17 10:18:05 +03:00
2017-05-16 19:36:03 +03:00
state - > tfork = tfork_create ( ) ;
if ( state - > tfork = = NULL ) {
2010-02-17 10:18:05 +03:00
close ( p1 [ 0 ] ) ;
close ( p1 [ 1 ] ) ;
close ( p2 [ 0 ] ) ;
close ( p2 [ 1 ] ) ;
2011-11-30 07:07:51 +04:00
close ( p3 [ 0 ] ) ;
close ( p3 [ 1 ] ) ;
2010-02-24 14:43:45 +03:00
tevent_req_error ( req , errno ) ;
return tevent_req_post ( req , ev ) ;
2010-02-17 10:18:05 +03:00
}
2017-05-16 19:36:03 +03:00
state - > pid = tfork_child_pid ( state - > tfork ) ;
2010-02-24 14:43:45 +03:00
if ( state - > pid ! = 0 ) {
2010-02-17 10:18:05 +03:00
/* the parent */
close ( p1 [ 1 ] ) ;
close ( p2 [ 1 ] ) ;
2011-11-30 07:07:51 +04:00
close ( p3 [ 0 ] ) ;
2010-02-24 14:43:45 +03:00
state - > fd_stdout = p1 [ 0 ] ;
state - > fd_stderr = p2 [ 0 ] ;
2011-11-30 07:07:51 +04:00
state - > fd_stdin = p3 [ 1 ] ;
2017-05-16 19:36:03 +03:00
state - > fd_status = tfork_event_fd ( state - > tfork ) ;
2011-11-30 07:07:51 +04:00
2010-02-24 14:43:45 +03:00
set_blocking ( state - > fd_stdout , false ) ;
set_blocking ( state - > fd_stderr , false ) ;
2011-11-30 07:07:51 +04:00
set_blocking ( state - > fd_stdin , false ) ;
2017-05-16 19:36:03 +03:00
set_blocking ( state - > fd_status , false ) ;
2010-02-24 14:43:45 +03:00
2011-11-30 08:18:29 +04:00
smb_set_close_on_exec ( state - > fd_stdin ) ;
smb_set_close_on_exec ( state - > fd_stdout ) ;
smb_set_close_on_exec ( state - > fd_stderr ) ;
2017-05-16 19:36:03 +03:00
smb_set_close_on_exec ( state - > fd_status ) ;
2011-11-30 08:18:29 +04:00
2017-09-29 14:07:53 +03:00
tevent_req_set_cleanup_fn ( req , samba_runcmd_cleanup_fn ) ;
2010-02-24 14:43:45 +03:00
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 ) ) {
2011-11-30 07:07:51 +04:00
close ( state - > fd_stdout ) ;
close ( state - > fd_stderr ) ;
2017-05-16 19:36:03 +03:00
close ( state - > fd_status ) ;
2010-02-24 14:43:45 +03:00
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 ) ) {
2017-05-16 19:36:03 +03:00
close ( state - > fd_stdout ) ;
2011-11-30 07:07:51 +04:00
close ( state - > fd_stderr ) ;
2017-05-16 19:36:03 +03:00
close ( state - > fd_status ) ;
2010-02-24 14:43:45 +03:00
return tevent_req_post ( req , ev ) ;
}
tevent_fd_set_auto_close ( state - > fde_stderr ) ;
2017-05-16 19:36:03 +03:00
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 ) ;
2010-02-24 14:43:45 +03:00
if ( ! timeval_is_zero ( & endtime ) ) {
tevent_req_set_endtime ( req , ev , endtime ) ;
2010-02-17 10:18:05 +03:00
}
2010-02-24 14:43:45 +03:00
return req ;
2010-02-17 10:18:05 +03:00
}
/* the child */
close ( p1 [ 0 ] ) ;
close ( p2 [ 0 ] ) ;
2011-11-30 07:07:51 +04:00
close ( p3 [ 1 ] ) ;
2010-02-17 10:18:05 +03:00
close ( 0 ) ;
close ( 1 ) ;
close ( 2 ) ;
2010-04-19 11:14:33 +04:00
/* we want to ensure that all of the network sockets we had
open are closed */
tevent_re_initialise ( ev ) ;
2010-02-17 10:18:05 +03:00
/* setup for logging to go to the parents debug log */
2011-11-30 07:07:51 +04:00
dup2 ( p3 [ 0 ] , 0 ) ;
2010-02-17 10:18:05 +03:00
dup2 ( p1 [ 1 ] , 1 ) ;
dup2 ( p2 [ 1 ] , 2 ) ;
2011-11-30 08:18:29 +04:00
close ( p1 [ 1 ] ) ;
close ( p2 [ 1 ] ) ;
close ( p3 [ 0 ] ) ;
2010-02-24 14:43:45 +03:00
argv = str_list_copy ( state , discard_const_p ( const char * , argv0 ) ) ;
2010-02-17 10:18:05 +03:00
if ( ! argv ) {
fprintf ( stderr , " Out of memory in child \n " ) ;
_exit ( 255 ) ;
}
2010-02-26 09:09:35 +03:00
va_start ( ap , argv0 ) ;
2010-02-17 10:18:05 +03:00
while ( 1 ) {
2014-02-26 10:38:07 +04:00
const char * * l ;
2010-02-17 10:18:05 +03:00
char * arg = va_arg ( ap , char * ) ;
if ( arg = = NULL ) break ;
2014-02-26 10:38:07 +04:00
l = discard_const_p ( const char * , argv ) ;
l = str_list_add ( l , arg ) ;
if ( l = = NULL ) {
2010-02-17 10:18:05 +03:00
fprintf ( stderr , " Out of memory in child \n " ) ;
_exit ( 255 ) ;
}
2014-02-26 10:38:07 +04:00
argv = discard_const_p ( char * , l ) ;
2010-02-17 10:18:05 +03:00
}
va_end ( ap ) ;
2011-11-22 01:06:00 +04:00
( void ) execvp ( state - > arg0 , argv ) ;
2010-02-17 10:18:05 +03:00
fprintf ( stderr , " Failed to exec child - %s \n " , strerror ( errno ) ) ;
_exit ( 255 ) ;
return NULL ;
}
2010-02-24 14:43:45 +03:00
/*
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 ;
2017-05-16 19:36:03 +03:00
if ( ! ( flags & TEVENT_FD_READ ) ) {
return ;
}
2010-02-24 14:43:45 +03:00
if ( fde = = state - > fde_stdout ) {
level = state - > stdout_log_level ;
fd = state - > fd_stdout ;
2011-11-30 07:07:51 +04:00
} else if ( fde = = state - > fde_stderr ) {
2010-02-24 14:43:45 +03:00
level = state - > stderr_log_level ;
fd = state - > fd_stderr ;
2011-11-30 07:07:51 +04:00
} else {
2017-05-16 19:36:03 +03:00
int status ;
2010-02-24 14:43:45 +03:00
2017-05-16 19:36:03 +03:00
status = tfork_status ( & state - > tfork , false ) ;
if ( status = = - 1 ) {
if ( errno = = EAGAIN | | errno = = EWOULDBLOCK ) {
return ;
}
DBG_ERR ( " Bad read on status pipe \n " ) ;
tevent_req_error ( req , errno ) ;
return ;
}
2017-09-29 13:45:24 +03:00
state - > pid = - 1 ;
2017-09-08 05:03:25 +03:00
TALLOC_FREE ( fde ) ;
2017-05-16 19:36:03 +03:00
if ( WIFEXITED ( status ) ) {
status = WEXITSTATUS ( status ) ;
} else if ( WIFSIGNALED ( status ) ) {
status = WTERMSIG ( status ) ;
} else {
status = ECHILD ;
}
DBG_NOTICE ( " Child %s exited %d \n " , state - > arg0 , status ) ;
if ( status ! = 0 ) {
tevent_req_error ( req , status ) ;
return ;
}
tevent_req_done ( req ) ;
2017-04-28 12:33:24 +03:00
return ;
}
2017-04-11 21:05:05 +03:00
2010-02-24 14:43:45 +03:00
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 ;
2017-05-16 19:36:03 +03:00
return ;
2010-02-24 14:43:45 +03:00
}
if ( fde = = state - > fde_stderr ) {
talloc_free ( fde ) ;
state - > fde_stderr = NULL ;
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 ;
}