Browse Source

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.
master
Oswald Buddenhagen 2 years ago
parent
commit
ced20ad0d9
  1. 40
      src/main.c
  2. 2
      src/main_list.c
  3. 1
      src/main_p.h
  4. 2
      src/main_sync.c
  5. 4
      src/util.c

40
src/main.c

@ -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 );
}

2
src/main_list.c

@ -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

1
src/main_p.h

@ -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

2
src/main_sync.c

@ -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 );
} }

4
src/util.c

@ -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:

Loading…
Cancel
Save