isync

mailbox synchronization program
git clone https://git.code.sf.net/p/isync/isync
Log | Files | Refs | README | LICENSE

commit ced20ad0d962a8809f53cbd214313cad13ad5fab
parent b84137482765c90ce0578b56e9fe322474bf4771
Author: Oswald Buddenhagen <ossi@users.sf.net>
Date:   Sun,  3 Jul 2022 11:52:20 +0200

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.

Diffstat:
Msrc/main.c | 40++++++++++++++++++++++++++++++++++++++++
Msrc/main_list.c | 2+-
Msrc/main_p.h | 1+
Msrc/main_sync.c | 2+-
Msrc/util.c | 4++++
5 files changed, 47 insertions(+), 2 deletions(-)

diff --git a/src/main.c b/src/main.c @@ -89,6 +89,26 @@ PACKAGE " " VERSION " - mailbox synchronizer\n" 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__ static void ATTR_NORETURN crashHandler( int n ) @@ -543,9 +563,29 @@ main( int argc, char **argv ) 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) list_stores( mvars, argv + oind ); else sync_chans( mvars, argv + oind ); return mvars->ret; } + +void +cleanup_mainloop( void ) +{ + cleanup_drivers(); + wipe_notifier( &child_notifier ); +} diff --git a/src/main_list.c b/src/main_list.c @@ -126,7 +126,7 @@ do_list_stores( list_vars_t *lvars ) next: advance_store( lvars ); } - cleanup_drivers(); + cleanup_mainloop(); } static void diff --git a/src/main_p.h b/src/main_p.h @@ -21,5 +21,6 @@ typedef struct { void sync_chans( core_vars_t *cvars, char **argv ); void list_stores( core_vars_t *cvars, char **argv ); +void cleanup_mainloop( void ); #endif diff --git a/src/main_sync.c b/src/main_sync.c @@ -496,7 +496,7 @@ do_sync_chans( main_vars_t *mvars ) next: advance_chan( mvars ); } - cleanup_drivers(); + cleanup_mainloop(); if (!mvars->cvars->list && (DFlags & PROGRESS)) wipe_wakeup( &stats_wakeup ); } diff --git a/src/util.c b/src/util.c @@ -1110,6 +1110,8 @@ event_wait( void ) case 0: return; case -1: + if (errno == EINTR) + return; perror( "poll() failed in event loop" ); abort(); default: @@ -1162,6 +1164,8 @@ event_wait( void ) case 0: return; case -1: + if (errno == EINTR) + return; perror( "select() failed in event loop" ); abort(); default: