fix Tunnel leaving behind zombies

this generally went unnoticed, as the tunnel usually terminates right
before we exit anyway. however, if multiple Channels are synced, it may
become visible.

this is a "shotgun" implementation, where the main loop just reaps all
unclaimed children.
arguably, it would be cleaner if each socket actually tracked its own
process, but getting synchronous kills+waits right is tricky, so we
continue to pretend that there is no process as far as the socket layer
is concerned.

poll()/select() are not restartable, so they need EINTR handling now
that SIGCHLD is actually delivered.
This commit is contained in:
Oswald Buddenhagen 2022-07-03 11:52:20 +02:00
parent b841374827
commit ced20ad0d9
5 changed files with 47 additions and 2 deletions

View File

@ -89,6 +89,26 @@ PACKAGE " " VERSION " - mailbox synchronizer\n"
exit( code ); exit( code );
} }
static int child_pipe[2];
static notifier_t child_notifier;
static void
childHandler( int n ATTR_UNUSED )
{
// We can't just reap everything here, as we might steal children
// from popen(). Let the main loop handle it synchronously instead.
char dummy = 0;
write( child_pipe[1], &dummy, 1 );
}
static void
childReaper( int events ATTR_UNUSED, void *aux ATTR_UNUSED )
{
char dummy;
while (read( child_pipe[0], &dummy, 1 ) == 1) {}
while (waitpid( -1, NULL, WNOHANG ) > 0) {}
}
#ifdef __linux__ #ifdef __linux__
static void ATTR_NORETURN static void ATTR_NORETURN
crashHandler( int n ) crashHandler( int n )
@ -543,9 +563,29 @@ main( int argc, char **argv )
signal( SIGPIPE, SIG_IGN ); signal( SIGPIPE, SIG_IGN );
if (pipe( child_pipe )) {
perror( "pipe" );
return 1;
}
fcntl( child_pipe[0], F_SETFL, O_NONBLOCK );
fcntl( child_pipe[1], F_SETFL, O_NONBLOCK );
init_notifier( &child_notifier, child_pipe[0], childReaper, NULL );
conf_notifier( &child_notifier, 0, POLLIN );
struct sigaction sa = { 0 };
sa.sa_handler = childHandler;
sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
sigaction( SIGCHLD, &sa, NULL );
if (mvars->list_stores) if (mvars->list_stores)
list_stores( mvars, argv + oind ); list_stores( mvars, argv + oind );
else else
sync_chans( mvars, argv + oind ); sync_chans( mvars, argv + oind );
return mvars->ret; return mvars->ret;
} }
void
cleanup_mainloop( void )
{
cleanup_drivers();
wipe_notifier( &child_notifier );
}

View File

@ -126,7 +126,7 @@ do_list_stores( list_vars_t *lvars )
next: next:
advance_store( lvars ); advance_store( lvars );
} }
cleanup_drivers(); cleanup_mainloop();
} }
static void static void

View File

@ -21,5 +21,6 @@ typedef struct {
void sync_chans( core_vars_t *cvars, char **argv ); void sync_chans( core_vars_t *cvars, char **argv );
void list_stores( core_vars_t *cvars, char **argv ); void list_stores( core_vars_t *cvars, char **argv );
void cleanup_mainloop( void );
#endif #endif

View File

@ -496,7 +496,7 @@ do_sync_chans( main_vars_t *mvars )
next: next:
advance_chan( mvars ); advance_chan( mvars );
} }
cleanup_drivers(); cleanup_mainloop();
if (!mvars->cvars->list && (DFlags & PROGRESS)) if (!mvars->cvars->list && (DFlags & PROGRESS))
wipe_wakeup( &stats_wakeup ); wipe_wakeup( &stats_wakeup );
} }

View File

@ -1110,6 +1110,8 @@ event_wait( void )
case 0: case 0:
return; return;
case -1: case -1:
if (errno == EINTR)
return;
perror( "poll() failed in event loop" ); perror( "poll() failed in event loop" );
abort(); abort();
default: default:
@ -1162,6 +1164,8 @@ event_wait( void )
case 0: case 0:
return; return;
case -1: case -1:
if (errno == EINTR)
return;
perror( "select() failed in event loop" ); perror( "select() failed in event loop" );
abort(); abort();
default: default: