2016-08-30 10:33:42 +03:00
/*
Run a child process and collect the output
Copyright ( C ) Amitay Isaacs 2016
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/>.
*/
# include "replace.h"
# include "system/filesys.h"
# include "system/wait.h"
# include <talloc.h>
# include <tevent.h>
# include "lib/util/tevent_unix.h"
# include "lib/util/sys_rw.h"
# include "lib/util/blocking.h"
2018-05-09 07:07:35 +03:00
# include "lib/util/dlinklist.h"
2016-08-30 10:33:42 +03:00
# include "common/run_proc.h"
/*
* Process abstraction
*/
2018-05-09 07:07:35 +03:00
struct run_proc_context ;
2016-08-30 10:33:42 +03:00
struct proc_context {
2018-05-09 07:07:35 +03:00
struct proc_context * prev , * next ;
2016-08-30 10:33:42 +03:00
pid_t pid ;
int fd ;
struct tevent_fd * fde ;
char * output ;
struct run_proc_result result ;
struct tevent_req * req ;
} ;
2018-05-09 07:07:35 +03:00
static int proc_destructor ( struct proc_context * proc ) ;
static struct proc_context * proc_new ( TALLOC_CTX * mem_ctx ,
struct run_proc_context * run_ctx )
2016-08-30 10:33:42 +03:00
{
struct proc_context * proc ;
proc = talloc_zero ( mem_ctx , struct proc_context ) ;
if ( proc = = NULL ) {
return NULL ;
}
proc - > pid = - 1 ;
proc - > fd = - 1 ;
2018-05-09 07:07:35 +03:00
talloc_set_destructor ( proc , proc_destructor ) ;
2016-08-30 10:33:42 +03:00
return proc ;
}
2018-05-09 07:07:35 +03:00
static void run_proc_kill ( struct tevent_req * req ) ;
static int proc_destructor ( struct proc_context * proc )
{
if ( proc - > req ! = NULL ) {
run_proc_kill ( proc - > req ) ;
}
talloc_free ( proc - > fde ) ;
if ( proc - > pid ! = - 1 ) {
kill ( - proc - > pid , SIGKILL ) ;
}
return 0 ;
}
2016-08-30 10:33:42 +03:00
static void proc_read_handler ( struct tevent_context * ev ,
struct tevent_fd * fde , uint16_t flags ,
void * private_data ) ;
static int proc_start ( struct proc_context * proc , struct tevent_context * ev ,
2017-05-05 19:47:00 +03:00
const char * path , const char * * argv , int stdin_fd )
2016-08-30 10:33:42 +03:00
{
int fd [ 2 ] ;
int ret ;
ret = pipe ( fd ) ;
if ( ret ! = 0 ) {
return ret ;
}
proc - > pid = fork ( ) ;
if ( proc - > pid = = - 1 ) {
ret = errno ;
close ( fd [ 0 ] ) ;
close ( fd [ 1 ] ) ;
return ret ;
}
if ( proc - > pid = = 0 ) {
close ( fd [ 0 ] ) ;
ret = dup2 ( fd [ 1 ] , STDOUT_FILENO ) ;
if ( ret = = - 1 ) {
exit ( 64 + errno ) ;
}
ret = dup2 ( fd [ 1 ] , STDERR_FILENO ) ;
if ( ret = = - 1 ) {
exit ( 64 + errno ) ;
}
close ( fd [ 1 ] ) ;
2017-05-05 19:47:00 +03:00
if ( stdin_fd ! = - 1 ) {
ret = dup2 ( stdin_fd , STDIN_FILENO ) ;
if ( ret = = - 1 ) {
exit ( 64 + errno ) ;
}
}
2016-08-30 10:33:42 +03:00
ret = setpgid ( 0 , 0 ) ;
if ( ret ! = 0 ) {
exit ( 64 + errno ) ;
}
ret = execv ( path , discard_const ( argv ) ) ;
if ( ret ! = 0 ) {
exit ( 64 + errno ) ;
}
exit ( 64 + ENOEXEC ) ;
}
close ( fd [ 1 ] ) ;
proc - > fd = fd [ 0 ] ;
proc - > fde = tevent_add_fd ( ev , proc , fd [ 0 ] , TEVENT_FD_READ ,
proc_read_handler , proc ) ;
if ( proc - > fde = = NULL ) {
2018-05-09 07:07:35 +03:00
close ( fd [ 0 ] ) ;
2016-08-30 10:33:42 +03:00
return ENOMEM ;
}
tevent_fd_set_auto_close ( proc - > fde ) ;
return 0 ;
}
static void proc_read_handler ( struct tevent_context * ev ,
struct tevent_fd * fde , uint16_t flags ,
void * private_data )
{
struct proc_context * proc = talloc_get_type_abort (
private_data , struct proc_context ) ;
2017-02-01 07:52:48 +03:00
size_t offset ;
2016-08-30 10:33:42 +03:00
ssize_t nread ;
2017-02-01 07:52:48 +03:00
int len = 0 ;
2016-08-30 10:33:42 +03:00
int ret ;
ret = ioctl ( proc - > fd , FIONREAD , & len ) ;
if ( ret ! = 0 ) {
goto fail ;
}
if ( len = = 0 ) {
/* pipe closed */
goto close ;
}
offset = ( proc - > output = = NULL ) ? 0 : strlen ( proc - > output ) ;
proc - > output = talloc_realloc ( proc , proc - > output , char , offset + len + 1 ) ;
if ( proc - > output = = NULL ) {
goto fail ;
}
nread = sys_read ( proc - > fd , proc - > output + offset , len ) ;
if ( nread = = - 1 ) {
goto fail ;
}
proc - > output [ offset + nread ] = ' \0 ' ;
return ;
fail :
2018-05-09 07:07:35 +03:00
if ( proc - > pid ! = - 1 ) {
kill ( - proc - > pid , SIGKILL ) ;
proc - > pid = - 1 ;
}
2016-08-30 10:33:42 +03:00
close :
TALLOC_FREE ( proc - > fde ) ;
proc - > fd = - 1 ;
}
/*
* Run proc abstraction
*/
struct run_proc_context {
struct tevent_context * ev ;
struct tevent_signal * se ;
2018-05-09 07:07:35 +03:00
struct proc_context * plist ;
2016-08-30 10:33:42 +03:00
} ;
static void run_proc_signal_handler ( struct tevent_context * ev ,
struct tevent_signal * se ,
int signum , int count , void * siginfo ,
void * private_data ) ;
static int run_proc_context_destructor ( struct run_proc_context * run_ctx ) ;
static void run_proc_done ( struct tevent_req * req ) ;
int run_proc_init ( TALLOC_CTX * mem_ctx , struct tevent_context * ev ,
struct run_proc_context * * result )
{
struct run_proc_context * run_ctx ;
run_ctx = talloc_zero ( mem_ctx , struct run_proc_context ) ;
if ( run_ctx = = NULL ) {
return ENOMEM ;
}
run_ctx - > ev = ev ;
run_ctx - > se = tevent_add_signal ( ev , run_ctx , SIGCHLD , 0 ,
run_proc_signal_handler , run_ctx ) ;
if ( run_ctx - > se = = NULL ) {
talloc_free ( run_ctx ) ;
return ENOMEM ;
}
talloc_set_destructor ( run_ctx , run_proc_context_destructor ) ;
* result = run_ctx ;
return 0 ;
}
static void run_proc_signal_handler ( struct tevent_context * ev ,
struct tevent_signal * se ,
int signum , int count , void * siginfo ,
void * private_data )
{
struct run_proc_context * run_ctx = talloc_get_type_abort (
private_data , struct run_proc_context ) ;
struct proc_context * proc ;
pid_t pid = - 1 ;
2018-05-09 07:07:35 +03:00
int status ;
2016-08-30 10:33:42 +03:00
again :
pid = waitpid ( - 1 , & status , WNOHANG ) ;
if ( pid = = - 1 ) {
return ;
}
if ( pid = = 0 ) {
return ;
}
2018-05-09 07:07:35 +03:00
for ( proc = run_ctx - > plist ; proc ! = NULL ; proc = proc - > next ) {
if ( proc - > pid = = pid ) {
break ;
}
}
if ( proc = = NULL ) {
2016-08-30 10:33:42 +03:00
/* unknown process */
2018-05-09 07:07:35 +03:00
goto again ;
2016-08-30 10:33:42 +03:00
}
/* Mark the process as terminated */
proc - > pid = - 1 ;
/* Update process status */
if ( WIFEXITED ( status ) ) {
int pstatus = WEXITSTATUS ( status ) ;
if ( WIFSIGNALED ( status ) ) {
proc - > result . sig = WTERMSIG ( status ) ;
} else if ( pstatus > = 64 & & pstatus < 255 ) {
proc - > result . err = pstatus - 64 ;
} else {
proc - > result . status = pstatus ;
}
} else if ( WIFSIGNALED ( status ) ) {
proc - > result . sig = WTERMSIG ( status ) ;
}
2019-04-09 07:44:04 +03:00
/* Confirm that all data has been read from the pipe */
if ( proc - > fd ! = - 1 ) {
proc_read_handler ( ev , proc - > fde , 0 , proc ) ;
TALLOC_FREE ( proc - > fde ) ;
proc - > fd = - 1 ;
}
2019-05-13 10:07:59 +03:00
DLIST_REMOVE ( run_ctx - > plist , proc ) ;
2016-08-30 10:33:42 +03:00
/* Active run_proc request */
if ( proc - > req ! = NULL ) {
run_proc_done ( proc - > req ) ;
2019-05-13 10:07:59 +03:00
} else {
talloc_free ( proc ) ;
2016-08-30 10:33:42 +03:00
}
goto again ;
}
static int run_proc_context_destructor ( struct run_proc_context * run_ctx )
{
2018-05-09 07:07:35 +03:00
struct proc_context * proc ;
2016-08-30 10:33:42 +03:00
/* Get rid of signal handler */
TALLOC_FREE ( run_ctx - > se ) ;
/* Kill any pending processes */
2018-05-09 07:07:35 +03:00
while ( ( proc = run_ctx - > plist ) ! = NULL ) {
DLIST_REMOVE ( run_ctx - > plist , proc ) ;
talloc_free ( proc ) ;
}
2016-08-30 10:33:42 +03:00
return 0 ;
}
struct run_proc_state {
struct tevent_context * ev ;
2018-05-09 07:07:35 +03:00
struct run_proc_context * run_ctx ;
2016-08-30 10:33:42 +03:00
struct proc_context * proc ;
struct run_proc_result result ;
char * output ;
pid_t pid ;
} ;
static int run_proc_state_destructor ( struct run_proc_state * state ) ;
static void run_proc_timedout ( struct tevent_req * subreq ) ;
struct tevent_req * run_proc_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct run_proc_context * run_ctx ,
const char * path , const char * * argv ,
2017-05-05 19:47:00 +03:00
int stdin_fd , struct timeval timeout )
2016-08-30 10:33:42 +03:00
{
struct tevent_req * req ;
struct run_proc_state * state ;
struct stat st ;
int ret ;
req = tevent_req_create ( mem_ctx , & state , struct run_proc_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > ev = ev ;
2018-05-09 07:07:35 +03:00
state - > run_ctx = run_ctx ;
2016-08-30 10:33:42 +03:00
state - > pid = - 1 ;
ret = stat ( path , & st ) ;
if ( ret ! = 0 ) {
state - > result . err = errno ;
tevent_req_done ( req ) ;
return tevent_req_post ( req , ev ) ;
}
if ( ! ( st . st_mode & S_IXUSR ) ) {
state - > result . err = EACCES ;
tevent_req_done ( req ) ;
return tevent_req_post ( req , ev ) ;
}
2018-05-09 07:07:35 +03:00
state - > proc = proc_new ( run_ctx , run_ctx ) ;
2016-08-30 10:33:42 +03:00
if ( tevent_req_nomem ( state - > proc , req ) ) {
return tevent_req_post ( req , ev ) ;
}
2018-05-09 07:07:35 +03:00
state - > proc - > req = req ;
DLIST_ADD ( run_ctx - > plist , state - > proc ) ;
2017-05-05 19:47:00 +03:00
ret = proc_start ( state - > proc , ev , path , argv , stdin_fd ) ;
2016-08-30 10:33:42 +03:00
if ( ret ! = 0 ) {
tevent_req_error ( req , ret ) ;
return tevent_req_post ( req , ev ) ;
}
talloc_set_destructor ( state , run_proc_state_destructor ) ;
if ( ! tevent_timeval_is_zero ( & timeout ) ) {
struct tevent_req * subreq ;
subreq = tevent_wakeup_send ( state , ev , timeout ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , run_proc_timedout , req ) ;
}
return req ;
}
static int run_proc_state_destructor ( struct run_proc_state * state )
{
/* Do not get rid of the child process if timeout has occurred */
if ( state - > proc - > req ! = NULL ) {
state - > proc - > req = NULL ;
2018-05-09 07:07:35 +03:00
DLIST_REMOVE ( state - > run_ctx - > plist , state - > proc ) ;
talloc_free ( state - > proc ) ;
2016-08-30 10:33:42 +03:00
}
return 0 ;
}
static void run_proc_done ( struct tevent_req * req )
{
struct run_proc_state * state = tevent_req_data (
req , struct run_proc_state ) ;
state - > proc - > req = NULL ;
state - > result = state - > proc - > result ;
if ( state - > proc - > output ! = NULL ) {
2021-05-18 09:32:45 +03:00
state - > output = talloc_move ( state , & state - > proc - > output ) ;
2016-08-30 10:33:42 +03:00
}
2019-05-13 10:07:59 +03:00
talloc_steal ( state , state - > proc ) ;
2016-08-30 10:33:42 +03:00
tevent_req_done ( req ) ;
}
2018-05-09 07:07:35 +03:00
static void run_proc_kill ( struct tevent_req * req )
{
struct run_proc_state * state = tevent_req_data (
req , struct run_proc_state ) ;
state - > proc - > req = NULL ;
state - > result . sig = SIGKILL ;
tevent_req_done ( req ) ;
}
2016-08-30 10:33:42 +03:00
static void run_proc_timedout ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct run_proc_state * state = tevent_req_data (
req , struct run_proc_state ) ;
bool status ;
state - > proc - > req = NULL ;
status = tevent_wakeup_recv ( subreq ) ;
TALLOC_FREE ( subreq ) ;
if ( ! status ) {
tevent_req_error ( req , EIO ) ;
return ;
}
2018-07-10 11:19:09 +03:00
state - > result . err = ETIMEDOUT ;
2016-08-30 10:33:42 +03:00
if ( state - > proc - > output ! = NULL ) {
2021-05-18 09:32:45 +03:00
state - > output = talloc_move ( state , & state - > proc - > output ) ;
2016-08-30 10:33:42 +03:00
}
state - > pid = state - > proc - > pid ;
tevent_req_done ( req ) ;
}
bool run_proc_recv ( struct tevent_req * req , int * perr ,
struct run_proc_result * result , pid_t * pid ,
TALLOC_CTX * mem_ctx , char * * output )
{
struct run_proc_state * state = tevent_req_data (
req , struct run_proc_state ) ;
int ret ;
if ( tevent_req_is_unix_error ( req , & ret ) ) {
if ( perr ! = NULL ) {
* perr = ret ;
}
return false ;
}
if ( result ! = NULL ) {
* result = state - > result ;
}
if ( pid ! = NULL ) {
* pid = state - > pid ;
}
if ( output ! = NULL ) {
2021-05-18 09:32:45 +03:00
* output = talloc_move ( mem_ctx , & state - > output ) ;
2016-08-30 10:33:42 +03:00
}
return true ;
}