2017-04-11 18:32:01 +03:00
/*
* Tests for tfork
*
* Copyright Ralph Boehme < slow @ samba . org > 2017
*
* 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 <talloc.h>
2017-04-11 21:00:05 +03:00
# include <tevent.h>
2017-04-11 18:32:01 +03:00
# include "system/filesys.h"
2017-05-01 18:09:35 +03:00
# include "system/wait.h"
2017-09-11 05:48:21 +03:00
# include "system/select.h"
2017-04-11 18:32:01 +03:00
# include "libcli/util/ntstatus.h"
# include "torture/torture.h"
# include "lib/util/data_blob.h"
# include "torture/local/proto.h"
# include "lib/util/tfork.h"
# include "lib/util/samba_util.h"
# include "lib/util/sys_rw.h"
2017-05-26 19:10:07 +03:00
# ifdef HAVE_PTHREAD
# include <pthread.h>
# endif
2017-04-11 18:32:01 +03:00
static bool test_tfork_simple ( struct torture_context * tctx )
{
2017-04-26 01:48:39 +03:00
pid_t parent = getpid ( ) ;
struct tfork * t = NULL ;
pid_t child ;
int ret ;
2017-04-11 18:32:01 +03:00
2017-04-26 01:48:39 +03:00
t = tfork_create ( ) ;
if ( t = = NULL ) {
torture_fail ( tctx , " tfork failed \n " ) ;
return false ;
}
child = tfork_child_pid ( t ) ;
if ( child = = 0 ) {
torture_comment ( tctx , " my parent pid is %d \n " , parent ) ;
torture_assert ( tctx , getpid ( ) ! = parent , " tfork failed \n " ) ;
_exit ( 0 ) ;
}
2017-04-11 18:32:01 +03:00
2017-04-26 01:48:39 +03:00
ret = tfork_destroy ( & t ) ;
torture_assert ( tctx , ret = = 0 , " tfork_destroy failed \n " ) ;
return true ;
2017-04-11 18:32:01 +03:00
}
static bool test_tfork_status ( struct torture_context * tctx )
{
2017-04-26 01:48:39 +03:00
struct tfork * t = NULL ;
2017-04-11 18:32:01 +03:00
int status ;
2017-04-26 01:48:39 +03:00
pid_t child ;
2017-04-11 18:32:01 +03:00
bool ok = true ;
2017-04-26 01:48:39 +03:00
t = tfork_create ( ) ;
if ( t = = NULL ) {
2017-04-11 18:32:01 +03:00
torture_fail ( tctx , " tfork failed \n " ) ;
return false ;
}
2017-04-26 01:48:39 +03:00
child = tfork_child_pid ( t ) ;
if ( child = = 0 ) {
_exit ( 123 ) ;
}
2017-04-11 18:32:01 +03:00
2017-04-26 01:48:39 +03:00
status = tfork_status ( & t , true ) ;
if ( status = = - 1 ) {
torture_fail ( tctx , " tfork_status failed \n " ) ;
2017-04-11 18:32:01 +03:00
}
torture_assert_goto ( tctx , WIFEXITED ( status ) = = true , ok , done ,
" tfork failed \n " ) ;
torture_assert_goto ( tctx , WEXITSTATUS ( status ) = = 123 , ok , done ,
" tfork failed \n " ) ;
torture_comment ( tctx , " exit status [%d] \n " , WEXITSTATUS ( status ) ) ;
done :
return ok ;
}
2017-05-26 19:10:07 +03:00
static bool test_tfork_sigign ( struct torture_context * tctx )
{
struct tfork * t = NULL ;
struct sigaction act ;
pid_t child ;
int status ;
bool ok = true ;
int ret ;
act = ( struct sigaction ) {
. sa_flags = SA_NOCLDWAIT ,
. sa_handler = SIG_IGN ,
} ;
ret = sigaction ( SIGCHLD , & act , NULL ) ;
torture_assert_goto ( tctx , ret = = 0 , ok , done , " sigaction failed \n " ) ;
t = tfork_create ( ) ;
if ( t = = NULL ) {
torture_fail ( tctx , " tfork failed \n " ) ;
return false ;
}
child = tfork_child_pid ( t ) ;
if ( child = = 0 ) {
sleep ( 1 ) ;
_exit ( 123 ) ;
}
child = fork ( ) ;
if ( child = = - 1 ) {
torture_fail ( tctx , " fork failed \n " ) ;
return false ;
}
if ( child = = 0 ) {
_exit ( 0 ) ;
}
status = tfork_status ( & t , true ) ;
if ( status = = - 1 ) {
torture_fail ( tctx , " tfork_status failed \n " ) ;
}
torture_assert_goto ( tctx , WIFEXITED ( status ) = = true , ok , done ,
" tfork failed \n " ) ;
torture_assert_goto ( tctx , WEXITSTATUS ( status ) = = 123 , ok , done ,
" tfork failed \n " ) ;
torture_comment ( tctx , " exit status [%d] \n " , WEXITSTATUS ( status ) ) ;
done :
return ok ;
}
static void sigchld_handler1 ( int signum , siginfo_t * si , void * u )
{
pid_t pid ;
int status ;
if ( signum ! = SIGCHLD ) {
abort ( ) ;
}
pid = waitpid ( si - > si_pid , & status , 0 ) ;
if ( pid ! = si - > si_pid ) {
abort ( ) ;
}
}
static bool test_tfork_sighandler ( struct torture_context * tctx )
{
struct tfork * t = NULL ;
struct sigaction act ;
struct sigaction oldact ;
pid_t child ;
int status ;
bool ok = true ;
int ret ;
act = ( struct sigaction ) {
. sa_flags = SA_SIGINFO ,
. sa_sigaction = sigchld_handler1 ,
} ;
ret = sigaction ( SIGCHLD , & act , & oldact ) ;
torture_assert_goto ( tctx , ret = = 0 , ok , done , " sigaction failed \n " ) ;
t = tfork_create ( ) ;
if ( t = = NULL ) {
torture_fail ( tctx , " tfork failed \n " ) ;
return false ;
}
child = tfork_child_pid ( t ) ;
if ( child = = 0 ) {
sleep ( 1 ) ;
_exit ( 123 ) ;
}
child = fork ( ) ;
if ( child = = - 1 ) {
torture_fail ( tctx , " fork failed \n " ) ;
return false ;
}
if ( child = = 0 ) {
_exit ( 0 ) ;
}
status = tfork_status ( & t , true ) ;
if ( status = = - 1 ) {
torture_fail ( tctx , " tfork_status failed \n " ) ;
}
torture_assert_goto ( tctx , WIFEXITED ( status ) = = true , ok , done ,
" tfork failed \n " ) ;
torture_assert_goto ( tctx , WEXITSTATUS ( status ) = = 123 , ok , done ,
" tfork failed \n " ) ;
torture_comment ( tctx , " exit status [%d] \n " , WEXITSTATUS ( status ) ) ;
done :
sigaction ( SIGCHLD , & oldact , NULL ) ;
return ok ;
}
static bool test_tfork_process_hierarchy ( struct torture_context * tctx )
{
struct tfork * t = NULL ;
pid_t pid = getpid ( ) ;
pid_t child ;
pid_t pgid = getpgid ( 0 ) ;
pid_t sid = getsid ( 0 ) ;
char * procpath = NULL ;
int status ;
struct stat st ;
int ret ;
bool ok = true ;
procpath = talloc_asprintf ( tctx , " /proc/%d/status " , getpid ( ) ) ;
torture_assert_not_null ( tctx , procpath , " talloc_asprintf failed \n " ) ;
ret = stat ( procpath , & st ) ;
TALLOC_FREE ( procpath ) ;
if ( ret ! = 0 ) {
if ( errno = = ENOENT ) {
torture_skip ( tctx , " /proc missing \n " ) ;
}
torture_fail ( tctx , " stat failed \n " ) ;
}
t = tfork_create ( ) ;
if ( t = = NULL ) {
torture_fail ( tctx , " tfork failed \n " ) ;
return false ;
}
child = tfork_child_pid ( t ) ;
if ( child = = 0 ) {
char * cmd = NULL ;
FILE * fp = NULL ;
char line [ 64 ] ;
char * p ;
pid_t ppid ;
torture_assert_goto ( tctx , pgid = = getpgid ( 0 ) , ok , child_fail , " tfork failed \n " ) ;
torture_assert_goto ( tctx , sid = = getsid ( 0 ) , ok , child_fail , " tfork failed \n " ) ;
cmd = talloc_asprintf ( tctx , " cat /proc/%d/status | awk '/^PPid:/ {print $2}' " , getppid ( ) ) ;
torture_assert_goto ( tctx , cmd ! = NULL , ok , child_fail , " talloc_asprintf failed \n " ) ;
fp = popen ( cmd , " r " ) ;
torture_assert_goto ( tctx , fp ! = NULL , ok , child_fail , " popen failed \n " ) ;
p = fgets ( line , sizeof ( line ) - 1 , fp ) ;
pclose ( fp ) ;
torture_assert_goto ( tctx , p ! = NULL , ok , child_fail , " popen failed \n " ) ;
ret = sscanf ( line , " %d " , & ppid ) ;
torture_assert_goto ( tctx , ret = = 1 , ok , child_fail , " sscanf failed \n " ) ;
2019-08-29 22:50:45 +03:00
torture_assert_goto ( tctx , ppid = = pid , ok , child_fail , " process hierarchy not rooted at caller \n " ) ;
2017-05-26 19:10:07 +03:00
_exit ( 0 ) ;
child_fail :
_exit ( 1 ) ;
}
status = tfork_status ( & t , true ) ;
if ( status = = - 1 ) {
torture_fail ( tctx , " tfork_status failed \n " ) ;
}
torture_assert_goto ( tctx , WIFEXITED ( status ) = = true , ok , done ,
" tfork failed \n " ) ;
torture_assert_goto ( tctx , WEXITSTATUS ( status ) = = 0 , ok , done ,
" tfork failed \n " ) ;
torture_comment ( tctx , " exit status [%d] \n " , WEXITSTATUS ( status ) ) ;
done :
return ok ;
}
static bool test_tfork_pipe ( struct torture_context * tctx )
{
struct tfork * t = NULL ;
int status ;
pid_t child ;
int up [ 2 ] ;
int down [ 2 ] ;
char c ;
int ret ;
bool ok = true ;
ret = pipe ( & up [ 0 ] ) ;
torture_assert ( tctx , ret = = 0 , " pipe failed \n " ) ;
ret = pipe ( & down [ 0 ] ) ;
torture_assert ( tctx , ret = = 0 , " pipe failed \n " ) ;
t = tfork_create ( ) ;
if ( t = = NULL ) {
torture_fail ( tctx , " tfork failed \n " ) ;
return false ;
}
child = tfork_child_pid ( t ) ;
if ( child = = 0 ) {
close ( up [ 0 ] ) ;
close ( down [ 1 ] ) ;
ret = read ( down [ 0 ] , & c , 1 ) ;
torture_assert_goto ( tctx , ret = = 1 , ok , child_fail , " read failed \n " ) ;
torture_assert_goto ( tctx , c = = 1 , ok , child_fail , " read failed \n " ) ;
ret = write ( up [ 1 ] , & ( char ) { 2 } , 1 ) ;
torture_assert_goto ( tctx , ret = = 1 , ok , child_fail , " write failed \n " ) ;
_exit ( 0 ) ;
child_fail :
_exit ( 1 ) ;
}
close ( up [ 1 ] ) ;
close ( down [ 0 ] ) ;
ret = write ( down [ 1 ] , & ( char ) { 1 } , 1 ) ;
torture_assert ( tctx , ret = = 1 , " read failed \n " ) ;
ret = read ( up [ 0 ] , & c , 1 ) ;
torture_assert ( tctx , ret = = 1 , " read failed \n " ) ;
torture_assert ( tctx , c = = 2 , " read failed \n " ) ;
status = tfork_status ( & t , true ) ;
if ( status = = - 1 ) {
torture_fail ( tctx , " tfork_status failed \n " ) ;
}
torture_assert_goto ( tctx , WIFEXITED ( status ) = = true , ok , done ,
" tfork failed \n " ) ;
torture_assert_goto ( tctx , WEXITSTATUS ( status ) = = 0 , ok , done ,
" tfork failed \n " ) ;
done :
return ok ;
}
static bool test_tfork_twice ( struct torture_context * tctx )
{
struct tfork * t = NULL ;
int status ;
pid_t child ;
pid_t pid ;
int up [ 2 ] ;
int ret ;
bool ok = true ;
ret = pipe ( & up [ 0 ] ) ;
torture_assert ( tctx , ret = = 0 , " pipe failed \n " ) ;
t = tfork_create ( ) ;
if ( t = = NULL ) {
torture_fail ( tctx , " tfork failed \n " ) ;
return false ;
}
child = tfork_child_pid ( t ) ;
if ( child = = 0 ) {
close ( up [ 0 ] ) ;
t = tfork_create ( ) ;
if ( t = = NULL ) {
torture_fail ( tctx , " tfork failed \n " ) ;
return false ;
}
child = tfork_child_pid ( t ) ;
if ( child = = 0 ) {
sleep ( 1 ) ;
pid = getpid ( ) ;
ret = write ( up [ 1 ] , & pid , sizeof ( pid_t ) ) ;
torture_assert_goto ( tctx , ret = = sizeof ( pid_t ) , ok , child_fail , " write failed \n " ) ;
_exit ( 0 ) ;
child_fail :
_exit ( 1 ) ;
}
_exit ( 0 ) ;
}
close ( up [ 1 ] ) ;
ret = read ( up [ 0 ] , & pid , sizeof ( pid_t ) ) ;
torture_assert ( tctx , ret = = sizeof ( pid_t ) , " read failed \n " ) ;
status = tfork_status ( & t , true ) ;
torture_assert_goto ( tctx , status ! = - 1 , ok , done , " tfork_status failed \n " ) ;
torture_assert_goto ( tctx , WIFEXITED ( status ) = = true , ok , done ,
" tfork failed \n " ) ;
torture_assert_goto ( tctx , WEXITSTATUS ( status ) = = 0 , ok , done ,
" tfork failed \n " ) ;
done :
return ok ;
}
static void * tfork_thread ( void * p )
{
struct tfork * t = NULL ;
int status ;
pid_t child ;
2018-05-09 18:52:19 +03:00
uint64_t tid = ( uint64_t ) pthread_self ( ) ;
2017-05-26 19:10:07 +03:00
uint64_t * result = NULL ;
int up [ 2 ] ;
ssize_t nread ;
int ret ;
ret = pipe ( up ) ;
if ( ret ! = 0 ) {
pthread_exit ( NULL ) ;
}
t = tfork_create ( ) ;
if ( t = = NULL ) {
pthread_exit ( NULL ) ;
}
child = tfork_child_pid ( t ) ;
if ( child = = 0 ) {
ssize_t nwritten ;
close ( up [ 0 ] ) ;
tid + + ;
nwritten = sys_write ( up [ 1 ] , & tid , sizeof ( uint64_t ) ) ;
if ( nwritten ! = sizeof ( uint64_t ) ) {
_exit ( 1 ) ;
}
_exit ( 0 ) ;
}
close ( up [ 1 ] ) ;
result = malloc ( sizeof ( uint64_t ) ) ;
if ( result = = NULL ) {
pthread_exit ( NULL ) ;
}
nread = sys_read ( up [ 0 ] , result , sizeof ( uint64_t ) ) ;
if ( nread ! = sizeof ( uint64_t ) ) {
pthread_exit ( NULL ) ;
}
status = tfork_status ( & t , true ) ;
if ( status = = - 1 ) {
pthread_exit ( NULL ) ;
}
pthread_exit ( result ) ;
}
static bool test_tfork_threads ( struct torture_context * tctx )
{
int ret ;
bool ok = true ;
const int num_threads = 64 ;
pthread_t threads [ num_threads ] ;
2018-11-19 18:47:33 +03:00
sigset_t set ;
2017-05-26 19:10:07 +03:00
int i ;
# ifndef HAVE_PTHREAD
torture_skip ( tctx , " no pthread support \n " ) ;
# endif
2018-11-19 18:47:33 +03:00
/*
* Be nasty and taste for the worst case : ensure all threads start with
* SIGCHLD unblocked so we have the most fun with SIGCHLD being
* delivered to a random thread . : )
*/
sigemptyset ( & set ) ;
sigaddset ( & set , SIGCHLD ) ;
# ifdef HAVE_PTHREAD
ret = pthread_sigmask ( SIG_UNBLOCK , & set , NULL ) ;
# else
ret = sigprocmask ( SIG_UNBLOCK , & set , NULL ) ;
# endif
if ( ret ! = 0 ) {
2019-05-09 22:17:24 +03:00
return false ;
2018-11-19 18:47:33 +03:00
}
2017-05-26 19:10:07 +03:00
for ( i = 0 ; i < num_threads ; i + + ) {
2018-05-09 18:52:19 +03:00
ret = pthread_create ( & threads [ i ] , NULL , tfork_thread , NULL ) ;
2017-05-26 19:10:07 +03:00
torture_assert_goto ( tctx , ret = = 0 , ok , done ,
" pthread_create failed \n " ) ;
}
for ( i = 0 ; i < num_threads ; i + + ) {
void * p ;
uint64_t * result ;
ret = pthread_join ( threads [ i ] , & p ) ;
torture_assert_goto ( tctx , ret = = 0 , ok , done ,
" pthread_join failed \n " ) ;
result = ( uint64_t * ) p ;
torture_assert_goto ( tctx , * result = = ( uint64_t ) threads [ i ] + 1 ,
ok , done , " thread failed \n " ) ;
free ( p ) ;
}
done :
return ok ;
}
2017-04-11 21:00:05 +03:00
static bool test_tfork_cmd_send ( struct torture_context * tctx )
{
struct tevent_context * ev = NULL ;
struct tevent_req * req = NULL ;
const char * cmd [ 2 ] = { NULL , NULL } ;
bool ok = true ;
ev = tevent_context_init ( tctx ) ;
torture_assert_goto ( tctx , ev ! = NULL , ok , done ,
" tevent_context_init failed \n " ) ;
cmd [ 0 ] = talloc_asprintf ( tctx , " %s/testprogs/blackbox/tfork.sh " , SRCDIR ) ;
torture_assert_goto ( tctx , cmd [ 0 ] ! = NULL , ok , done ,
" talloc_asprintf failed \n " ) ;
2017-05-18 13:02:22 +03:00
req = samba_runcmd_send ( tctx , ev , timeval_zero ( ) , 0 , 0 ,
2017-04-11 21:00:05 +03:00
cmd , " foo " , NULL ) ;
torture_assert_goto ( tctx , req ! = NULL , ok , done ,
" samba_runcmd_send failed \n " ) ;
ok = tevent_req_poll ( req , ev ) ;
torture_assert_goto ( tctx , ok , ok , done , " tevent_req_poll failed \n " ) ;
torture_comment ( tctx , " samba_runcmd_send test finished \n " ) ;
done :
TALLOC_FREE ( ev ) ;
return ok ;
}
2017-09-11 05:48:21 +03:00
/*
* Test to ensure that the event_fd becomes readable after
* a tfork_process terminates .
*/
static bool test_tfork_event_file_handle ( struct torture_context * tctx )
{
bool ok = true ;
struct tfork * t1 = NULL ;
pid_t child1 ;
2019-01-07 19:33:57 +03:00
struct pollfd poll1 [ ] = {
{
. fd = - 1 ,
. events = POLLIN ,
} ,
} ;
2017-09-11 05:48:21 +03:00
struct tfork * t2 = NULL ;
pid_t child2 ;
2019-01-07 19:33:57 +03:00
struct pollfd poll2 [ ] = {
{
. fd = - 1 ,
. events = POLLIN ,
} ,
} ;
2017-09-11 05:48:21 +03:00
t1 = tfork_create ( ) ;
if ( t1 = = NULL ) {
torture_fail ( tctx , " tfork failed \n " ) ;
return false ;
}
child1 = tfork_child_pid ( t1 ) ;
if ( child1 = = 0 ) {
/*
* Parent process will kill this with a SIGTERM
* so 10 seconds should be plenty
*/
sleep ( 10 ) ;
exit ( 1 ) ;
}
poll1 [ 0 ] . fd = tfork_event_fd ( t1 ) ;
t2 = tfork_create ( ) ;
if ( t2 = = NULL ) {
torture_fail ( tctx , " tfork failed \n " ) ;
return false ;
}
child2 = tfork_child_pid ( t2 ) ;
if ( child2 = = 0 ) {
/*
* Parent process will kill this with a SIGTERM
* so 10 seconds should be plenty
*/
sleep ( 10 ) ;
exit ( 2 ) ;
}
poll2 [ 0 ] . fd = tfork_event_fd ( t2 ) ;
/*
* Have forked two process and are in the master process
* Expect that both event_fds are unreadable
*/
poll ( poll1 , 1 , 0 ) ;
ok = ! ( poll1 [ 0 ] . revents & POLLIN ) ;
torture_assert_goto ( tctx , ok , ok , done ,
" tfork process 1 event fd readable \n " ) ;
poll ( poll2 , 1 , 0 ) ;
ok = ! ( poll2 [ 0 ] . revents & POLLIN ) ;
torture_assert_goto ( tctx , ok , ok , done ,
" tfork process 1 event fd readable \n " ) ;
/* Kill the first child process */
kill ( child1 , SIGKILL ) ;
sleep ( 1 ) ;
/*
* Have killed the first child , so expect it ' s event_fd to have gone
* readable .
*
*/
poll ( poll1 , 1 , 0 ) ;
ok = ( poll1 [ 0 ] . revents & POLLIN ) ;
torture_assert_goto ( tctx , ok , ok , done ,
" tfork process 1 event fd not readable \n " ) ;
poll ( poll2 , 1 , 0 ) ;
ok = ! ( poll2 [ 0 ] . revents & POLLIN ) ;
torture_assert_goto ( tctx , ok , ok , done ,
" tfork process 2 event fd readable \n " ) ;
/* Kill the secind child process */
kill ( child2 , SIGKILL ) ;
sleep ( 1 ) ;
/*
* Have killed the children , so expect their event_fd ' s to have gone
* readable .
*
*/
poll ( poll1 , 1 , 0 ) ;
ok = ( poll1 [ 0 ] . revents & POLLIN ) ;
torture_assert_goto ( tctx , ok , ok , done ,
" tfork process 1 event fd not readable \n " ) ;
poll ( poll2 , 1 , 0 ) ;
ok = ( poll2 [ 0 ] . revents & POLLIN ) ;
torture_assert_goto ( tctx , ok , ok , done ,
" tfork process 2 event fd not readable \n " ) ;
done :
2019-07-29 16:46:15 +03:00
free ( t1 ) ;
free ( t2 ) ;
2017-09-11 05:48:21 +03:00
return ok ;
}
/*
* Test to ensure that the status calls behave as expected after a process
* terminates .
*
* As the parent process owns the status fd ' s they get passed to all
* subsequent children after a tfork . So it ' s possible for another
* child process to hold the status pipe open .
*
* The event fd needs to be left open by tfork , as a close in the status
* code can cause issues in tevent code .
*
*/
static bool test_tfork_status_handle ( struct torture_context * tctx )
{
bool ok = true ;
struct tfork * t1 = NULL ;
pid_t child1 ;
struct tfork * t2 = NULL ;
pid_t child2 ;
int status ;
int fd ;
int ev1_fd ;
int ev2_fd ;
t1 = tfork_create ( ) ;
if ( t1 = = NULL ) {
torture_fail ( tctx , " tfork failed \n " ) ;
return false ;
}
child1 = tfork_child_pid ( t1 ) ;
if ( child1 = = 0 ) {
/*
* Parent process will kill this with a SIGTERM
* so 10 seconds should be plenty
*/
sleep ( 10 ) ;
exit ( 1 ) ;
}
ev1_fd = tfork_event_fd ( t1 ) ;
t2 = tfork_create ( ) ;
if ( t2 = = NULL ) {
torture_fail ( tctx , " tfork failed \n " ) ;
return false ;
}
child2 = tfork_child_pid ( t2 ) ;
if ( child2 = = 0 ) {
/*
* Parent process will kill this with a SIGTERM
* so 10 seconds should be plenty
*/
sleep ( 10 ) ;
exit ( 2 ) ;
}
ev2_fd = tfork_event_fd ( t2 ) ;
/*
* Have forked two process and are in the master process
* expect that the status call will block , and hence return - 1
* as the processes are still running
* The event fd ' s should be open .
*/
status = tfork_status ( & t1 , false ) ;
ok = status = = - 1 ;
torture_assert_goto ( tctx , ok , ok , done ,
" tfork status available for non terminated "
" process 1 \n " ) ;
/* Is the event fd open? */
fd = dup ( ev1_fd ) ;
ok = fd ! = - 1 ;
torture_assert_goto ( tctx , ok , ok , done ,
" tfork process 1 event fd is not open " ) ;
status = tfork_status ( & t2 , false ) ;
ok = status = = - 1 ;
torture_assert_goto ( tctx , ok , ok , done ,
2019-08-29 22:53:18 +03:00
" tfork status available for non terminated "
2017-09-11 05:48:21 +03:00
" process 2 \n " ) ;
/* Is the event fd open? */
fd = dup ( ev2_fd ) ;
ok = fd ! = - 1 ;
torture_assert_goto ( tctx , ok , ok , done ,
" tfork process 2 event fd is not open " ) ;
/*
* Kill the first process , it ' s status should be readable
* and it ' s event_fd should be open
* The second process ' s status should be unreadable .
*/
kill ( child1 , SIGTERM ) ;
sleep ( 1 ) ;
status = tfork_status ( & t1 , false ) ;
ok = status ! = - 1 ;
torture_assert_goto ( tctx , ok , ok , done ,
" tfork status for child 1 not available after "
" termination \n " ) ;
/* Is the event fd open? */
fd = dup ( ev2_fd ) ;
ok = fd ! = - 1 ;
torture_assert_goto ( tctx , ok , ok , done ,
" tfork process 1 event fd is not open " ) ;
status = tfork_status ( & t2 , false ) ;
ok = status = = - 1 ;
torture_assert_goto ( tctx , ok , ok , done ,
" tfork status available for child 2 after "
" termination of child 1 \n " ) ;
/*
* Kill the second process , it ' s status should be readable
*/
kill ( child2 , SIGTERM ) ;
sleep ( 1 ) ;
status = tfork_status ( & t2 , false ) ;
ok = status ! = - 1 ;
torture_assert_goto ( tctx , ok , ok , done ,
" tfork status for child 2 not available after "
" termination \n " ) ;
/* Check that the event fd's are still open */
/* Is the event fd open? */
fd = dup ( ev1_fd ) ;
ok = fd ! = - 1 ;
torture_assert_goto ( tctx , ok , ok , done ,
" tfork process 1 event fd is not open " ) ;
/* Is the event fd open? */
fd = dup ( ev2_fd ) ;
ok = fd ! = - 1 ;
torture_assert_goto ( tctx , ok , ok , done ,
" tfork process 2 event fd is not open " ) ;
done :
return ok ;
}
2017-04-11 18:32:01 +03:00
struct torture_suite * torture_local_tfork ( TALLOC_CTX * mem_ctx )
{
struct torture_suite * suite =
torture_suite_create ( mem_ctx , " tfork " ) ;
torture_suite_add_simple_test ( suite ,
" tfork_simple " ,
test_tfork_simple ) ;
torture_suite_add_simple_test ( suite ,
" tfork_status " ,
test_tfork_status ) ;
2017-05-26 19:10:07 +03:00
torture_suite_add_simple_test ( suite ,
" tfork_sigign " ,
test_tfork_sigign ) ;
torture_suite_add_simple_test ( suite ,
" tfork_sighandler " ,
test_tfork_sighandler ) ;
torture_suite_add_simple_test ( suite ,
" tfork_process_hierarchy " ,
test_tfork_process_hierarchy ) ;
torture_suite_add_simple_test ( suite ,
" tfork_pipe " ,
test_tfork_pipe ) ;
torture_suite_add_simple_test ( suite ,
" tfork_twice " ,
test_tfork_twice ) ;
torture_suite_add_simple_test ( suite ,
" tfork_threads " ,
test_tfork_threads ) ;
2017-04-11 21:00:05 +03:00
torture_suite_add_simple_test ( suite ,
" tfork_cmd_send " ,
test_tfork_cmd_send ) ;
2017-09-11 05:48:21 +03:00
torture_suite_add_simple_test ( suite ,
" tfork_event_file_handle " ,
test_tfork_event_file_handle ) ;
torture_suite_add_simple_test ( suite ,
" tfork_status_handle " ,
test_tfork_status_handle ) ;
2017-04-11 18:32:01 +03:00
return suite ;
}