/* * 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. */ /* RCVS lock mechanism: There are four type of RCVS locks: 1. 'WFL' lock is set by write-type commands (add(directory), admin, ci, import, tag) issued by remote CVS that lock against any write-type command, this lock protects the integrity of clone and main repository. This lock should also lock against read-type commands (add(file) diff export log remove rtag status update) issued by remote CVS to protect the integrity of the clone. 2. 'wfl' lock is set by write-type commands issued by local CVS that lock against any write-type commands issue by remote CVS, this lock protects the integrity of main repository. This lock does not lock against write-type commands, since it is handled by local CVS itself. (thus more than one local CVS users can do commit different modules in the same time). 3. 'RFL' lock is set by read-type commands issued by remote CVS that lock against write-type commands issued by remote CVS, this lock protects the integrity of the clone. This lock does not lock against read-type commands, since they are all just copying files from main repository to clone. (thus more than one remote users can do update in the same time, occasionally the read-lock set by local CVS in phase-2 may cause rdist error, but this is a minor price we can afford). 4. 'rfl' lock is set by read-type commands issued by local CVS that lock against write-type commands issued by remote CVS, this lock protects the integrity of the main repository. This lock does not lock against read-type commands issued by local CVS. */ #include "cvs.h" /* rcvs: */ #include "rcvs.h" #include "patchlevel.h" static char *rshtmp = NULL; static char lockfile[PATH_MAX]; static int size_rmlist = 0; static int size_dirlist = 0; static char *lock_msg[] = { "local", "remote" }; /* rcvs: lock errors */ int rcvs_lock_error (lockfile, msg) char *lockfile; char *msg; { FILE *lockfp; char line[PATH_MAX]; lockfp = open_file (lockfile, "r"); (void) fgets (line, sizeof(line), lockfp); if (fclose (lockfp) == EOF) error (1, 0, "rcvs_lock_error: cannot close %s", lockfile); if (trace) fprintf(stderr, "-> rcvs_lock_error %s\n", msg); error (1, 0, "lock [%s] is set by %s\n consider using -U option (or try later if lock is recent)", lockfile, line ); return (0); } /* rcvs: check locks */ int rcvs_Check_lock () { if (noexec) return(0); if (rcvs_native_CVS) /* local CVS */ { if (rcvs_Uopt_unlock) rcvs_Clearlock_all (); if (index(rcvs_CM->phases,'3')) /* write type commands */ { if ( rcvs_lock_exist("WFL") && !rcvs_Uopt_unlock) (void) rcvs_lock_error (lockfile, "#1/1"); } else /* read type commands */ if ( rcvs_lock_exist("WFL") && !rcvs_Uopt_unlock) (void) rcvs_lock_error (lockfile, "#1/2"); if (rcvs_Lopt_lock || index(rcvs_CM->phases,'3')) { if ( rcvs_lock_exist("WFL")) (void) rcvs_lock_error (lockfile, "#1/3"); (void) rcvs_Setlock ("wfl"); } } else if (rcvs_native_RCVS) /* remote CVS, client's side */ { if (rcvs_CM == NULL) error (1, 0, "rcvs_Check_lock: bug#1, rcvs_CM = NULL"); if (rcvs_Uopt_unlock) rcvs_Clearlock_all (); if (index(rcvs_Popt_phases,'3')) /* set write lock */ { if ( rcvs_lock_exist("WFL") && !rcvs_Uopt_unlock) (void) rcvs_lock_error (lockfile, "#2/1"); if ( rcvs_lock_exist("RFL") && !rcvs_Uopt_unlock) (void) rcvs_lock_error (lockfile, "#2/2"); (void) rcvs_Setlock ("WFL"); if (!index(rcvs_Popt_phases,'1')) /* no phase 1 */ (void) rcvs_set_phase3_lock (); } else if (index(rcvs_Popt_phases,'8')) /* set read lock */ { if ( rcvs_lock_exist("WFL") && !rcvs_Uopt_unlock) (void) rcvs_lock_error (lockfile, "#2/3"); (void) rcvs_Setlock ("RFL"); } } else if (rcvs_sync_forward || rcvs_set_p3_lock) /* remote CVS, server*/ { if (rcvs_Uopt_unlock) rcvs_Clearlock_all (); if (rcvs_Lopt_lock) { if (!rcvs_Uopt_unlock) { if ( rcvs_lock_exist("WFL") && !rcvs_Uopt_unlock) (void) rcvs_lock_error (lockfile, "#3/1"); if ( rcvs_lock_exist("wfl") && !rcvs_Uopt_unlock) (void) rcvs_lock_error (lockfile, "#3/2"); if ( rcvs_lock_exist("rfl") && !rcvs_Uopt_unlock) (void) rcvs_lock_error (lockfile, "#3/3"); } (void) rcvs_Setlock ("WFL"); if (rcvs_sync_forward) fprintf(stderr, "CVSroot=%s\nRCVSLOCK=1\n", CVSroot); } } return(0); } /* cleanup lock */ int rcvs_lock_exist (type) char *type; { DIR *dirp; struct direct *dp; CONST char *regex_err; char tmp[PATH_MAX]; char cvsrootadm[PATH_MAX]; int found=0; sprintf (cvsrootadm, "%s/%s", CVSroot, CVSROOTADM); if ((dirp = opendir (cvsrootadm)) == NULL) error (1, 0, "cannot open directory %s", cvsrootadm); if (strcmp(type,"WFL") == 0) (void) sprintf (tmp, "^%s$", RCVSLOCK_WFL); else if (strcmp(type,"RFL") == 0) (void) sprintf (tmp, "^%s\\..*", RCVSLOCK_RFL); else if (strcmp(type,"wfl") == 0) (void) sprintf (tmp, "^%s\\..*", RCVSLOCK_wfl); else if (strcmp(type,"rfl") == 0) (void) sprintf (tmp, "^%s\\..*", RCVSLOCK_rfl); else error (1, 0, "rcvs_lock_exist: bug #1"); if ((regex_err = re_comp (tmp)) != NULL) error (1, 0, "%s", regex_err); while ((dp = readdir (dirp)) != NULL) { (void) sprintf (tmp, "%s/%s", cvsrootadm, dp->d_name); if (re_exec (dp->d_name)) /* found a match, lock exist */ { found=1; (void) sprintf (lockfile, "%s/%s", cvsrootadm, dp->d_name); break; } } (void) closedir (dirp); return (found); } /* cleanup lock */ int rcvs_Clearlock_all() { char tmp[PATH_MAX]; int ret = 0; (void) sprintf (tmp,"cd %s/%s; rm -f \\%s* \\%s* \\%s* \\%s*", CVSroot, CVSROOTADM, RCVSLOCK_WFL, RCVSLOCK_RFL, RCVSLOCK_wfl, RCVSLOCK_rfl); ret = system ( tmp ); if (trace) fprintf(stderr, "rcvs_Clearlock_all: %s\n", tmp); return (ret); } /* cleanup my lock */ int rcvs_Clear_my_lock() { if (trace) fprintf(stderr, "-> rcvs_Clear_my_lock: \n"); if (rcvs_lock_WFL_remote || rcvs_lock_WFL_local) (void) rcvs_Clearlock_WFL (); if (rcvs_lock_RFL) (void) rcvs_Clearlock ("RFL"); if (rcvs_lock_wfl) (void) rcvs_Clearlock ("wfl"); if (rcvs_lock_rfl) (void) rcvs_Clearlock ("rfl"); /* cleanup temporary file in /tmp/RCVS */ if (rcvs_tmpname != NULL) { char tmp[PATH_MAX]; sprintf (tmp,"cd /tmp; rm -r -f %s", rcvs_tmpname); (void) system (tmp); rcvs_tmpname = NULL; } } /* cleanup WFL lock */ int rcvs_Clearlock_WFL() { char tmp[PATH_MAX]; char lockfile[PATH_MAX]; if (trace) fprintf(stderr, "-> rcvs_Clearlock_WFL:\n"); /* rcvs: clear local lock */ (void) sprintf( lockfile,"%s/CVSROOT/%s",CVSroot,RCVSLOCK_WFL); if ( ( rcvs_lock_WFL_local || rcvs_Uopt_unlock) && isreadable (lockfile)) { if ( unlink_file (lockfile) ) error (1, 0, "cannot remove lockfile %s\n", lockfile); rcvs_lock_WFL_local = FALSE; if (trace) fprintf(stderr, "-> remove local RCVS lock %s\n", lockfile); } /* rcvs: clear remote lock */ if ( (rcvs_lock_WFL_remote || (rcvs_Uopt_unlock && rcvs_native_RCVS)) && RCVSroot != NULL ) { int err = FALSE; (void) sprintf( lockfile,"%s/CVSROOT/%s",RCVSroot,RCVSLOCK); if ( RCVSuser == NULL ) RCVSuser = rcvs_username; /*+IS*/ (void) sprintf (tmp,"echo \'rm %s\' | %s %s -l %s sh -s", lockfile, rshcmd, RCVShost, RCVSuser ); /*-IS*/ if (trace) fprintf(stderr, "-> %s\n", tmp); else fprintf(stderr, "Remove lock from server\n"); err = system ( tmp ); if (err) fprintf(stderr, "failed to remove remote lock \n"); else rcvs_lock_WFL_remote = FALSE; } } /* cleanup lock RFL, wfl, rfl */ int rcvs_Clearlock(type) char *type; { char tmp[PATH_MAX]; char lockfile[PATH_MAX]; FILE *lockfp; if (trace) fprintf(stderr, "-> rcvs_Clearlock: %s\n", type); if (strcmp(type,"RFL") == 0) (void) sprintf( lockfile,"%s/CVSROOT/%s.%d",CVSroot,RCVSLOCK_RFL, getpid ()); else if (strcmp(type,"wfl") == 0) (void) sprintf( lockfile,"%s/CVSROOT/%s.%d",CVSroot,RCVSLOCK_wfl, getpid ()); else if (strcmp(type,"rfl") == 0) (void) sprintf( lockfile,"%s/CVSROOT/%s.%d",CVSroot,RCVSLOCK_rfl, getpid ()); else error (1, 0, "rcvs_Clearlock: bug #1"); if ( isreadable (lockfile)) { if ( unlink_file (lockfile) ) error (1, 0, "cannot remove lockfile %s\n", lockfile); if (trace) fprintf(stderr, "-> remove lock %s\n", lockfile); if (strcmp(type,"RFL") == 0) rcvs_lock_RFL = FALSE; else if (strcmp(type,"wfl") == 0) rcvs_lock_wfl = FALSE; else rcvs_lock_rfl = FALSE; } else error (0, 0, "lock file %s not found\n", lockfile); } /* rcvs: set RCVS lock in $CVSroot/CVSROOT * CVSroot could be either CVSROOT or RCVSDIR */ int rcvs_Setlock (type) char *type; { char host[PATH_MAX]; char line[PATH_MAX]; FILE *fp; FILE *lockfp; char *cp; char *msg; int err = 0; { if (rcvs_sync_forward) (void) strcpy (host, RCVShost); else (void) gethostname ( host, sizeof(host) ); cp = index( host, '.'); if ( cp != NULL) *cp = '\0'; } fp = popen ("date", "r"); while (fgets (line, sizeof (line), fp)) ; (void) pclose (fp); if (strcmp(type,"WFL") == 0) { (void) sprintf( lockfile,"%s/CVSROOT/%s",CVSroot,RCVSLOCK_WFL); msg = lock_msg[1]; } else if (strcmp(type,"wfl") == 0) { (void) sprintf( lockfile,"%s/CVSROOT/%s.%d",CVSroot,RCVSLOCK_wfl, getpid ()); msg = lock_msg[0]; } else if (strcmp(type,"RFL") == 0) { (void) sprintf( lockfile,"%s/CVSROOT/%s.%d",CVSroot,RCVSLOCK_RFL, getpid ()); msg = lock_msg[1]; } else if (strcmp(type,"rfl") == 0) { (void) sprintf( lockfile,"%s/CVSROOT/%s.%d",CVSroot,RCVSLOCK_rfl, getpid ()); msg = lock_msg[0]; } else error (1, 0, "rcvs_Setlock: bug #2"); lockfp = open_file (lockfile, "w+"); *(line+strlen(line)-1) = '\0'; if (command_name == NULL) error (1, 0, "rcvs_Setlock: bug #1\n"); (void) fprintf (lockfp, "%s at [%s] from %s, %s-%s-lock,", rcvs_username, line, host, msg, command_name); err = fclose (lockfp); if (err) error (1, 0, "fail to set lock in %s", lockfile); else if (trace) fprintf(stderr, "-> rcvs_Setlock: set %s lock in %s\n", type, lockfile); if (strcmp(type,"WFL") == 0) rcvs_lock_WFL_local = TRUE; else if (strcmp(type,"RFL") == 0) rcvs_lock_RFL = TRUE; else if (strcmp(type,"wfl") == 0) rcvs_lock_wfl = TRUE; else rcvs_lock_rfl = TRUE; return err; } /* rcvs: do chmod on main repository on server for added directory */ int rcvs_add_dir_chmod ( p ) Node *p; { struct stat sbuf; char repository[PATH_MAX]; sprintf (repository, "%s/%s", CVSroot, p->key); if ( stat(repository, &sbuf) == 0) sprintf (rshtmp+strlen(rshtmp), "chmod %o %s/%s;", sbuf.st_mode, RCVSroot, p->key); return (0); } /* rcvs: calculate size of dir_list */ int rcvs_size_dir_chmod ( p ) Node *p; { struct stat sbuf; char repository[PATH_MAX]; sprintf (repository, "%s/%s", CVSroot, p->key); if ( stat(repository, &sbuf) == 0) size_dirlist = size_dirlist + strlen(repository); return (0); } /* collect files to be removed */ int rcvs_do_rmlist (p) Node *p; { sprintf(rshtmp+strlen(rshtmp),"%s/%s ", RCVSroot, p->key); return (0); } /* calculate size needed for rmlist */ int rcvs_size_rmlist (p) Node *p; { char tmp[PATH_MAX]; sprintf(tmp,"%s/%s ", RCVSroot, p->key); size_rmlist = size_rmlist + strlen(tmp); return (0); } /* 1. Append history file to main repository. * 2. Cleanup lock for commit. * 3. Remove files if requested. * 4. chmod for added directories in main repository. */ int rcvs_hist_and_lock( err , cvsroot, rcvsuser, rcvshost, rcvsroot, rcvsdir) int err; char *cvsroot; char *rcvsuser; char *rcvshost; char *rcvsroot; char *rcvsdir; { char line[PATH_MAX]; char lockfile[PATH_MAX]; char hist_svr[PATH_MAX]; char hist_clt[PATH_MAX]; int Err; int send = 0; int msgfile_size = 0; int alloc_size; if (trace) fprintf (stderr, "-> rcvs_hist_and_lock\n"); /* calculate size of buffer needed for rsh */ (void) walklist (rcvs_dirlist, rcvs_size_dir_chmod); (void) walklist (rcvs_rmlist, rcvs_size_rmlist); alloc_size = PATH_MAX + PATH_MAX + size_rmlist + size_dirlist; if (trace) fprintf (stderr, "-> size_rmlist = %d size_dirlist=%d alloc=%d\n", size_rmlist, size_dirlist, alloc_size); rshtmp = xmalloc (alloc_size); rshtmp[0] = '\0'; lockfile[0] = '\0'; hist_svr[0] = '\0'; hist_clt[0] = '\0'; (void) sprintf( lockfile,"%s/CVSROOT/%s",cvsroot,RCVSLOCK); if ( (rcvs_lock_WFL_local || rcvs_Uopt_unlock) && isreadable (lockfile)) if ( unlink_file (lockfile) ) error (1, 0, "cannot remove lockfile %s\n", lockfile); else rcvs_lock_WFL_local = FALSE; (void) sprintf( hist_clt,"%s/CVSROOT/%s",cvsroot,CVSROOTADM_HISTORY_NEW); (void) sprintf( hist_svr,"%s/CVSROOT/%s",rcvsroot,CVSROOTADM_HISTORY); if ( RCVSuser == NULL ) RCVSuser = rcvs_username; (void) sprintf (rshtmp,"echo \'"); (void) sprintf( line, "%s/CVSROOT/%s", CVSroot, CVSROOTADM_HISTORY_NEW); /* if no phase-3 and has history, pipe history file * this history log is additional thus should be sent only if there are * other operations need to be done on server by setting send=1. * Do not set send=1 here! */ if ((!index (rcvs_Popt_phases,'3')) && filesize(line)) { FILE *fp; int count=0; (void) sprintf (rshtmp+strlen(rshtmp), "echo \""); fp = open_file (hist_clt,"r"); while (fgets (line, sizeof (line), fp)) { *(line + strlen(line) -1) = '\0'; if (count >= 1) {sprintf(rshtmp+strlen(rshtmp),"\\n");} sprintf(rshtmp+strlen(rshtmp),"%s", line); count++; } if (fclose (fp) == EOF) error (1, 0, "rcvs_hist_and_lock: cannot close %s", hist_clt); (void) sprintf (rshtmp+strlen(rshtmp), "\" >> %s/CVSROOT/%s;", rcvsroot, CVSROOTADM_HISTORY); } /* history file is not empty*/ if (filesize(line) && (index(rcvs_Popt_phases,'3') )) { (void) sprintf (rshtmp+strlen(rshtmp), "cat %s/CVSROOT/%s >> %s/CVSROOT/%s; rm -f %s/CVSROOT/%s;", rcvsroot, CVSROOTADM_HISTORY_NEW, rcvsroot, CVSROOTADM_HISTORY, rcvsroot, CVSROOTADM_HISTORY_NEW); send = 1; } /* commitlog file is not empty*/ (void) sprintf( line, "%s/CVSROOT/%s", CVSroot, CVSROOTADM_COMMITLOG_NEW); if (filesize(line) && (index(rcvs_Popt_phases,'3') )) { (void) sprintf (rshtmp+strlen(rshtmp), "cat %s/CVSROOT/%s >> %s/CVSROOT/%s; rm -f %s/CVSROOT/%s; ", rcvsroot, CVSROOTADM_COMMITLOG_NEW, rcvsroot, CVSROOTADM_COMMITLOG, rcvsroot, CVSROOTADM_COMMITLOG_NEW); send = 1; } /* rcvs: cleanup lock on server */ if ( ( rcvs_lock_WFL_remote || rcvs_Uopt_unlock ) && rcvsroot != NULL ) { (void) sprintf( lockfile,"%s/CVSROOT/%s",rcvsroot,RCVSLOCK); (void) sprintf (rshtmp+strlen(rshtmp),"rm %s; ", lockfile ); if (!trace && err == 1) fprintf (stderr, "remove lock on server\n"); send = 1; } /* chmod for added directory */ if (strcmp (rcvs_CM->fullname, "add") == 0) { (void) sprintf (rshtmp+strlen(rshtmp)," "); (void) walklist (rcvs_dirlist, rcvs_add_dir_chmod); (void) sprintf (rshtmp+strlen(rshtmp)," "); send = 1; } /* remove files if list not empty */ if (rcvs_rmlist != NULL) { (void) sprintf (rshtmp+strlen(rshtmp),"rm -f "); (void) walklist (rcvs_rmlist, rcvs_do_rmlist); (void) sprintf (rshtmp+strlen(rshtmp),"; "); send = 1; } /* execute message from in-shell CVS, mostly to execute shell script */ if (rcvs_msgfile != NULL && isfile(rcvs_msgfile)) { sprintf(rshtmp+strlen(rshtmp),"CVSROOT=%s; export CVSROOT; ", RCVSroot); msgfp = open_file (rcvs_msgfile,"r"); while (fgets (line, sizeof (line), msgfp)) sprintf(rshtmp+strlen(rshtmp),"%s", line); if (fclose (msgfp) == EOF) error (1, 0, "rcvs_hist_and_lock: cannot close %s", rcvs_msgfile); send = 1; } /*+IS*/ (void) sprintf (rshtmp+strlen(rshtmp), " \' | %s %s -l %s sh -s" , rshcmd, rcvshost, RCVSuser); /*-IS*/ if (trace && send) fprintf(stderr, "-> rshtmp (%d/%d): %s\n", strlen(rshtmp), alloc_size, rshtmp); if (send) Err = system ( rshtmp ); if (!Err) rcvs_lock_WFL_remote = FALSE; free (rshtmp); /* free buffer */ }