root.c revision 25839
117721Speter/* 217721Speter * Copyright (c) 1992, Mark D. Baushke 317721Speter * 417721Speter * You may distribute under the terms of the GNU General Public License as 517721Speter * specified in the README file that comes with the CVS 1.4 kit. 617721Speter * 717721Speter * Name of Root 817721Speter * 917721Speter * Determine the path to the CVSROOT and set "Root" accordingly. 1017721Speter * If this looks like of modified clone of Name_Repository() in 1117721Speter * repos.c, it is... 1217721Speter */ 1317721Speter 1417721Speter#include "cvs.h" 1525839Speter#include "getline.h" 1617721Speter 1725839Speter/* Printable names for things in the CVSroot_method enum variable. 1825839Speter Watch out if the enum is changed in cvs.h! */ 1925839Speter 2025839Speterchar *method_names[] = { 2125839Speter "local", "server (rsh)", "pserver", "kserver", "ext" 2225839Speter}; 2325839Speter 2425839Speter#ifndef DEBUG 2525839Speter 2617721Speterchar * 2717721SpeterName_Root(dir, update_dir) 2817721Speter char *dir; 2917721Speter char *update_dir; 3017721Speter{ 3117721Speter FILE *fpin; 3217721Speter char *ret, *xupdate_dir; 3325839Speter char *root = NULL; 3425839Speter size_t root_allocated = 0; 3525839Speter char *tmp; 3625839Speter char *cvsadm; 3717721Speter char *cp; 3817721Speter 3917721Speter if (update_dir && *update_dir) 4017721Speter xupdate_dir = update_dir; 4117721Speter else 4217721Speter xupdate_dir = "."; 4317721Speter 4417721Speter if (dir != NULL) 4517721Speter { 4625839Speter cvsadm = xmalloc (strlen (dir) + sizeof (CVSADM) + 10); 4717721Speter (void) sprintf (cvsadm, "%s/%s", dir, CVSADM); 4825839Speter tmp = xmalloc (strlen (dir) + sizeof (CVSADM_ROOT) + 10); 4917721Speter (void) sprintf (tmp, "%s/%s", dir, CVSADM_ROOT); 5017721Speter } 5117721Speter else 5217721Speter { 5325839Speter cvsadm = xstrdup (CVSADM); 5425839Speter tmp = xstrdup (CVSADM_ROOT); 5517721Speter } 5617721Speter 5717721Speter /* 5817721Speter * Do not bother looking for a readable file if there is no cvsadm 5917721Speter * directory present. 6017721Speter * 6117721Speter * It is possible that not all repositories will have a CVS/Root 6217721Speter * file. This is ok, but the user will need to specify -d 6317721Speter * /path/name or have the environment variable CVSROOT set in 6425839Speter * order to continue. */ 6517721Speter if ((!isdir (cvsadm)) || (!isreadable (tmp))) 6617721Speter { 6725839Speter ret = NULL; 6825839Speter goto out; 6917721Speter } 7017721Speter 7117721Speter /* 7217721Speter * The assumption here is that the CVS Root is always contained in the 7317721Speter * first line of the "Root" file. 7417721Speter */ 7517721Speter fpin = open_file (tmp, "r"); 7617721Speter 7725839Speter if (getline (&root, &root_allocated, fpin) < 0) 7817721Speter { 7925839Speter /* FIXME: should be checking for end of file separately; errno 8025839Speter is not set in that case. */ 8117721Speter error (0, 0, "in directory %s:", xupdate_dir); 8217721Speter error (0, errno, "cannot read %s", CVSADM_ROOT); 8317721Speter error (0, 0, "please correct this problem"); 8425839Speter ret = NULL; 8525839Speter goto out; 8617721Speter } 8717721Speter (void) fclose (fpin); 8817721Speter if ((cp = strrchr (root, '\n')) != NULL) 8917721Speter *cp = '\0'; /* strip the newline */ 9017721Speter 9117721Speter /* 9217721Speter * root now contains a candidate for CVSroot. It must be an 9325839Speter * absolute pathname or specify a remote server. 9417721Speter */ 9517721Speter 9625839Speter if ( 9717721Speter#ifdef CLIENT_SUPPORT 9825839Speter (strchr (root, ':') == NULL) && 9925839Speter#endif 10025839Speter ! isabsolute (root)) 10117721Speter { 10217721Speter error (0, 0, "in directory %s:", xupdate_dir); 10317721Speter error (0, 0, 10417721Speter "ignoring %s because it does not contain an absolute pathname.", 10517721Speter CVSADM_ROOT); 10625839Speter ret = NULL; 10725839Speter goto out; 10817721Speter } 10917721Speter 11017721Speter#ifdef CLIENT_SUPPORT 11117721Speter if ((strchr (root, ':') == NULL) && !isdir (root)) 11217721Speter#else /* ! CLIENT_SUPPORT */ 11317721Speter if (!isdir (root)) 11417721Speter#endif /* CLIENT_SUPPORT */ 11517721Speter { 11617721Speter error (0, 0, "in directory %s:", xupdate_dir); 11717721Speter error (0, 0, 11817721Speter "ignoring %s because it specifies a non-existent repository %s", 11917721Speter CVSADM_ROOT, root); 12025839Speter ret = NULL; 12125839Speter goto out; 12217721Speter } 12317721Speter 12417721Speter /* allocate space to return and fill it in */ 12525839Speter strip_trailing_slashes (root); 12617721Speter ret = xstrdup (root); 12725839Speter out: 12825839Speter free (cvsadm); 12925839Speter free (tmp); 13025839Speter if (root != NULL) 13125839Speter free (root); 13217721Speter return (ret); 13317721Speter} 13417721Speter 13517721Speter/* 13617721Speter * Returns non-zero if the two directories have the same stat values 13717721Speter * which indicates that they are really the same directories. 13817721Speter */ 13917721Speterint 14017721Spetersame_directories (dir1, dir2) 14117721Speter char *dir1; 14217721Speter char *dir2; 14317721Speter{ 14417721Speter struct stat sb1; 14517721Speter struct stat sb2; 14617721Speter int ret; 14717721Speter 14825839Speter if ( CVS_STAT (dir1, &sb1) < 0) 14917721Speter return (0); 15025839Speter if ( CVS_STAT (dir2, &sb2) < 0) 15117721Speter return (0); 15217721Speter 15317721Speter ret = 0; 15417721Speter if ( (memcmp( &sb1.st_dev, &sb2.st_dev, sizeof(dev_t) ) == 0) && 15517721Speter (memcmp( &sb1.st_ino, &sb2.st_ino, sizeof(ino_t) ) == 0)) 15617721Speter ret = 1; 15717721Speter 15817721Speter return (ret); 15917721Speter} 16017721Speter 16117721Speter 16217721Speter/* 16317721Speter * Write the CVS/Root file so that the environment variable CVSROOT 16417721Speter * and/or the -d option to cvs will be validated or not necessary for 16517721Speter * future work. 16617721Speter */ 16717721Spetervoid 16817721SpeterCreate_Root (dir, rootdir) 16917721Speter char *dir; 17017721Speter char *rootdir; 17117721Speter{ 17217721Speter FILE *fout; 17325839Speter char *tmp; 17417721Speter 17517721Speter if (noexec) 17617721Speter return; 17717721Speter 17817721Speter /* record the current cvs root */ 17917721Speter 18017721Speter if (rootdir != NULL) 18117721Speter { 18217721Speter if (dir != NULL) 18325839Speter { 18425839Speter tmp = xmalloc (strlen (dir) + sizeof (CVSADM_ROOT) + 10); 18517721Speter (void) sprintf (tmp, "%s/%s", dir, CVSADM_ROOT); 18625839Speter } 18717721Speter else 18825839Speter tmp = xstrdup (CVSADM_ROOT); 18925839Speter 19017721Speter fout = open_file (tmp, "w+"); 19117721Speter if (fprintf (fout, "%s\n", rootdir) < 0) 19217721Speter error (1, errno, "write to %s failed", tmp); 19317721Speter if (fclose (fout) == EOF) 19417721Speter error (1, errno, "cannot close %s", tmp); 19525839Speter free (tmp); 19617721Speter } 19717721Speter} 19825839Speter 19925839Speter#endif /* ! DEBUG */ 20025839Speter 20125839Speter 20225839Speter/* Parse a CVSROOT variable into its constituent parts -- method, 20325839Speter * username, hostname, directory. The prototypical CVSROOT variable 20425839Speter * looks like: 20525839Speter * 20625839Speter * :method:user@host:path 20725839Speter * 20825839Speter * Some methods may omit fields; local, for example, doesn't need user 20925839Speter * and host. 21025839Speter * 21125839Speter * Returns zero on success, non-zero on failure. */ 21225839Speter 21325839Speterchar *CVSroot_original = NULL; /* the CVSroot that was passed in */ 21425839Speterint client_active; /* nonzero if we are doing remote access */ 21525839SpeterCVSmethod CVSroot_method; /* one of the enum values defined in cvs.h */ 21625839Speterchar *CVSroot_username; /* the username or NULL if method == local */ 21725839Speterchar *CVSroot_hostname; /* the hostname or NULL if method == local */ 21825839Speterchar *CVSroot_directory; /* the directory name */ 21925839Speter 22025839Speter#ifdef AUTH_SERVER_SUPPORT 22125839Speter/* Die if CVSroot_directory and Pserver_Repos don't match. */ 22225839Speterstatic void 22325839Spetercheck_root_consistent () 22425839Speter{ 22525839Speter /* FIXME: Should be using a deferred error, as the rest of 22625839Speter serve_root does. As it is now the call to error could conceivably 22725839Speter cause deadlock, as noted in server_cleanup. Best solution would 22825839Speter presumably be to write some code so that error() automatically 22925839Speter defers the error in those cases where that is needed. */ 23025839Speter /* FIXME? Possible that the wording should be more clear (e.g. 23125839Speter Root says "%s" but pserver protocol says "%s" 23225839Speter or something which would aid people who are writing implementations 23325839Speter of the client side of the CVS protocol. I don't see any security 23425839Speter problem with revealing that information. */ 23525839Speter if ((Pserver_Repos != NULL) && (CVSroot_directory != NULL)) 23625839Speter if (strcmp (Pserver_Repos, CVSroot_directory) != 0) 23725839Speter error (1, 0, "repository mismatch: \"%s\" vs \"%s\"", 23825839Speter Pserver_Repos, CVSroot_directory); 23925839Speter} 24025839Speter 24125839Speter#endif /* AUTH_SERVER_SUPPORT */ 24225839Speter 24325839Speter 24425839Speterint 24525839Speterparse_cvsroot (CVSroot) 24625839Speter char *CVSroot; 24725839Speter{ 24825839Speter static int cvsroot_parsed = 0; 24925839Speter char *cvsroot_copy, *p; 25025839Speter 25125839Speter /* Don't go through the trouble twice. */ 25225839Speter if (cvsroot_parsed) 25325839Speter { 25425839Speter error (0, 0, "WARNING (parse_cvsroot): someone called me twice!\n"); 25525839Speter return 0; 25625839Speter } 25725839Speter 25825839Speter CVSroot_original = xstrdup (CVSroot); 25925839Speter cvsroot_copy = xstrdup (CVSroot); 26025839Speter 26125839Speter if ((*cvsroot_copy == ':')) 26225839Speter { 26325839Speter char *method = ++cvsroot_copy; 26425839Speter 26525839Speter /* Access method specified, as in 26625839Speter * "cvs -d :pserver:user@host:/path", 26725839Speter * "cvs -d :local:e:\path", or 26825839Speter * "cvs -d :kserver:user@host:/path". 26925839Speter * We need to get past that part of CVSroot before parsing the 27025839Speter * rest of it. 27125839Speter */ 27225839Speter 27325839Speter if (! (p = strchr (method, ':'))) 27425839Speter { 27525839Speter error (0, 0, "bad CVSroot: %s", CVSroot); 27625839Speter return 1; 27725839Speter } 27825839Speter *p = '\0'; 27925839Speter cvsroot_copy = ++p; 28025839Speter 28125839Speter /* Now we have an access method -- see if it's valid. */ 28225839Speter 28325839Speter if (strcmp (method, "local") == 0) 28425839Speter CVSroot_method = local_method; 28525839Speter else if (strcmp (method, "pserver") == 0) 28625839Speter CVSroot_method = pserver_method; 28725839Speter else if (strcmp (method, "kserver") == 0) 28825839Speter CVSroot_method = kserver_method; 28925839Speter else if (strcmp (method, "server") == 0) 29025839Speter CVSroot_method = server_method; 29125839Speter else if (strcmp (method, "ext") == 0) 29225839Speter CVSroot_method = ext_method; 29325839Speter else 29425839Speter { 29525839Speter error (0, 0, "unknown method in CVSroot: %s", CVSroot); 29625839Speter return 1; 29725839Speter } 29825839Speter } 29925839Speter else 30025839Speter { 30125839Speter /* If the method isn't specified, assume 30225839Speter SERVER_METHOD/EXT_METHOD if the string contains a colon or 30325839Speter LOCAL_METHOD otherwise. */ 30425839Speter 30525839Speter CVSroot_method = ((strchr (cvsroot_copy, ':')) 30625839Speter#ifdef RSH_NOT_TRANSPARENT 30725839Speter ? server_method 30825839Speter#else 30925839Speter ? ext_method 31025839Speter#endif 31125839Speter : local_method); 31225839Speter } 31325839Speter 31425839Speter client_active = (CVSroot_method != local_method); 31525839Speter 31625839Speter /* Check for username/hostname if we're not LOCAL_METHOD. */ 31725839Speter 31825839Speter CVSroot_username = NULL; 31925839Speter CVSroot_hostname = NULL; 32025839Speter 32125839Speter if (CVSroot_method != local_method) 32225839Speter { 32325839Speter /* Check to see if there is a username in the string. */ 32425839Speter 32525839Speter if ((p = strchr (cvsroot_copy, '@'))) 32625839Speter { 32725839Speter CVSroot_username = cvsroot_copy; 32825839Speter *p = '\0'; 32925839Speter cvsroot_copy = ++p; 33025839Speter if (*CVSroot_username == '\0') 33125839Speter CVSroot_username = NULL; 33225839Speter } 33325839Speter 33425839Speter if ((p = strchr (cvsroot_copy, ':'))) 33525839Speter { 33625839Speter CVSroot_hostname = cvsroot_copy; 33725839Speter *p = '\0'; 33825839Speter cvsroot_copy = ++p; 33925839Speter 34025839Speter if (*CVSroot_hostname == '\0') 34125839Speter CVSroot_hostname = NULL; 34225839Speter } 34325839Speter } 34425839Speter 34525839Speter CVSroot_directory = cvsroot_copy; 34625839Speter#ifdef AUTH_SERVER_SUPPORT 34725839Speter check_root_consistent (); 34825839Speter#endif /* AUTH_SERVER_SUPPORT */ 34925839Speter 35025839Speter#if ! defined (CLIENT_SUPPORT) && ! defined (DEBUG) 35125839Speter if (CVSroot_method != local_method) 35225839Speter { 35325839Speter error (0, 0, "Your CVSROOT is set for a remote access method"); 35425839Speter error (0, 0, "but your CVS executable doesn't support it"); 35525839Speter error (0, 0, "(%s)", CVSroot); 35625839Speter return 1; 35725839Speter } 35825839Speter#endif 35925839Speter 36025839Speter /* Do various sanity checks. */ 36125839Speter 36225839Speter if (CVSroot_username && ! CVSroot_hostname) 36325839Speter { 36425839Speter error (0, 0, "missing hostname in CVSROOT: %s", CVSroot); 36525839Speter return 1; 36625839Speter } 36725839Speter 36825839Speter switch (CVSroot_method) 36925839Speter { 37025839Speter case local_method: 37125839Speter if (CVSroot_username || CVSroot_hostname) 37225839Speter { 37325839Speter error (0, 0, "can't specify hostname and username in CVSROOT"); 37425839Speter error (0, 0, "when using local access method"); 37525839Speter error (0, 0, "(%s)", CVSroot); 37625839Speter return 1; 37725839Speter } 37825839Speter /* cvs.texinfo has always told people that CVSROOT must be an 37925839Speter absolute pathname. Furthermore, attempts to use a relative 38025839Speter pathname produced various errors (I couldn't get it to work), 38125839Speter so there would seem to be little risk in making this a fatal 38225839Speter error. */ 38325839Speter if (!isabsolute (CVSroot_directory)) 38425839Speter error (1, 0, "CVSROOT %s must be an absolute pathname", 38525839Speter CVSroot_directory); 38625839Speter break; 38725839Speter case kserver_method: 38825839Speter#ifndef HAVE_KERBEROS 38925839Speter error (0, 0, "Your CVSROOT is set for a kerberos access method"); 39025839Speter error (0, 0, "but your CVS executable doesn't support it"); 39125839Speter error (0, 0, "(%s)", CVSroot); 39225839Speter return 1; 39325839Speter#endif 39425839Speter case server_method: 39525839Speter case ext_method: 39625839Speter case pserver_method: 39725839Speter if (! CVSroot_hostname) 39825839Speter { 39925839Speter error (0, 0, "didn't specify hostname in CVSROOT: %s", CVSroot); 40025839Speter return 1; 40125839Speter } 40225839Speter break; 40325839Speter } 40425839Speter 40525839Speter if (*CVSroot_directory == '\0') 40625839Speter { 40725839Speter error (0, 0, "missing directory in CVSROOT: %s", CVSroot); 40825839Speter return 1; 40925839Speter } 41025839Speter 41125839Speter /* Hooray! We finally parsed it! */ 41225839Speter return 0; 41325839Speter} 41425839Speter 41525839Speter 41625839Speter/* Set up the global CVSroot* variables as if we're using the local 41725839Speter repository DIR. */ 41825839Speter 41925839Spetervoid 42025839Speterset_local_cvsroot (dir) 42125839Speter char *dir; 42225839Speter{ 42325839Speter CVSroot_original = xstrdup (dir); 42425839Speter CVSroot_method = local_method; 42525839Speter CVSroot_directory = CVSroot_original; 42625839Speter#ifdef AUTH_SERVER_SUPPORT 42725839Speter check_root_consistent (); 42825839Speter#endif /* AUTH_SERVER_SUPPORT */ 42925839Speter CVSroot_username = NULL; 43025839Speter CVSroot_hostname = NULL; 43125839Speter client_active = 0; 43225839Speter} 43325839Speter 43425839Speter 43525839Speter#ifdef DEBUG 43625839Speter/* This is for testing the parsing function. */ 43725839Speter 43825839Speter#include <stdio.h> 43925839Speter 44025839Speterchar *CVSroot; 44125839Speterchar *program_name = "testing"; 44225839Speterchar *command_name = "parse_cvsroot"; /* XXX is this used??? */ 44325839Speter 44425839Spetervoid 44525839Spetermain (argc, argv) 44625839Speter int argc; 44725839Speter char *argv[]; 44825839Speter{ 44925839Speter program_name = argv[0]; 45025839Speter 45125839Speter if (argc != 2) 45225839Speter { 45325839Speter fprintf (stderr, "Usage: %s <CVSROOT>\n", program_name); 45425839Speter exit (2); 45525839Speter } 45625839Speter 45725839Speter if (parse_cvsroot (argv[1])) 45825839Speter { 45925839Speter fprintf (stderr, "%s: Parsing failed.", program_name); 46025839Speter exit (1); 46125839Speter } 46225839Speter printf ("CVSroot: %s\n", argv[1]); 46325839Speter printf ("CVSroot_method: %s\n", method_names[CVSroot_method]); 46425839Speter printf ("CVSroot_username: %s\n", 46525839Speter CVSroot_username ? CVSroot_username : "NULL"); 46625839Speter printf ("CVSroot_hostname: %s\n", 46725839Speter CVSroot_hostname ? CVSroot_hostname : "NULL"); 46825839Speter printf ("CVSroot_directory: %s\n", CVSroot_directory); 46925839Speter 47025839Speter exit (0); 47125839Speter /* NOTREACHED */ 47225839Speter} 47325839Speter#endif 474