/* * Copyright (c) 1992, Brian Berliner and Jeff Polk * Copyright (c) 1989-1992, Brian Berliner * * You may distribute under the terms of the GNU General Public License * as specified in the README file that comes with the CVS 1.3 kit. */ #include "cvs.h" /* rcvs: */ #include "rcvs.h" #include "patchlevel.h" static char *current = NULL; static char *distfile; static char *distfile2; static FILE *distfp = NULL; static char *cp = NULL; static int sync_root_only = FALSE; #if __STDC__ int checkout (int argc, char **argv); int history (int argc, char **argv); int patch (int argc, char **argv); int rtag (int argc, char **argv); int rcvs_sync_add (int argc, char **argv); int rcvs_sync_commit (int argc, char **argv); int rcvs_sync_history (int argc, char **argv); int rcvs_sync_import (int argc, char **argv); int rcvs_sync_rdiff (int argc, char **argv); int rcvs_sync_remove (int argc, char **argv); int rcvs_sync_rtag (int argc, char **argv); #else int checkout (); int history (); int patch (); int rtag (); int rcvs_sync_add (); int rcvs_sync_commit (); int rcvs_sync_history (); int rcvs_sync_import (); int rcvs_sync_rdiff (); int rcvs_sync_remove (); int rcvs_sync_rtag (); #endif /* __STDC__ */ struct cmd { char *fullname; char *nick1; char *nick2; int (*func) (); }; static struct CMd { char *fullname; int (*func) (); int check_error; } CMds [] = { { "add", rcvs_sync_add, 0 }, { "checkout", checkout, 1 }, { "commit", rcvs_sync_commit, 1 }, { "history", rcvs_sync_history, 0 }, { "import", rcvs_sync_import, 0 }, { "rdiff", rcvs_sync_rdiff, 0 }, { "remove", rcvs_sync_remove, 0 }, { "rtag", rcvs_sync_rtag, 0 }, { NULL, NULL }, }; /* error message from rdist */ struct RMsg { char *keyword; /* error keywords */ char *suggestion; /* suggestion, fix */ int at_head; /* keywords is at the head of message*/ }; /* the order of this table is alphabetical, but if a message is a subset of another, make sure the longer message is before the shorter one, else the longer one will never be detected */ struct RMsg rmsg[] = { { "Connection timed out", "check host can rsh to destination, then try again", 0 }, { "Login incorrect.", "check user name in network address", 1}, { "No more processes.\".", "reduce # of processes, i.e. reduce #windows", 0}, { "No space left on device", "clean up disk space", 0}, { "Permission denied.", "check .rhosts file", 1}, { "Permission is denied.", "check .rhosts file", 0}, { "rcmd: socket: Permission denied", " 1. check rsh is working 2. check owner of rdist is root 2. check s bit of rdist", 0}, { "Permission denied", "check write access of destination", 0}, { "rdist: connection failed", "check host can rsh to destination, then try again", 1 }, { "rdist: lost connection", "check host can rsh to destination, then try again", 1 }, { "syntax error", "rdist problem", 0}, { "The file access permissions do not allow the specified action.", "1. check write access of destination 2. check s bit of rdist", 0}, { "The remote user login is not correct.", "check user name in net address", 0}, { "timed out", "check host can rsh to destination, then try again", 0 }, { "Too many processes already exist.\".", "reduce # of processes", 0}, { "Unknown host", "check host name", 0}, { "unknown host", "check host name", 0}, { NULL, " ", 0}, }; /* warning fuzzy message from rdist to be filtered out */ struct WMsg { char *keyword; /* warning keywords */ int at_head; /* keywords is at the head of message*/ }; struct WMsg wmsg[] = { { "Not owner", 0}, { "Operation not permitted.", 0}, { NULL, 0}, }; /* rcvs: sync from root to clone (sync forward) */ int rcvs_Sync (argc, argv, cm) int argc; char *argv[]; struct cmd *cm; { int err = 0; struct CMd *CM; extern char *version_rcvs; if (!quiet) fprintf (stderr, "(%s on server)\n", version_rcvs); /* turn off -n in phase 1 */ if (noexec && rcvs_sync) noexec = FALSE; /* change stdout and stderr to line-buffered mode */ (void) setlinebuf(stderr); (void) setlinebuf(stdout); for (CM = CMds; CM->fullname; CM++) if (!strcmp (cm->fullname, CM->fullname)) break; if (!CM->fullname) error (1, 0, "rcvs_Sync: bad command '%s'", cm->fullname); rcvs_sync_check_error = CM->check_error; err = (*(CM->func)) (argc, argv); if (trace) fprintf ( stderr, "-> rcvs_Sync: error from %s = %d\n", CM->fullname, err); /* if we go this far, this is a successful sync, * this is the only way to tell that cvs on server is working. * CVSROOT= is a signal that CVS sync operation is successful. */ if (!err) { fprintf(stderr,"CVSROOT=%s\n",CVSroot); if (ver_rcvs_client >=67) fprintf(stderr, "SERVER_VERSION=%d\n", ver_rcvs); } exit(err); } /* rcvs: Add directory to directory list */ int rcvs_Add_Dirlist ( mlist, prepath, realdir) List **mlist; char *prepath; char *realdir; { char repository[PATH_MAX]; char *cp; int err; if (*mlist == NULL) *mlist = getlist (); sprintf (repository, "%s/%s", prepath, realdir); cp = repository + strlen(CVSroot); *cp = '\0'; if (strcmp (repository, CVSroot) != 0) error (1, 0, "rcvs_Add_Dirlist: bug #1 %s\n", repository); else if (! isdir (CVSroot)) error (1, 0, "rcvs_Add_Dirlist: bug #2 %s does not exists\n", CVSroot); cp += 1; if (strncmp(cp, "/", 1) == 0) cp += 1; (void) addlist (mlist, cp); if (trace) fprintf(stderr, "-> directory: %s\n", cp); return (0); } /* rcvs: Add file to file list */ int rcvs_Add_filelist ( mlist, dir, filename ) List **mlist; char *dir; char *filename; { char repository[PATH_MAX]; char *cp; if (*mlist == NULL) *mlist = getlist (); if (rcvs_sync) (void) sprintf (repository, "%s/%s,v", dir, filename); else error(1, 0, "rcvs_Add_filelist: bug #1\n" ); if (strncmp (repository, CVSroot, strlen(CVSroot)) != 0) { error (0, 0, "rcvs_Add_filelist: bug #3, repository= %s", repository); error (1, 0, "repository does not match %s: %s", CVSROOT_ENV, CVSroot); } if (! isfile (repository)) /* file not exist */ { cp = repository+strlen(CVSroot)+1; if (rcvs_sync_forward) fprintf( stderr, "REMOVE=%s\n",cp); else (void) addlist ( &rcvs_rmlist, cp); if (trace) fprintf ( stderr, "scheduling to remove: %s\n", cp); } else { cp = repository+strlen(CVSroot)+1; (void) addlist ( mlist, cp); if (!quiet) fprintf(stderr, "file: %s\n", cp); } /* add Attic file also */ { (void) sprintf (repository, "%s/%s/%s,v", dir, CVSATTIC, filename); if (! isfile (repository)) { cp = repository+strlen(CVSroot)+1; if (rcvs_sync_forward) fprintf(stderr, "REMOVE=%s\n",cp); else (void) addlist ( &rcvs_rmlist, cp); if (trace) fprintf ( stderr, "scheduling to remove: %s\n", cp); } else { cp = repository+strlen(CVSroot)+1; (void) addlist ( mlist, cp); if (!quiet) fprintf(stderr, "file: %s\n", cp); } } return (0); } /* rcvs: Add directory to chmod directory list */ int rcvs_Add_chmodlist ( mlist, repository, update_dir, realdir ) List **mlist; char *repository; char *update_dir; /* NOT USED !! */ char *realdir; { char *cp; char dir[PATH_MAX]; int err; if (*mlist == NULL) *mlist = getlist (); /* derive relative path of directory */ if ( strcmp(realdir, ".") == 0) { if (strncmp (repository, CVSroot, strlen (CVSroot)) != 0) error (1, 0, "rcvs_Add_chmodlist: bug #1 %s does not match CVSROOT %s\n", repository, CVSroot); cp = repository + strlen(CVSroot) + 1; sprintf (dir, "%s", cp); } else if ( strcmp(update_dir, "") == 0) sprintf (dir, "%s", realdir); else { cp = repository + strlen(CVSroot) + 1; sprintf (dir, "%s/%s", cp, realdir); } /* add relative path of directory to list */ (void) addlist ( mlist, dir); if (!quiet) fprintf( stderr, "chmod dir: %s\n", dir); return(0); } /* rcvs: cleanup temporary directory */ int rcvs_Cleanup() { char tmp[PATH_MAX]; (void) sprintf (tmp, "rm -r -f %s", CVSADM); (void) system (tmp); return (0); } /* rcvs: get Group/Access attribute of directory */ int rcvs_GetAttr (p) Node *p; { List *dirlist = NULL; char *dir; char *olddir; char repository[PATH_MAX]; olddir = current; sprintf (repository, "%s/%s", current, p->key); /* if not directory, simply return */ if (!isdir(repository)) error (0, 0, "rcvs_GetAttr: #1 %s is not a directory\n", repository); dir = repository + strlen(CVSroot) + 1; if (trace) fprintf(stderr, "-> rcvs_GetAttr: dir=%s\n", dir); dirlist=Find_Dirs ( repository, W_REPOS); current = repository; if ( strcmp(p->key, ".") == 0 || !walklist (dirlist, rcvs_GetAttr)) { struct stat sbuf; struct group *Gid; if ( stat(current, &sbuf) == 0) Gid = getgrgid(sbuf.st_gid); if (Gid != NULL) fprintf(stderr, "GROUP/MODE=%s %s %o \n", dir, Gid->gr_name, sbuf.st_mode); } current = olddir; return (0); } /* rcvs: Print key of one node from module list into distfile */ int rcvs_Print_Mod (p) Node *p; { fprintf(distfp,"%s ",p->key); return (0); } /* rcvs: create input file (distfile) for rdist */ int rcvs_Rdist_init () { char tmp[PATH_MAX]; int err; /* rcvs: use /tmp/RCVS as working directory */ cp = RCVSTMP; if ( !isdir(cp) ) { if ( mkdir (cp, 0777) ) error (1, 0, "sorry, not enough access to %s\n", cp); else if ( chmod (cp, 0777)) error (1, 0, "sorry, cannot chmod 777 for %s\n", cp); } if ( (chdir (cp) < 0)) error (1, 0, "cannot chdir to %s", cp); /* rcvs: create a dummy working directory in /tmp/RCVS for user */ (void) sprintf( tmp,"%s/%s.%d", RCVSTMP, RCVSuser, getpid ()); cp = rcvs_tmpname = xstrdup(tmp); if ( !isdir(cp) ) if ( mkdir (cp, 0777) ) error (1, 0, "sorry, not enough access to %s",RCVSTMP); if ( (chdir (cp) < 0)) error (1, 0 , "cannot chdir to %s", cp); /* rcvs: open distfile */ (void) sprintf ( tmp,"%s/distfile", rcvs_tmpname); distfile = xstrdup (tmp); (void) sprintf ( tmp,"%s/distfile.%s", RCVSTMP, RCVSuser); distfile2 = xstrdup (tmp); distfp = open_file (distfile, "w+"); if (distfp == NULL) err = 1; else err = 0; return (err); } /* examine output from rsh. */ int rcvs_check_rsh_output(line) char * line; { int err = 0; struct RMsg *Rm; int size; size = strlen(line); /* check rsh error messages */ for (Rm = rmsg; Rm->keyword; Rm++) { int ksize; ksize = strlen(Rm->keyword); if (Rm->at_head) { if (!strncmp (Rm->keyword, line, strlen(Rm->keyword))) { err = 1 ; break; } } else { if (0 && trace && size > ksize) /* set 1 to debug */ fprintf(stderr, "-> rcvs_check_rdist_output: %s", line+size-ksize-1); if (size > ksize && !strncmp (Rm->keyword, line+size-ksize-1, ksize)) { err = 1 ; break; } } } if (err) fprintf (stderr, "suggestion: <<%s>>\n", Rm->suggestion); return (err); } /* examine stderr for rdist. * Why check error for rdist? Because USC's rdist does not return error code * properly. * return code: 1=error 2=fuzzy warning (can be filtered) */ int rcvs_check_rdist_output(line) char * line; { int err = 0; struct RMsg *Rm; struct WMsg *Wm; int size; size = strlen(line); /* check rsh & rdist error messages */ for (Rm = rmsg; Rm->keyword; Rm++) { int ksize; ksize = strlen(Rm->keyword); if (Rm->at_head) { if (!strncmp (Rm->keyword, line, strlen(Rm->keyword))) { err = 1 ; break; } } else { if (0 && trace && size > ksize) /* set 1 to debug */ fprintf(stderr, "-> rcvs_check_rdist_output: %s", line+size-ksize-1); if (size > ksize && !strncmp (Rm->keyword, line+size-ksize-1, ksize)) { err = 1 ; break; } } } if (err) fprintf (stderr, "| suggestion: <<%s>>\n", Rm->suggestion); /* filter out excessive rdist fuzzy warm messages */ for (Wm = wmsg; Wm->keyword; Wm++) { int ksize; ksize = strlen(Wm->keyword); if (Wm->at_head) { if (!strncmp (Wm->keyword, line, strlen(Wm->keyword))) { err = 2; break; } } else { if (0 && trace && size > ksize) /* set 1 to debug */ fprintf(stderr, "-> rcvs_check_rdist_output: %s", line+size-ksize-1); if (size > ksize && !strncmp (Wm->keyword, line+size-ksize-1, ksize)) { err = 2; break; } } } return (err); } /* rcvs: run rdist to send modules over */ int rcvs_Rdist() { FILE *fp; FILE *fp_err; char line[PATH_MAX]; char tmp[PATH_MAX]; char *cvsrec; int err = 0; int err2 = 0; int Err = 0; register int i; fprintf(distfp,"SRC = ("); if (!sync_root_only) { /*if ( (rcvs_dirlist == NULL && rcvs_filelist == NULL) && !quiet) error (0, 0, "both dirlist and filelist are empty");*/ if (rcvs_dirlist != NULL) { if (trace) rcvs_dump_list ("#dirlist: ", rcvs_dirlist ); (void) walklist (rcvs_dirlist, rcvs_Print_Mod ); } if (rcvs_filelist != NULL) { if (trace) rcvs_dump_list ("#filelist: ", rcvs_filelist ); (void) walklist (rcvs_filelist, rcvs_Print_Mod ); } } fprintf(distfp,"CVSROOT)\n"); if (rcvs_cvsrec == NULL) /* rdist receiver */ cvsrec = RCVSuser; else cvsrec = rcvs_cvsrec; fprintf (distfp,"HOST = (%s@%s)\n", cvsrec, RCVShost); /* if client is eariler than cvs-0.6.7, send commitlog over */ if (rcvs_sync_forward && ver_rcvs_client < 67) fprintf (distfp,"EXCEPT = (CVSROOT/%s )\n", CVSROOTADM_HISTORY); else { /* in phase 3, always send history.tmp and commitlog.tmp back */ if (rcvs_sync_backward) fprintf (distfp,"EXCEPT = (CVSROOT/%s CVSROOT/%s)\n", CVSROOTADM_HISTORY, CVSROOTADM_COMMITLOG); /* in phase 2, send neither history nor commitlog over */ else fprintf (distfp,"EXCEPT = (CVSROOT/%s CVSROOT/%s CVSROOT/%s CVSROOT/%s)\n", CVSROOTADM_HISTORY, CVSROOTADM_COMMITLOG, CVSROOTADM_HISTORY_NEW, CVSROOTADM_COMMITLOG_NEW); } if (trace) { if (rcvs_sync_forward && ver_rcvs_client < 67) fprintf (stderr, "-> old rcvs client, send commitlog to client\n"); else if (rcvs_sync_backward) fprintf (stderr, "-> always send commitlog.tmp to server\n"); } fprintf(distfp,"${SRC} -> ${HOST}\n"); fprintf(distfp,"install "); /*+IS*/ #ifdef USCrdist fprintf(distfp,"-oremove,whole %s;\n", RCVSroot); #else /*-IS*/ fprintf(distfp,"-R "); fprintf(distfp,"-w %s;\n", RCVSroot); /*+IS*/ #endif /*-IS*/ fprintf(distfp,"except ${EXCEPT};\n"); fprintf(distfp,"except_pat (CVSROOT/\\%s\\*);\n", RCVSLOCK); (void) fclose (distfp); copy_file (distfile, distfile2); if (trace) fprintf(stderr, "rdist %s to %s (at %s)\n", CVSroot, RCVSroot, RCVShost); if ( (chdir (CVSroot) < 0)) error (1, errno, "cannot chdir to %s",CVSroot); (void) sprintf (tmp,"rdist"); if (noexec) (void) strcat (tmp, " -n"); if (quiet) (void) strcat (tmp, " -q"); (void) sprintf (tmp+strlen(tmp)," -f %s", distfile); (void) strcat (tmp, " 2>&1"); /* put all rdist output in stdout */ fprintf (stderr, "rdist modules to %s@%s\n", cvsrec, RCVShost); if (trace) fprintf (stderr, "-> %s\n", tmp); { fp = popen (tmp, "r"); /* since some version of rdist send output into stdout instead of * stderr, we have to check error in both */ while (fgets (line, sizeof (line), fp)) { Err = rcvs_check_rdist_output (line); if (Err == 1) { err2 = 1; /* record error within a loop */ fprintf (stderr, "| %s", line); break; } if (trace || Err != 2) /* not a fuzzy warning message */ fprintf (stderr, "| %s", line); } err = pclose (fp); if (trace) fprintf (stderr, "-> rcvs_Rdist: err from rdist msg=%d\n", err2); } if (trace) fprintf (stderr, "-> rcvs_Rdist: err from rdist=%d\n", err); if (err2) err = 1; if (rcvs_sync_forward && err) fprintf(stderr, "RDIST_STATUS=1\n"); /* send back CVSROOT= */ if (!err && rcvs_sync_forward) { /* collect group/mode attribute list */ current = CVSroot; if ( ! sync_root_only) addlist (&rcvs_chmodlist, "."); addlist (&rcvs_chmodlist, CVSROOTADM); walklist (rcvs_chmodlist, rcvs_GetAttr); } /* cleanup temporary file in /tmp/RCVS */ if (rcvs_tmpname != NULL) { sprintf (tmp,"rm -r -f %s", rcvs_tmpname); (void) system (tmp); rcvs_tmpname = NULL; } return (err); } /* do synchorization for add */ int rcvs_sync_add (argc, argv) int argc; char *argv[]; { int err = 0; if (!quiet) fprintf (stderr, "check if it's in cvsroot already, ok if not\n"); err = (*checkout) (argc, argv); /* signal ok if file not found under file mode */ if (err) fprintf(stderr, "CVSROOT=%s\n",CVSroot); return (err); } /* do synchorization for commit */ int rcvs_sync_commit (argc, argv) int argc; char *argv[]; { int err = 0; if (!quiet) fprintf (stderr, "check if it's in cvsroot already, ok if not\n"); err = (*checkout) (argc, argv); /* signal ok if file not found under file mode */ if (err) fprintf( stderr, "CVSROOT=%s\n",CVSroot); return (err); } /* do history command on server and send result back */ int rcvs_sync_history (argc, argv) int argc; char *argv[]; { int err = 0; rcvs_do_rdist = FALSE; err = (*history) (argc, argv); return (err); } /* do synchronization for import */ int rcvs_sync_import (argc, argv) int argc; char *argv[]; { int err = 0; if (!quiet) fprintf (stderr, "check if it's in cvsroot already, ok if not\n"); err = (*checkout) (argc, argv); return (err); } /* do rdiff command on server directly */ int rcvs_sync_rdiff (argc, argv) int argc; char *argv[]; { int err = 0; rcvs_do_rdist = FALSE; err = (*patch) (argc, argv); return (err); } /* do synchorization for remove */ int rcvs_sync_remove (argc, argv) int argc; char *argv[]; { int err = 0; if (!quiet) fprintf (stderr, "check if it's in cvsroot already, ok if not\n"); err = (*checkout) (argc, argv); /* signal ok if file not found under file mode */ if (err) fprintf( stderr, "CVSROOT=%s\n",CVSroot); return (err); } /* do rtag command on server directly */ int rcvs_sync_rtag (argc, argv) int argc; char *argv[]; { int err = 0; rcvs_do_rdist = FALSE; err = (*rtag) (argc, argv); return (err); } /*+IS*/ #if defined(__hpux) || defined(__solaris) /* * setlinebuf (FILE *fp) * * Routine to set line buffering on "fp". */ /* From: system@alchemy.chem.utoronto.ca (System Admin (Mike Peterson)) hp tricks */ int setlinebuf (FILE *fp) { (void) setvbuf (fp, NULL, _IOLBF, 0); return(0); } #endif /* __hpux || __solaris */ /*-IS*/