Home > computers > linux > clsync | About

Understanding how rsync is called from the source code

in main.c, we call sync_run

... main(...) {
...
	// == RUNNING ==
	if ( ret == 0 )
		ret = sync_run ( ctx_p );

	// == /RUNNING ==
...
}

in sync.c, sync_run sets the notification handler then calls sync_loop
the notification handler will be called for each new event on the watched
files/directories of the filesystem

__extension__ int sync_run ( ctx_t *ctx_p )
{
...
		// Preparing monitor subsystem context function pointers
		switch ( ctx_p->flags[MONITOR] ) {
#ifdef INOTIFY_SUPPORT

			case NE_INOTIFY:
				ctx_p->notifyenginefunct.add_watch_dir = inotify_add_watch_dir;
				ctx_p->notifyenginefunct.wait          = inotify_wait;
				ctx_p->notifyenginefunct.handle        = inotify_handle;
				break;
#endif
...
...
	// "Infinite" loop of processling the events
	ret = sync_loop ( ctx_p, &indexes );
...
}

sync_loop will call ctx_p→notifiyenginefunct.handle for each new event
but first it calls sync_intialsync to mark all the necessary files
and directories that need to be watched as such.

int sync_loop ( ctx_t *ctx_p, indexes_t *indexes_p )
{
...
			case STATE_INITSYNC:
			...
				ret = sync_initialsync ( ctx_p->watchdir, ctx_p, indexes_p, INITSYNC_FULL );
		        ...
		...
		int count = ctx_p->notifyenginefunct.handle ( ctx_p, indexes_p );
...
}

ctx_p→notifyenginefunct.handle was set in sync_run to inotify_handle
inotify_handle is defined in mon_inotify.c
it calls sync_prequeue_loadmark

int inotify_handle ( ctx_t *ctx_p, indexes_t *indexes_p )
{
...
			if ( sync_prequeue_loadmark ( 1, ctx_p, indexes_p, path_full, NULL, lstat_p, r.objtype_old, r.objtype_new, event->mask, event->wd, st_mode, st_size, &path_rel, &path_rel_len, NULL ) ) {
				count = -1;
				goto l_inotify_handle_end;
			}
...
}

sync_prequeue_loadmark calls sync_dosync

int sync_prequeue_loadmark
(
...
)
{
...
	switch ( ctx_p->flags[MODE] ) {
		case MODE_SIMPLE:
			if ( path_full == NULL ) {
				*path_buf_p   = sync_path_rel2abs ( ctx_p, path_rel,  -1, path_buf_len_p, *path_buf_p );
				path_full    = *path_buf_p;
			}
			return SAFE ( sync_dosync ( path_full, event_mask, ctx_p, indexes_p ), debug ( 1, "fpath == \"%s\"; evmask == 0x%o", path_full, event_mask ); return -1; );

		default:
			break;
	}
...
}

sync_dosync calls sync_dosync_exec

int sync_dosync ( const char *fpath, uint32_t evmask, ctx_t *ctx_p, indexes_t *indexes_p )
{
...
	ret = sync_dosync_exec ( ctx_p, indexes_p, evmask_str, fpath );
...
}

sync_dosync_exec calls SYNC_EXEC_ARGV.
SYNC_EXEC_ARGV will execute the handler (rsync for example) on the file/directory that has received an event. The path to the handler is put in argv[0] by sync_customargv
at the end of the function call chain, execvp will execute argv[0] which is set by sync_customargv

static inline int sync_dosync_exec ( ctx_t *ctx_p, indexes_t *indexes_p, const char *evmask_str, const char *fpath )
{
...
	char **argv = sync_customargv ( ctx_p, &dosync_arg, &ctx_p->synchandler_args[SHARGS_PRIMARY] );
	rc = SYNC_EXEC_ARGV (
	         ctx_p,
	         indexes_p,
	         NULL, NULL,
	         argv );
...
}

This will set argv[0] to ctx_p→handlerfpath.
It will be passed down to execvp as first argument through SYNC_EXEC_ARGV > sync_exec_argv > exec_argv > privileged_fork_execvp > __privileged_fork_execvp > execvp

static char **sync_customargv ( ctx_t *ctx_p, struct dosync_arg *dosync_arg_p, synchandler_args_t *args_p )
{
...
	s = d = 0;
	argv[d++] = strdup ( ctx_p->handlerfpath );
...
}

SYNC_EXEC_ARGV is set to sync_exec_argv in sync.c

...
#ifdef THREADING_SUPPORT
#define SYNC_EXEC_ARGV(...) (SHOULD_THREAD(ctx_p) ? sync_exec_argv_thread : sync_exec_argv)(__VA_ARGS__)
#else
#define SYNC_EXEC_ARGV(...) sync_exec_argv(__VA_ARGS__)
#endif
...

sync_exec_argv will keep invoking the handler (via exec_argv) until succeeding.

int sync_exec_argv ( ctx_t *ctx_p, indexes_t *indexes_p, thread_callbackfunct_t callback, thread_callbackfunct_arg_t *callback_arg_p, char **argv )
{
...
	do {
		try_again = 0;
		try_n++;
		debug ( 2, "try_n == %u (retries == %u)", try_n, ctx_p->retries );
		alarm ( ctx_p->synctimeout );
		ctx_p->children = 1;
		exitcode = exec_argv ( argv, ctx_p->child_pid );
		ctx_p->children = 0;
		alarm ( 0 );
        ...
	} while ( try_again );

...
}

exec_argv will simply call argv[0] as a privileged user in a separate thread

int exec_argv ( char **argv, int *child_pid )
{
...
	// Forking
	pid = privileged_fork_execvp ( argv[0], ( char *const * ) argv );
...
}

in privileged.h

#define privileged_fork_execvp			_privileged_fork_execvp

in privileged.c
__privileged_fork_execvp will simply set uid/gid then call execvp

int __privileged_fork_execvp ( const char *file, char *const argv[] )
{
...
				int rc;
				( void ) rc;	// anti-warning on ./configure --enable-debug=no
				rc = setgid ( __privileged_fork_execvp_gid );
				debug ( 4, "setgid(%u) == %i", __privileged_fork_execvp_gid, rc );
				rc = setuid ( __privileged_fork_execvp_uid );
				debug ( 4, "setuid(%u) == %i", __privileged_fork_execvp_uid, rc );
				errno = 0;
				execvp ( file, argv );
				exit ( errno );
...
}

execvp is a system call.
file was set to ctx_p→handlerfpath by sync_customargv


contact : @ychaouche yacinechaouche at yahoocom


QR Code
QR Code Understanding how rsync is called from the source code (generated for current page)