root.c revision 26801
1/* 2 * Copyright (c) 1992, Mark D. Baushke 3 * 4 * You may distribute under the terms of the GNU General Public License as 5 * specified in the README file that comes with the CVS 1.4 kit. 6 * 7 * Name of Root 8 * 9 * Determine the path to the CVSROOT and set "Root" accordingly. 10 * If this looks like of modified clone of Name_Repository() in 11 * repos.c, it is... 12 */ 13 14#include "cvs.h" 15#include "getline.h" 16 17/* Printable names for things in the CVSroot_method enum variable. 18 Watch out if the enum is changed in cvs.h! */ 19 20char *method_names[] = { 21 "local", "server (rsh)", "pserver", "kserver", "ext" 22}; 23 24#ifndef DEBUG 25 26char * 27Name_Root(dir, update_dir) 28 char *dir; 29 char *update_dir; 30{ 31 FILE *fpin; 32 char *ret, *xupdate_dir; 33 char *root = NULL; 34 size_t root_allocated = 0; 35 char *tmp; 36 char *cvsadm; 37 char *cp; 38 39 if (update_dir && *update_dir) 40 xupdate_dir = update_dir; 41 else 42 xupdate_dir = "."; 43 44 if (dir != NULL) 45 { 46 cvsadm = xmalloc (strlen (dir) + sizeof (CVSADM) + 10); 47 (void) sprintf (cvsadm, "%s/%s", dir, CVSADM); 48 tmp = xmalloc (strlen (dir) + sizeof (CVSADM_ROOT) + 10); 49 (void) sprintf (tmp, "%s/%s", dir, CVSADM_ROOT); 50 } 51 else 52 { 53 cvsadm = xstrdup (CVSADM); 54 tmp = xstrdup (CVSADM_ROOT); 55 } 56 57 /* 58 * Do not bother looking for a readable file if there is no cvsadm 59 * directory present. 60 * 61 * It is possible that not all repositories will have a CVS/Root 62 * file. This is ok, but the user will need to specify -d 63 * /path/name or have the environment variable CVSROOT set in 64 * order to continue. */ 65 if ((!isdir (cvsadm)) || (!isreadable (tmp))) 66 { 67 ret = NULL; 68 goto out; 69 } 70 71 /* 72 * The assumption here is that the CVS Root is always contained in the 73 * first line of the "Root" file. 74 */ 75 fpin = open_file (tmp, "r"); 76 77 if (getline (&root, &root_allocated, fpin) < 0) 78 { 79 /* FIXME: should be checking for end of file separately; errno 80 is not set in that case. */ 81 error (0, 0, "in directory %s:", xupdate_dir); 82 error (0, errno, "cannot read %s", CVSADM_ROOT); 83 error (0, 0, "please correct this problem"); 84 ret = NULL; 85 goto out; 86 } 87 (void) fclose (fpin); 88 if ((cp = strrchr (root, '\n')) != NULL) 89 *cp = '\0'; /* strip the newline */ 90 91 /* 92 * root now contains a candidate for CVSroot. It must be an 93 * absolute pathname or specify a remote server. 94 */ 95 96 if ( 97#ifdef CLIENT_SUPPORT 98 (strchr (root, ':') == NULL) && 99#endif 100 ! isabsolute (root)) 101 { 102 error (0, 0, "in directory %s:", xupdate_dir); 103 error (0, 0, 104 "ignoring %s because it does not contain an absolute pathname.", 105 CVSADM_ROOT); 106 ret = NULL; 107 goto out; 108 } 109 110#ifdef CLIENT_SUPPORT 111 if ((strchr (root, ':') == NULL) && !isdir (root)) 112#else /* ! CLIENT_SUPPORT */ 113 if (!isdir (root)) 114#endif /* CLIENT_SUPPORT */ 115 { 116 error (0, 0, "in directory %s:", xupdate_dir); 117 error (0, 0, 118 "ignoring %s because it specifies a non-existent repository %s", 119 CVSADM_ROOT, root); 120 ret = NULL; 121 goto out; 122 } 123 124 /* allocate space to return and fill it in */ 125 strip_trailing_slashes (root); 126 ret = xstrdup (root); 127 out: 128 free (cvsadm); 129 free (tmp); 130 if (root != NULL) 131 free (root); 132 return (ret); 133} 134 135/* 136 * Returns non-zero if the two directories have the same stat values 137 * which indicates that they are really the same directories. 138 */ 139int 140same_directories (dir1, dir2) 141 char *dir1; 142 char *dir2; 143{ 144 struct stat sb1; 145 struct stat sb2; 146 int ret; 147 148 if ( CVS_STAT (dir1, &sb1) < 0) 149 return (0); 150 if ( CVS_STAT (dir2, &sb2) < 0) 151 return (0); 152 153 ret = 0; 154 if ( (memcmp( &sb1.st_dev, &sb2.st_dev, sizeof(dev_t) ) == 0) && 155 (memcmp( &sb1.st_ino, &sb2.st_ino, sizeof(ino_t) ) == 0)) 156 ret = 1; 157 158 return (ret); 159} 160 161 162/* 163 * Write the CVS/Root file so that the environment variable CVSROOT 164 * and/or the -d option to cvs will be validated or not necessary for 165 * future work. 166 */ 167void 168Create_Root (dir, rootdir) 169 char *dir; 170 char *rootdir; 171{ 172 FILE *fout; 173 char *tmp; 174 175 if (noexec) 176 return; 177 178 /* record the current cvs root */ 179 180 if (rootdir != NULL) 181 { 182 if (dir != NULL) 183 { 184 tmp = xmalloc (strlen (dir) + sizeof (CVSADM_ROOT) + 10); 185 (void) sprintf (tmp, "%s/%s", dir, CVSADM_ROOT); 186 } 187 else 188 tmp = xstrdup (CVSADM_ROOT); 189 190 fout = open_file (tmp, "w+"); 191 if (fprintf (fout, "%s\n", rootdir) < 0) 192 error (1, errno, "write to %s failed", tmp); 193 if (fclose (fout) == EOF) 194 error (1, errno, "cannot close %s", tmp); 195 free (tmp); 196 } 197} 198 199#endif /* ! DEBUG */ 200 201 202/* The root_allow_* stuff maintains a list of legal CVSROOT 203 directories. Then we can check against them when a remote user 204 hands us a CVSROOT directory. */ 205 206static unsigned int root_allow_count; 207static char **root_allow_vector; 208static unsigned int root_allow_size; 209 210void 211root_allow_add (arg) 212 char *arg; 213{ 214 char *p; 215 216 if (root_allow_size <= root_allow_count) 217 { 218 if (root_allow_size == 0) 219 { 220 root_allow_size = 1; 221 root_allow_vector = 222 (char **) malloc (root_allow_size * sizeof (char *)); 223 } 224 else 225 { 226 root_allow_size *= 2; 227 root_allow_vector = 228 (char **) realloc (root_allow_vector, 229 root_allow_size * sizeof (char *)); 230 } 231 232 if (root_allow_vector == NULL) 233 { 234 no_memory: 235 /* Strictly speaking, we're not supposed to output anything 236 now. But we're about to exit(), give it a try. */ 237 printf ("E Fatal server error, aborting.\n\ 238error ENOMEM Virtual memory exhausted.\n"); 239 240 /* I'm doing this manually rather than via error_exit () 241 because I'm not sure whether we want to call server_cleanup. 242 Needs more investigation.... */ 243 244#ifdef SYSTEM_CLEANUP 245 /* Hook for OS-specific behavior, for example socket 246 subsystems on NT and OS2 or dealing with windows 247 and arguments on Mac. */ 248 SYSTEM_CLEANUP (); 249#endif 250 251 exit (EXIT_FAILURE); 252 } 253 } 254 p = malloc (strlen (arg) + 1); 255 if (p == NULL) 256 goto no_memory; 257 strcpy (p, arg); 258 root_allow_vector[root_allow_count++] = p; 259} 260 261void 262root_allow_free () 263{ 264 if (root_allow_vector != NULL) 265 free (root_allow_vector); 266 root_allow_count = 0; 267 root_allow_size = 0; 268} 269 270int 271root_allow_ok (arg) 272 char *arg; 273{ 274 unsigned int i; 275 for (i = 0; i < root_allow_count; ++i) 276 if (strcmp (root_allow_vector[i], arg) == 0) 277 return 1; 278 return 0; 279} 280 281 282/* Parse a CVSROOT variable into its constituent parts -- method, 283 * username, hostname, directory. The prototypical CVSROOT variable 284 * looks like: 285 * 286 * :method:user@host:path 287 * 288 * Some methods may omit fields; local, for example, doesn't need user 289 * and host. 290 * 291 * Returns zero on success, non-zero on failure. */ 292 293char *CVSroot_original = NULL; /* the CVSroot that was passed in */ 294int client_active; /* nonzero if we are doing remote access */ 295CVSmethod CVSroot_method; /* one of the enum values defined in cvs.h */ 296char *CVSroot_username; /* the username or NULL if method == local */ 297char *CVSroot_hostname; /* the hostname or NULL if method == local */ 298char *CVSroot_directory; /* the directory name */ 299 300#ifdef AUTH_SERVER_SUPPORT 301/* Die if CVSroot_directory and Pserver_Repos don't match. */ 302static void 303check_root_consistent () 304{ 305 /* FIXME: Should be using a deferred error, as the rest of 306 serve_root does. As it is now the call to error could conceivably 307 cause deadlock, as noted in server_cleanup. Best solution would 308 presumably be to write some code so that error() automatically 309 defers the error in those cases where that is needed. */ 310 /* FIXME? Possible that the wording should be more clear (e.g. 311 Root says "%s" but pserver protocol says "%s" 312 or something which would aid people who are writing implementations 313 of the client side of the CVS protocol. I don't see any security 314 problem with revealing that information. */ 315 if ((Pserver_Repos != NULL) && (CVSroot_directory != NULL)) 316 if (strcmp (Pserver_Repos, CVSroot_directory) != 0) 317 error (1, 0, "repository mismatch: \"%s\" vs \"%s\"", 318 Pserver_Repos, CVSroot_directory); 319} 320 321#endif /* AUTH_SERVER_SUPPORT */ 322 323 324int 325parse_cvsroot (CVSroot) 326 char *CVSroot; 327{ 328 static int cvsroot_parsed = 0; 329 char *cvsroot_copy, *p; 330 331 /* Don't go through the trouble twice. */ 332 if (cvsroot_parsed) 333 { 334 error (0, 0, "WARNING (parse_cvsroot): someone called me twice!\n"); 335 return 0; 336 } 337 338 CVSroot_original = xstrdup (CVSroot); 339 cvsroot_copy = xstrdup (CVSroot); 340 341 if ((*cvsroot_copy == ':')) 342 { 343 char *method = ++cvsroot_copy; 344 345 /* Access method specified, as in 346 * "cvs -d :pserver:user@host:/path", 347 * "cvs -d :local:e:\path", or 348 * "cvs -d :kserver:user@host:/path". 349 * We need to get past that part of CVSroot before parsing the 350 * rest of it. 351 */ 352 353 if (! (p = strchr (method, ':'))) 354 { 355 error (0, 0, "bad CVSroot: %s", CVSroot); 356 return 1; 357 } 358 *p = '\0'; 359 cvsroot_copy = ++p; 360 361 /* Now we have an access method -- see if it's valid. */ 362 363 if (strcmp (method, "local") == 0) 364 CVSroot_method = local_method; 365 else if (strcmp (method, "pserver") == 0) 366 CVSroot_method = pserver_method; 367 else if (strcmp (method, "kserver") == 0) 368 CVSroot_method = kserver_method; 369 else if (strcmp (method, "server") == 0) 370 CVSroot_method = server_method; 371 else if (strcmp (method, "ext") == 0) 372 CVSroot_method = ext_method; 373 else 374 { 375 error (0, 0, "unknown method in CVSroot: %s", CVSroot); 376 return 1; 377 } 378 } 379 else 380 { 381 /* If the method isn't specified, assume 382 SERVER_METHOD/EXT_METHOD if the string contains a colon or 383 LOCAL_METHOD otherwise. */ 384 385 CVSroot_method = ((strchr (cvsroot_copy, ':')) 386#ifdef RSH_NOT_TRANSPARENT 387 ? server_method 388#else 389 ? ext_method 390#endif 391 : local_method); 392 } 393 394 client_active = (CVSroot_method != local_method); 395 396 /* Check for username/hostname if we're not LOCAL_METHOD. */ 397 398 CVSroot_username = NULL; 399 CVSroot_hostname = NULL; 400 401 if (CVSroot_method != local_method) 402 { 403 /* Check to see if there is a username in the string. */ 404 405 if ((p = strchr (cvsroot_copy, '@'))) 406 { 407 CVSroot_username = cvsroot_copy; 408 *p = '\0'; 409 cvsroot_copy = ++p; 410 if (*CVSroot_username == '\0') 411 CVSroot_username = NULL; 412 } 413 414 if ((p = strchr (cvsroot_copy, ':'))) 415 { 416 CVSroot_hostname = cvsroot_copy; 417 *p = '\0'; 418 cvsroot_copy = ++p; 419 420 if (*CVSroot_hostname == '\0') 421 CVSroot_hostname = NULL; 422 } 423 } 424 425 CVSroot_directory = cvsroot_copy; 426#ifdef AUTH_SERVER_SUPPORT 427 check_root_consistent (); 428#endif /* AUTH_SERVER_SUPPORT */ 429 430#if ! defined (CLIENT_SUPPORT) && ! defined (DEBUG) 431 if (CVSroot_method != local_method) 432 { 433 error (0, 0, "Your CVSROOT is set for a remote access method"); 434 error (0, 0, "but your CVS executable doesn't support it"); 435 error (0, 0, "(%s)", CVSroot); 436 return 1; 437 } 438#endif 439 440 /* Do various sanity checks. */ 441 442 if (CVSroot_username && ! CVSroot_hostname) 443 { 444 error (0, 0, "missing hostname in CVSROOT: %s", CVSroot); 445 return 1; 446 } 447 448 switch (CVSroot_method) 449 { 450 case local_method: 451 if (CVSroot_username || CVSroot_hostname) 452 { 453 error (0, 0, "can't specify hostname and username in CVSROOT"); 454 error (0, 0, "when using local access method"); 455 error (0, 0, "(%s)", CVSroot); 456 return 1; 457 } 458 /* cvs.texinfo has always told people that CVSROOT must be an 459 absolute pathname. Furthermore, attempts to use a relative 460 pathname produced various errors (I couldn't get it to work), 461 so there would seem to be little risk in making this a fatal 462 error. */ 463 if (!isabsolute (CVSroot_directory)) 464 error (1, 0, "CVSROOT %s must be an absolute pathname", 465 CVSroot_directory); 466 break; 467 case kserver_method: 468#ifndef HAVE_KERBEROS 469 error (0, 0, "Your CVSROOT is set for a kerberos access method"); 470 error (0, 0, "but your CVS executable doesn't support it"); 471 error (0, 0, "(%s)", CVSroot); 472 return 1; 473#endif 474 case server_method: 475 case ext_method: 476 case pserver_method: 477 if (! CVSroot_hostname) 478 { 479 error (0, 0, "didn't specify hostname in CVSROOT: %s", CVSroot); 480 return 1; 481 } 482 break; 483 } 484 485 if (*CVSroot_directory == '\0') 486 { 487 error (0, 0, "missing directory in CVSROOT: %s", CVSroot); 488 return 1; 489 } 490 491 /* Hooray! We finally parsed it! */ 492 return 0; 493} 494 495 496/* Set up the global CVSroot* variables as if we're using the local 497 repository DIR. */ 498 499void 500set_local_cvsroot (dir) 501 char *dir; 502{ 503 CVSroot_original = xstrdup (dir); 504 CVSroot_method = local_method; 505 CVSroot_directory = CVSroot_original; 506#ifdef AUTH_SERVER_SUPPORT 507 check_root_consistent (); 508#endif /* AUTH_SERVER_SUPPORT */ 509 CVSroot_username = NULL; 510 CVSroot_hostname = NULL; 511 client_active = 0; 512} 513 514 515#ifdef DEBUG 516/* This is for testing the parsing function. */ 517 518#include <stdio.h> 519 520char *CVSroot; 521char *program_name = "testing"; 522char *command_name = "parse_cvsroot"; /* XXX is this used??? */ 523 524void 525main (argc, argv) 526 int argc; 527 char *argv[]; 528{ 529 program_name = argv[0]; 530 531 if (argc != 2) 532 { 533 fprintf (stderr, "Usage: %s <CVSROOT>\n", program_name); 534 exit (2); 535 } 536 537 if (parse_cvsroot (argv[1])) 538 { 539 fprintf (stderr, "%s: Parsing failed.", program_name); 540 exit (1); 541 } 542 printf ("CVSroot: %s\n", argv[1]); 543 printf ("CVSroot_method: %s\n", method_names[CVSroot_method]); 544 printf ("CVSroot_username: %s\n", 545 CVSroot_username ? CVSroot_username : "NULL"); 546 printf ("CVSroot_hostname: %s\n", 547 CVSroot_hostname ? CVSroot_hostname : "NULL"); 548 printf ("CVSroot_directory: %s\n", CVSroot_directory); 549 550 exit (0); 551 /* NOTREACHED */ 552} 553#endif 554