David S. Ahern
2003-Jun-07 06:21 UTC
patch to rsync to add options for pre- and post-transfer commands
In case others find this of value, I wrote a patch to rsync 2.5.6 to give rsync in --daemon mode the ability to run a pre-transfer and post-transfer command. These options handle our need to prepare a server to receive files and to do some processing after receiving files. The options for /etc/rsyncd.conf are pretransfer script = /some/command/to/run posttransfer script = /some/other/command The commands are exec'd (not run through system() or popen()) and the inputs are the list of files (and directories) that are being synched (from struct file_list *flist). If the pretransfer script fails the synch is stopped. -- david ahern -------------- next part -------------- Only in rsync-2.5.6: config.h Only in rsync-2.5.6: config.log Only in rsync-2.5.6: config.status Common subdirectories: rsync-2.5.6.orig/doc and rsync-2.5.6/doc diff --exclude='*.o' -b --context rsync-2.5.6.orig/errcode.h rsync-2.5.6/errcode.h *** rsync-2.5.6.orig/errcode.h 2002-04-08 23:29:26.000000000 -0600 --- rsync-2.5.6/errcode.h 2003-04-28 20:32:40.000000000 -0600 *************** *** 35,40 **** --- 35,44 ---- #define RERR_MESSAGEIO 13 /* errors with program diagnostics */ #define RERR_IPC 14 /* error in IPC code */ + #define RERR_PRESCRIPT 17 /* error running pre-transfer script */ + #define RERR_POSTSCRIPT 18 /* error running post-transfer script */ + #define RERR_SCRIPT 19 /* system error running transfer script */ + #define RERR_SIGNAL 20 /* status returned when sent SIGUSR1, SIGINT */ #define RERR_WAITCHILD 21 /* some error returned by waitpid() */ #define RERR_MALLOC 22 /* error allocating core memory buffers */ Common subdirectories: rsync-2.5.6.orig/lib and rsync-2.5.6/lib diff --exclude='*.o' -b --context rsync-2.5.6.orig/loadparm.c rsync-2.5.6/loadparm.c *** rsync-2.5.6.orig/loadparm.c 2002-08-30 17:27:26.000000000 -0600 --- rsync-2.5.6/loadparm.c 2003-04-27 20:25:42.000000000 -0600 *************** *** 140,145 **** --- 140,147 ---- int timeout; int max_connections; BOOL ignore_nonreadable; + char *prescript; + char *postscript; } service; *************** *** 180,186 **** "*.gz *.tgz *.zip *.z *.rpm *.deb *.iso *.bz2 *.tbz", /* dont compress */ 0, /* timeout */ 0, /* max connections */ ! False /* ignore nonreadable */ }; --- 182,191 ---- "*.gz *.tgz *.zip *.z *.rpm *.deb *.iso *.bz2 *.tbz", /* dont compress */ 0, /* timeout */ 0, /* max connections */ ! False, /* ignore nonreadable */ ! ! NULL, /* pre-transfer script */ ! NULL, /* post-transfer script */ }; *************** *** 295,300 **** --- 300,307 ---- {"log format", P_STRING, P_LOCAL, &sDefault.log_format, NULL, 0}, {"refuse options", P_STRING, P_LOCAL, &sDefault.refuse_options,NULL, 0}, {"dont compress", P_STRING, P_LOCAL, &sDefault.dont_compress,NULL, 0}, + {"pretransfer script", P_STRING, P_LOCAL, &sDefault.prescript,NULL, 0}, + {"posttransfer script", P_STRING, P_LOCAL, &sDefault.postscript,NULL, 0}, {NULL, P_BOOL, P_NONE, NULL, NULL, 0} }; *************** *** 374,379 **** --- 381,388 ---- FN_LOCAL_STRING(lp_dont_compress, dont_compress) FN_LOCAL_INTEGER(lp_timeout, timeout) FN_LOCAL_INTEGER(lp_max_connections, max_connections) + FN_LOCAL_STRING(lp_prescript, prescript) + FN_LOCAL_STRING(lp_postscript, postscript) /* local prototypes */ static int strwicmp( char *psz1, char *psz2 ); diff --exclude='*.o' -b --context rsync-2.5.6.orig/log.c rsync-2.5.6/log.c *** rsync-2.5.6.orig/log.c 2002-12-24 00:42:04.000000000 -0700 --- rsync-2.5.6/log.c 2003-04-28 09:32:31.000000000 -0600 *************** *** 49,54 **** --- 49,56 ---- { RERR_STREAMIO , "error in rsync protocol data stream" }, { RERR_MESSAGEIO , "errors with program diagnostics" }, { RERR_IPC , "error in IPC code" }, + { RERR_PRESCRIPT , "error running pre-transfer script" }, + { RERR_POSTSCRIPT , "error running post-transfer script" }, { RERR_SIGNAL , "received SIGUSR1 or SIGINT" }, { RERR_WAITCHILD , "some error returned by waitpid()" }, { RERR_MALLOC , "error allocating core memory buffers" }, diff --exclude='*.o' -b --context rsync-2.5.6.orig/main.c rsync-2.5.6/main.c *** rsync-2.5.6.orig/main.c 2003-01-27 22:05:53.000000000 -0700 --- rsync-2.5.6/main.c 2003-05-19 14:18:32.000000000 -0600 *************** *** 69,75 **** * message describing the purpose of the child. Also indicate * this to the caller so that thhey know something went * wrong. */ ! *status = WEXITSTATUS(*status); } static void report(int f) --- 69,75 ---- * message describing the purpose of the child. Also indicate * this to the caller so that thhey know something went * wrong. */ ! if (WIFEXITED(*status) != 0) *status = WEXITSTATUS(*status); } static void report(int f) *************** *** 384,389 **** --- 384,486 ---- } + static int run_xfer_script(const char *script, const struct file_list *flist, const char *name) + { + int status; + pid_t cmd; + + cmd = fork(); + switch (cmd) { + case 0: /* child */ + { + char **pargv; + char *fname; + int i, j; + + pargv = (char **) malloc((flist->count + 2)*sizeof(char *)); + if (pargv == NULL) { + rprintf(FERROR,"ERROR: malloc for argv failed: %s\n", + strerror(errno)); + return(RERR_MALLOC); + } + + /* first arg is basename of command to be run */ + pargv[0] = basename(script); + + /* add file list */ + j = 1; + for (i = 0; i < flist->count; ++i) + { + /* get full path to file; need to make a copy + * because f_name uses a static buffer + */ + fname = f_name(flist->files[i]); + if (fname == NULL) continue; + + pargv[j] = strdup(fname); + if (pargv[j] == NULL) { + rprintf(FERROR,"ERROR: strdup of file name %s failed: %s\n", + fname, strerror(errno)); + return(RERR_MALLOC); + } + j++; + } + pargv[j] = NULL; + + execv(script, pargv); + rprintf(FERROR,"ERROR: exec on %s script failed: %s\n", + name, strerror(errno)); + exit(RERR_SCRIPT); + break; + } + + case -1: + rprintf(FERROR,"ERROR: fork for %s script failed: %s\n", + name, strerror(errno)); + return(RERR_SCRIPT); + break; + + default: + status = -1; + wait_process(cmd, &status); + if ( (verbose > 1) || (status != 0) ) { + rprintf(FINFO,"%s script exited with %d status\n", + name, status); + } + } + + return(status); + } + + + static int run_prescript(const struct file_list *flist) + { + extern int module_id; + const char *prescript = lp_prescript(module_id); + + if ( (prescript == NULL) || (*prescript == '\0') ) return(0); + if (verbose > 1) { + rprintf(FINFO,"running pre-transfer script: %s\n", prescript); + } + + return(run_xfer_script(prescript, flist, "pre-transfer")); + } + + + static int run_postscript(const struct file_list *flist) + { + extern int module_id; + const char *postscript = lp_postscript(module_id); + + if ( (postscript == NULL) || (*postscript == '\0') ) return(0); + if (verbose > 1) { + rprintf(FINFO,"running post-transfer script: %s\n", postscript); + } + + return(run_xfer_script(postscript, flist, "post-transfer")); + } + + static int do_recv(int f_in,int f_out,struct file_list *flist,char *local_name) { int pid; *************** *** 416,421 **** --- 513,522 ---- exit_cleanup(RERR_SOCKETIO); } + if (run_prescript(flist) != 0) { + exit_cleanup(RERR_PRESCRIPT); + } + io_flush(); if ((pid=do_fork()) == 0) { *************** *** 464,469 **** --- 565,575 ---- io_set_error_fd(-1); kill(pid, SIGUSR2); wait_process(pid, &status); + + if (run_postscript(flist) != 0) { + exit_cleanup(RERR_POSTSCRIPT); + } + return status; } Only in rsync-2.5.6: main.c.ver1 Only in rsync-2.5.6: Makefile Common subdirectories: rsync-2.5.6.orig/packaging and rsync-2.5.6/packaging Common subdirectories: rsync-2.5.6.orig/patches and rsync-2.5.6/patches Common subdirectories: rsync-2.5.6.orig/popt and rsync-2.5.6/popt diff --exclude='*.o' -b --context rsync-2.5.6.orig/proto.h rsync-2.5.6/proto.h *** rsync-2.5.6.orig/proto.h 2003-01-26 20:35:09.000000000 -0700 --- rsync-2.5.6/proto.h 2003-04-27 11:34:53.000000000 -0600 *************** *** 151,156 **** --- 151,158 ---- BOOL lp_load(char *pszFname, int globals_only); int lp_numservices(void); int lp_number(char *name); + char *lp_prescript(int ); + char *lp_postscript(int ); void err_list_push(void); void log_init(void); void log_open(void); Only in rsync-2.5.6: rsync Only in rsync-2.5.6: rsync_script.patch Only in rsync-2.5.6: shconfig diff --exclude='*.o' -b --context rsync-2.5.6.orig/socket.c rsync-2.5.6/socket.c *** rsync-2.5.6.orig/socket.c 2003-01-26 20:35:09.000000000 -0700 --- rsync-2.5.6/socket.c 2003-05-19 14:22:41.000000000 -0600 *************** *** 419,426 **** if (fd == -1) continue; signal(SIGCHLD, SIG_IGN); ! /* we shouldn't have any children left hanging around but I have had reports that on Digital Unix zombies are produced, so this ensures that they are reaped */ --- 419,432 ---- if (fd == -1) continue; + #if 0 + /* This resets the signal handler set in main.c! + * which means after the first accept, wait_process + * does not work as intended. I get the impression + * this was a haphazard "fix", so comment it out. + */ signal(SIGCHLD, SIG_IGN); ! #endif /* we shouldn't have any children left hanging around but I have had reports that on Digital Unix zombies are produced, so this ensures that they are reaped */ Only in rsync-2.5.6: tags Common subdirectories: rsync-2.5.6.orig/testhelp and rsync-2.5.6/testhelp Common subdirectories: rsync-2.5.6.orig/testsuite and rsync-2.5.6/testsuite Common subdirectories: rsync-2.5.6.orig/zlib and rsync-2.5.6/zlib
jw schultz
2003-Jun-07 08:28 UTC
patch to rsync to add options for pre- and post-transfer commands
On Fri, Jun 06, 2003 at 02:21:20PM -0600, David S. Ahern wrote:> In case others find this of value, I wrote a patch to rsync 2.5.6 to > give rsync in --daemon mode the ability to run a pre-transfer and > post-transfer command. These options handle our need to prepare a server > to receive files and to do some processing after receiving files. > > The options for /etc/rsyncd.conf are > pretransfer script = /some/command/to/run > posttransfer script = /some/other/command > > The commands are exec'd (not run through system() or popen()) and the > inputs are the list of files (and directories) that are being synched > (from struct file_list *flist). > > If the pretransfer script fails the synch is stopped.Firstly, if doing this you need to cope with arguments for the script. And if passing the file list do so on stdin; What, pray tell, are you going to do if the file list is 286,000 entries? Where is stdout and stderr of the pre and post "scripts" going. Don't assume that the commands will be scripts unless you plan on passing them as arguments to /bin/sh. Finally, I don't like it. Rsync is not a swiss army knife. This isn't a feature, it is a function. If you need pre and post commands run a script on the client or create a wrapper daemon that does the pre action, runs a one-shot rsync daemon and then does the post action. -- ________________________________________________________________ J.W. Schultz Pegasystems Technologies email address: jw@pegasys.ws Remember Cernan and Schmitt