1 /*
2 * Copyright (c) 1992, Brian Berliner and Jeff Polk
3 * Copyright (c) 1989-1992, Brian Berliner
4 *
5 * You may distribute under the terms of the GNU General Public License
6 * as specified in the README file that comes with the CVS source distribution.
7 *
8 * This is the main C driver for the CVS system.
9 *
10 * Credit to Dick Grune, Vrije Universiteit, Amsterdam, for writing
11 * the shell-script CVS system that this is based on.
12 *
13 */
14
15 #include <assert.h>
16 #include "cvs.h"
17
18 #ifdef HAVE_WINSOCK_H
19 #include <winsock.h>
20 #else
21 extern int gethostname ();
22 #endif
23
24 char *program_name;
25 char *program_path;
26 char *command_name;
27
28 char *global_session_id; /* Random session ID */
29
30 /* I'd dynamically allocate this, but it seems like gethostname
31 requires a fixed size array. If I'm remembering the RFCs right,
32 256 should be enough. */
33 #ifndef MAXHOSTNAMELEN
34 #define MAXHOSTNAMELEN 256
35 #endif
36
37 char hostname[MAXHOSTNAMELEN];
38
39 int use_editor = 1;
40 int use_cvsrc = 1;
41 int cvswrite = !CVSREAD_DFLT;
42 int really_quiet = 0;
43 int quiet = 0;
44 int trace = 0;
45 int noexec = 0;
46 int readonlyfs = 0;
47 int logoff = 0;
48
49 /* Set if we should be writing CVSADM directories at top level. At
50 least for now we'll make the default be off (the CVS 1.9, not CVS
51 1.9.2, behavior). */
52 int top_level_admin = 0;
53
54 mode_t cvsumask = UMASK_DFLT;
55 char *RCS_citag = NULL;
56 int disable_mdocdate = 0;
57
58 char *CurDir;
59
60 /*
61 * Defaults, for the environment variables that are not set
62 */
63 char *Tmpdir = TMPDIR_DFLT;
64 char *Editor = EDITOR_DFLT;
65
66
67 /* When our working directory contains subdirectories with different
68 values in CVS/Root files, we maintain a list of them. */
69 List *root_directories = NULL;
70
71 /* We step through the above values. This variable is set to reflect
72 * the currently active value.
73 *
74 * Now static. FIXME - this variable should be removable (well, localizable)
75 * with a little more work.
76 */
77 static char *current_root = NULL;
78
79
80 static const struct cmd
81 {
82 char *fullname; /* Full name of the function (e.g. "commit") */
83
84 /* Synonyms for the command, nick1 and nick2. We supply them
85 mostly for two reasons: (1) CVS has always supported them, and
86 we need to maintain compatibility, (2) if there is a need for a
87 version which is shorter than the fullname, for ease in typing.
88 Synonyms have the disadvantage that people will see "new" and
89 then have to think about it, or look it up, to realize that is
90 the operation they know as "add". Also, this means that one
91 cannot create a command "cvs new" with a different meaning. So
92 new synonyms are probably best used sparingly, and where used
93 should be abbreviations of the fullname (preferably consisting
94 of the first 2 or 3 or so letters).
95
96 One thing that some systems do is to recognize any unique
97 abbreviation, for example "annotat" "annota", etc., for
98 "annotate". The problem with this is that scripts and user
99 habits will expect a certain abbreviation to be unique, and in
100 a future release of CVS it may not be. So it is better to
101 accept only an explicit list of abbreviations and plan on
102 supporting them in the future as well as now. */
103
104 char *nick1;
105 char *nick2;
106
107 int (*func) (); /* Function takes (argc, argv) arguments. */
108 unsigned long attr; /* Attributes. */
109 } cmds[] =
110
111 {
112 { "add", "ad", "new", add, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
113 { "admin", "adm", "rcs", admin, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
114 { "annotate", "ann", "blame", annotate, CVS_CMD_USES_WORK_DIR },
115 { "checkout", "co", "get", checkout, 0 },
116 { "commit", "ci", "com", commit, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
117 { "diff", "di", "dif", diff, CVS_CMD_USES_WORK_DIR },
118 { "edit", NULL, NULL, edit, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
119 { "editors", NULL, NULL, editors, CVS_CMD_USES_WORK_DIR },
120 { "export", "exp", "ex", checkout, CVS_CMD_USES_WORK_DIR },
121 { "history", "hi", "his", history, CVS_CMD_USES_WORK_DIR },
122 { "import", "im", "imp", import, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR | CVS_CMD_IGNORE_ADMROOT},
123 { "init", NULL, NULL, init, CVS_CMD_MODIFIES_REPOSITORY },
124 #if defined (HAVE_KERBEROS) && defined (SERVER_SUPPORT)
125 { "kserver", NULL, NULL, server, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, /* placeholder */
126 #endif
127 { "log", "lo", NULL, cvslog, CVS_CMD_USES_WORK_DIR },
128 #ifdef AUTH_CLIENT_SUPPORT
129 { "login", "logon", "lgn", login, 0 },
130 { "logout", NULL, NULL, logout, 0 },
131 #endif /* AUTH_CLIENT_SUPPORT */
132 #if (defined(AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)) && defined(SERVER_SUPPORT)
133 { "pserver", NULL, NULL, server, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, /* placeholder */
134 #endif
135 { "rannotate","rann", "ra", annotate, 0 },
136 { "rdiff", "patch", "pa", patch, 0 },
137 { "release", "re", "rel", release, 0 },
138 { "remove", "rm", "delete", cvsremove, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
139 { "rlog", "rl", NULL, cvslog, 0 },
140 { "rtag", "rt", "rfreeze", cvstag, CVS_CMD_MODIFIES_REPOSITORY },
141 #ifdef SERVER_SUPPORT
142 { "server", NULL, NULL, server, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
143 #endif
144 { "status", "st", "stat", cvsstatus, CVS_CMD_USES_WORK_DIR },
145 { "tag", "ta", "freeze", cvstag, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
146 { "unedit", NULL, NULL, unedit, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
147 { "update", "up", "upd", update, CVS_CMD_USES_WORK_DIR },
148 { "version", "ve", "ver", version, 0 },
149 { "watch", NULL, NULL, watch, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
150 { "watchers", NULL, NULL, watchers, CVS_CMD_USES_WORK_DIR },
151 { NULL, NULL, NULL, NULL, 0 },
152 };
153
154 static const char *const usg[] =
155 {
156 /* CVS usage messages never have followed the GNU convention of
157 putting metavariables in uppercase. I don't know whether that
158 is a good convention or not, but if it changes it would have to
159 change in all the usage messages. For now, they consistently
160 use lowercase, as far as I know. Puncutation is pretty funky,
161 though. Sometimes they use none, as here. Sometimes they use
162 single quotes (not the TeX-ish `' stuff), as in --help-options.
163 Sometimes they use double quotes, as in cvs -H add.
164
165 Most (not all) of the usage messages seem to have periods at
166 the end of each line. I haven't tried to duplicate this style
167 in --help as it is a rather different format from the rest. */
168
169 "Usage: %s [cvs-options] command [command-options-and-arguments]\n",
170 " where cvs-options are -q, -n, etc.\n",
171 " (specify --help-options for a list of options)\n",
172 " where command is add, admin, etc.\n",
173 " (specify --help-commands for a list of commands\n",
174 " or --help-synonyms for a list of command synonyms)\n",
175 " where command-options-and-arguments depend on the specific command\n",
176 " (specify -H followed by a command name for command-specific help)\n",
177 " Specify --help to receive this message\n",
178 "\n",
179 NULL,
180 };
181
182 static const char *const cmd_usage[] =
183 {
184 "CVS commands are:\n",
185 " add Add a new file/directory to the repository\n",
186 " admin Administration front end for rcs\n",
187 " annotate Show last revision where each line was modified\n",
188 " checkout Checkout sources for editing\n",
189 " commit Check files into the repository\n",
190 " diff Show differences between revisions\n",
191 " edit Get ready to edit a watched file\n",
192 " editors See who is editing a watched file\n",
193 " export Export sources from CVS, similar to checkout\n",
194 " history Show repository access history\n",
195 " import Import sources into CVS, using vendor branches\n",
196 " init Create a CVS repository if it doesn't exist\n",
197 #if defined (HAVE_KERBEROS) && defined (SERVER_SUPPORT)
198 " kserver Kerberos server mode\n",
199 #endif
200 " log Print out history information for files\n",
201 #ifdef AUTH_CLIENT_SUPPORT
202 " login Prompt for password for authenticating server\n",
203 " logout Removes entry in .cvspass for remote repository\n",
204 #endif /* AUTH_CLIENT_SUPPORT */
205 #if (defined(AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)) && defined(SERVER_SUPPORT)
206 " pserver Password server mode\n",
207 #endif
208 " rannotate Show last revision where each line of module was modified\n",
209 " rdiff Create 'patch' format diffs between releases\n",
210 " release Indicate that a Module is no longer in use\n",
211 " remove Remove an entry from the repository\n",
212 " rlog Print out history information for a module\n",
213 " rtag Add a symbolic tag to a module\n",
214 #ifdef SERVER_SUPPORT
215 " server Server mode\n",
216 #endif
217 " status Display status information on checked out files\n",
218 " tag Add a symbolic tag to checked out version of files\n",
219 " unedit Undo an edit command\n",
220 " update Bring work tree in sync with repository\n",
221 " version Show current CVS version(s)\n",
222 " watch Set watches\n",
223 " watchers See who is watching a file\n",
224 "(Specify the --help option for a list of other help options)\n",
225 NULL,
226 };
227
228 static const char *const opt_usage[] =
229 {
230 /* Omit -b because it is just for compatibility. */
231 "CVS global options (specified before the command name) are:\n",
232 " -H Displays usage information for command.\n",
233 " -Q Cause CVS to be really quiet.\n",
234 " -q Cause CVS to be somewhat quiet.\n",
235 " -r Make checked-out files read-only.\n",
236 " -w Make checked-out files read-write (default).\n",
237 " -l Turn history logging off.\n",
238 " -n Do not execute anything that will change the disk.\n",
239 " -t Show trace of program execution -- try with -n.\n",
240 " -v CVS version and copyright.\n",
241 " -R Read-only repository.\n",
242 " -T tmpdir Use 'tmpdir' for temporary files.\n",
243 " -e editor Use 'editor' for editing log information.\n",
244 " -d CVS_root Overrides $CVSROOT as the root of the CVS tree.\n",
245 " -f Do not use the ~/.cvsrc file.\n",
246 #ifdef CLIENT_SUPPORT
247 " -z # Use compression level '#' for net traffic.\n",
248 #ifdef ENCRYPTION
249 " -x Encrypt all net traffic.\n",
250 #endif
251 " -a Authenticate all net traffic.\n",
252 #endif
253 " -s VAR=VAL Set CVS user variable.\n",
254 "(Specify the --help option for a list of other help options)\n",
255 NULL
256 };
257
258
259 static int
set_root_directory(p,ignored)260 set_root_directory (p, ignored)
261 Node *p;
262 void *ignored;
263 {
264 if (current_root == NULL && p->data == NULL)
265 {
266 current_root = p->key;
267 return 1;
268 }
269 return 0;
270 }
271
272
273 static const char * const*
cmd_synonyms()274 cmd_synonyms ()
275 {
276 char ** synonyms;
277 char ** line;
278 const struct cmd *c = &cmds[0];
279 /* Three more for title, "specify --help" line, and NULL. */
280 int numcmds = 3;
281
282 while (c->fullname != NULL)
283 {
284 numcmds++;
285 c++;
286 }
287
288 synonyms = (char **) xmalloc(numcmds * sizeof(char *));
289 line = synonyms;
290 *line++ = "CVS command synonyms are:\n";
291 for (c = &cmds[0]; c->fullname != NULL; c++)
292 {
293 if (c->nick1 || c->nick2)
294 {
295 *line = xmalloc (strlen (c->fullname)
296 + (c->nick1 != NULL ? strlen (c->nick1) : 0)
297 + (c->nick2 != NULL ? strlen (c->nick2) : 0)
298 + 40);
299 sprintf(*line, " %-12s %s %s\n", c->fullname,
300 c->nick1 ? c->nick1 : "",
301 c->nick2 ? c->nick2 : "");
302 line++;
303 }
304 }
305 *line++ = "(Specify the --help option for a list of other help options)\n";
306 *line = NULL;
307
308 return (const char * const*) synonyms; /* will never be freed */
309 }
310
311
312 unsigned long int
lookup_command_attribute(cmd_name)313 lookup_command_attribute (cmd_name)
314 char *cmd_name;
315 {
316 const struct cmd *cm;
317
318 for (cm = cmds; cm->fullname; cm++)
319 {
320 if (strcmp (cmd_name, cm->fullname) == 0)
321 break;
322 }
323 return cm->attr;
324 }
325
326
327 static RETSIGTYPE
main_cleanup(sig)328 main_cleanup (sig)
329 int sig;
330 {
331 #ifndef DONT_USE_SIGNALS
332 const char *name;
333 char temp[10];
334
335 switch (sig)
336 {
337 #ifdef SIGABRT
338 case SIGABRT:
339 name = "abort";
340 break;
341 #endif
342 #ifdef SIGHUP
343 case SIGHUP:
344 name = "hangup";
345 break;
346 #endif
347 #ifdef SIGINT
348 case SIGINT:
349 name = "interrupt";
350 break;
351 #endif
352 #ifdef SIGQUIT
353 case SIGQUIT:
354 name = "quit";
355 break;
356 #endif
357 #ifdef SIGPIPE
358 case SIGPIPE:
359 name = "broken pipe";
360 break;
361 #endif
362 #ifdef SIGTERM
363 case SIGTERM:
364 name = "termination";
365 break;
366 #endif
367 default:
368 /* This case should never be reached, because we list above all
369 the signals for which we actually establish a signal handler. */
370 sprintf (temp, "%d", sig);
371 name = temp;
372 break;
373 }
374
375 error (1, 0, "received %s signal", name);
376 #endif /* !DONT_USE_SIGNALS */
377 }
378
379 int
main(argc,argv)380 main (argc, argv)
381 int argc;
382 char **argv;
383 {
384 char *CVSroot = CVSROOT_DFLT;
385 char *cp, *end;
386 const struct cmd *cm;
387 int c, err = 0;
388 int tmpdir_update_env, cvs_update_env;
389 int free_CVSroot = 0;
390 int free_Editor = 0;
391 int free_Tmpdir = 0;
392
393 int help = 0; /* Has the user asked for help? This
394 lets us support the `cvs -H cmd'
395 convention to give help for cmd. */
396 static const char short_options[] = "+Qqrwtnlvb:T:e:d:Hfz:s:xaR";
397 static struct option long_options[] =
398 {
399 {"help", 0, NULL, 'H'},
400 {"version", 0, NULL, 'v'},
401 {"help-commands", 0, NULL, 1},
402 {"help-synonyms", 0, NULL, 2},
403 {"help-options", 0, NULL, 4},
404 {"allow-root", required_argument, NULL, 3},
405 {0, 0, 0, 0}
406 };
407 /* `getopt_long' stores the option index here, but right now we
408 don't use it. */
409 int option_index = 0;
410
411 #ifdef SYSTEM_INITIALIZE
412 /* Hook for OS-specific behavior, for example socket subsystems on
413 NT and OS2 or dealing with windows and arguments on Mac. */
414 SYSTEM_INITIALIZE (&argc, &argv);
415 #endif
416
417 #ifdef HAVE_TZSET
418 /* On systems that have tzset (which is almost all the ones I know
419 of), it's a good idea to call it. */
420 tzset ();
421 #endif
422
423 /*
424 * Just save the last component of the path for error messages
425 */
426 program_path = xstrdup (argv[0]);
427 #ifdef ARGV0_NOT_PROGRAM_NAME
428 /* On some systems, e.g. VMS, argv[0] is not the name of the command
429 which the user types to invoke the program. */
430 program_name = "cvs";
431 #else
432 program_name = last_component (argv[0]);
433 #endif
434
435 if (pledge("stdio rpath wpath cpath fattr getpw proc exec inet dns tty", NULL) == -1)
436 error (1, errno, "pledge init");
437
438 /*
439 * Query the environment variables up-front, so that
440 * they can be overridden by command line arguments
441 */
442 cvs_update_env = 0;
443 tmpdir_update_env = *Tmpdir; /* TMPDIR_DFLT must be set */
444 if ((cp = getenv (TMPDIR_ENV)) != NULL)
445 {
446 Tmpdir = cp;
447 tmpdir_update_env = 0; /* it's already there */
448 }
449 if ((cp = getenv (EDITOR1_ENV)) != NULL)
450 Editor = cp;
451 else if ((cp = getenv (EDITOR2_ENV)) != NULL)
452 Editor = cp;
453 else if ((cp = getenv (EDITOR3_ENV)) != NULL)
454 Editor = cp;
455 if ((cp = getenv (CVSROOT_ENV)) != NULL)
456 {
457 CVSroot = cp;
458 cvs_update_env = 0; /* it's already there */
459 }
460 if (getenv (CVSREAD_ENV) != NULL)
461 cvswrite = 0;
462 if (getenv (CVSREADONLYFS_ENV)) {
463 readonlyfs = 1;
464 logoff = 1;
465 }
466
467 /* Set this to 0 to force getopt initialization. getopt() sets
468 this to 1 internally. */
469 optind = 0;
470
471 /* We have to parse the options twice because else there is no
472 chance to avoid reading the global options from ".cvsrc". Set
473 opterr to 0 for avoiding error messages about invalid options.
474 */
475 opterr = 0;
476
477 while ((c = getopt_long
478 (argc, argv, short_options, long_options, &option_index))
479 != EOF)
480 {
481 if (c == 'f')
482 use_cvsrc = 0;
483 }
484
485 /*
486 * Scan cvsrc file for global options.
487 */
488 if (use_cvsrc)
489 read_cvsrc (&argc, &argv, "cvs");
490
491 optind = 0;
492 opterr = 1;
493
494 while ((c = getopt_long
495 (argc, argv, short_options, long_options, &option_index))
496 != EOF)
497 {
498 switch (c)
499 {
500 case 1:
501 /* --help-commands */
502 usage (cmd_usage);
503 break;
504 case 2:
505 /* --help-synonyms */
506 usage (cmd_synonyms());
507 break;
508 case 4:
509 /* --help-options */
510 usage (opt_usage);
511 break;
512 case 3:
513 /* --allow-root */
514 root_allow_add (optarg);
515 break;
516 case 'Q':
517 really_quiet = 1;
518 /* FALL THROUGH */
519 case 'q':
520 quiet = 1;
521 break;
522 case 'r':
523 cvswrite = 0;
524 break;
525 case 'w':
526 cvswrite = 1;
527 break;
528 case 't':
529 trace = 1;
530 break;
531 case 'n':
532 noexec = 1;
533 case 'l': /* Fall through */
534 logoff = 1;
535 break;
536 case 'R':
537 logoff = 1;
538 readonlyfs = 1;
539 break;
540 case 'v':
541 (void) fputs ("\n", stdout);
542 version (0, (char **) NULL);
543 (void) fputs ("\n", stdout);
544 (void) fputs ("\
545 Copyright (c) 1989-2001 Brian Berliner, david d `zoo' zuhn, \n\
546 Jeff Polk, and other authors\n", stdout);
547 (void) fputs ("\n", stdout);
548 (void) fputs ("CVS may be copied only under the terms of the GNU General Public License,\n", stdout);
549 (void) fputs ("a copy of which can be found with the CVS distribution kit.\n", stdout);
550 (void) fputs ("\n", stdout);
551
552 (void) fputs ("Specify the --help option for further information about CVS\n", stdout);
553
554 exit (0);
555 break;
556 case 'b':
557 /* This option used to specify the directory for RCS
558 executables. But since we don't run them any more,
559 this is a noop. Silently ignore it so that .cvsrc
560 and scripts and inetd.conf and such can work with
561 either new or old CVS. */
562 break;
563 case 'T':
564 Tmpdir = xstrdup (optarg);
565 free_Tmpdir = 1;
566 tmpdir_update_env = 1; /* need to update environment */
567 break;
568 case 'e':
569 Editor = xstrdup (optarg);
570 free_Editor = 1;
571 break;
572 case 'd':
573 if (CVSroot_cmdline != NULL)
574 free (CVSroot_cmdline);
575 CVSroot_cmdline = xstrdup (optarg);
576 if (free_CVSroot)
577 free (CVSroot);
578 CVSroot = xstrdup (optarg);
579 free_CVSroot = 1;
580 cvs_update_env = 1; /* need to update environment */
581 break;
582 case 'H':
583 help = 1;
584 break;
585 case 'f':
586 use_cvsrc = 0; /* unnecessary, since we've done it above */
587 break;
588 case 'z':
589 #ifdef CLIENT_SUPPORT
590 gzip_level = atoi (optarg);
591 if (gzip_level < 0 || gzip_level > 9)
592 error (1, 0,
593 "gzip compression level must be between 0 and 9");
594 #endif
595 /* If no CLIENT_SUPPORT, we just silently ignore the gzip
596 level, so that users can have it in their .cvsrc and not
597 cause any trouble. */
598 break;
599 case 's':
600 variable_set (optarg);
601 break;
602 case 'x':
603 #ifdef CLIENT_SUPPORT
604 cvsencrypt = 1;
605 #endif /* CLIENT_SUPPORT */
606 /* If no CLIENT_SUPPORT, ignore -x, so that users can
607 have it in their .cvsrc and not cause any trouble.
608 If no ENCRYPTION, we still accept -x, but issue an
609 error if we are being run as a client. */
610 break;
611 case 'a':
612 #ifdef CLIENT_SUPPORT
613 cvsauthenticate = 1;
614 #endif
615 /* If no CLIENT_SUPPORT, ignore -a, so that users can
616 have it in their .cvsrc and not cause any trouble.
617 We will issue an error later if stream
618 authentication is not supported. */
619 break;
620 case '?':
621 default:
622 usage (usg);
623 }
624 }
625
626 argc -= optind;
627 argv += optind;
628 if (argc < 1)
629 usage (usg);
630
631 /* Generate the cvs global session ID */
632
633 {
634 int i = 0;
635 u_int32_t c;
636 global_session_id = xmalloc(17);
637
638 while (i <= 16) {
639 c = arc4random_uniform(75) + 48;
640 if ((c >= 48 && c <= 57) || (c >= 65 && c <= 90) ||
641 (c >= 97 && c <= 122)) {
642 global_session_id[i] = c;
643 i++;
644 }
645 }
646 global_session_id[16] = '\0';
647 }
648
649 if (trace)
650 fprintf (stderr, "main: Session ID is %s", global_session_id);
651
652
653 /* Look up the command name. */
654
655 command_name = argv[0];
656 for (cm = cmds; cm->fullname; cm++)
657 {
658 if (cm->nick1 && !strcmp (command_name, cm->nick1))
659 break;
660 if (cm->nick2 && !strcmp (command_name, cm->nick2))
661 break;
662 if (!strcmp (command_name, cm->fullname))
663 break;
664 }
665
666 if (!cm->fullname)
667 {
668 fprintf (stderr, "Unknown command: `%s'\n\n", command_name);
669 usage (cmd_usage);
670 }
671 else
672 command_name = cm->fullname; /* Global pointer for later use */
673
674 if (help)
675 {
676 argc = -1; /* some functions only check for this */
677 err = (*(cm->func)) (argc, argv);
678 }
679 else
680 {
681 /* The user didn't ask for help, so go ahead and authenticate,
682 set up CVSROOT, and the rest of it. */
683
684 /* The UMASK environment variable isn't handled with the
685 others above, since we don't want to signal errors if the
686 user has asked for help. This won't work if somebody adds
687 a command-line flag to set the umask, since we'll have to
688 parse it before we get here. */
689
690 if ((cp = getenv (CVSUMASK_ENV)) != NULL)
691 {
692 /* FIXME: Should be accepting symbolic as well as numeric mask. */
693 cvsumask = strtol (cp, &end, 8) & 0777;
694 if (*end != '\0')
695 error (1, errno, "invalid umask value in %s (%s)",
696 CVSUMASK_ENV, cp);
697 }
698
699 #if defined (HAVE_KERBEROS) && defined (SERVER_SUPPORT)
700 /* If we are invoked with a single argument "kserver", then we are
701 running as Kerberos server as root. Do the authentication as
702 the very first thing, to minimize the amount of time we are
703 running as root. */
704 if (strcmp (command_name, "kserver") == 0)
705 {
706 kserver_authenticate_connection ();
707
708 /* Pretend we were invoked as a plain server. */
709 command_name = "server";
710 }
711 #endif /* HAVE_KERBEROS */
712
713
714 #if (defined(AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)) && defined(SERVER_SUPPORT)
715 if (strcmp (command_name, "pserver") == 0)
716 {
717 /* The reason that --allow-root is not a command option
718 is mainly the comment in server() about how argc,argv
719 might be from .cvsrc. I'm not sure about that, and
720 I'm not sure it is only true of command options, but
721 it seems easier to make it a global option. */
722
723 /* Gets username and password from client, authenticates, then
724 switches to run as that user and sends an ACK back to the
725 client. */
726 pserver_authenticate_connection ();
727
728 /* Pretend we were invoked as a plain server. */
729 command_name = "server";
730 }
731 #endif /* (AUTH_SERVER_SUPPORT || HAVE_GSSAPI) && SERVER_SUPPORT */
732
733 #ifdef SERVER_SUPPORT
734 server_active = strcmp (command_name, "server") == 0;
735 if (server_active)
736 {
737 if (pledge("stdio rpath wpath cpath fattr getpw proc exec", NULL) == -1)
738 error (1, errno, "pledge");
739
740 }
741 #endif
742
743 /* This is only used for writing into the history file. For
744 remote connections, it might be nice to have hostname
745 and/or remote path, on the other hand I'm not sure whether
746 it is worth the trouble. */
747
748 #ifdef SERVER_SUPPORT
749 if (server_active)
750 CurDir = xstrdup ("<remote>");
751 else
752 #endif
753 {
754 CurDir = xgetwd ();
755 if (CurDir == NULL)
756 error (1, errno, "cannot get working directory");
757 }
758
759 if (Tmpdir == NULL || Tmpdir[0] == '\0')
760 Tmpdir = "/tmp";
761
762 #ifdef HAVE_PUTENV
763 if (tmpdir_update_env)
764 {
765 char *env;
766 env = xmalloc (strlen (TMPDIR_ENV) + strlen (Tmpdir) + 1 + 1);
767 (void) sprintf (env, "%s=%s", TMPDIR_ENV, Tmpdir);
768 (void) putenv (env);
769 /* do not free env, as putenv has control of it */
770 }
771 #endif
772
773 #ifndef DONT_USE_SIGNALS
774 /* make sure we clean up on error */
775 #ifdef SIGABRT
776 (void) SIG_register (SIGABRT, main_cleanup);
777 #endif
778 #ifdef SIGHUP
779 (void) SIG_register (SIGHUP, main_cleanup);
780 #endif
781 #ifdef SIGINT
782 (void) SIG_register (SIGINT, main_cleanup);
783 #endif
784 #ifdef SIGQUIT
785 (void) SIG_register (SIGQUIT, main_cleanup);
786 #endif
787 #ifdef SIGPIPE
788 (void) SIG_register (SIGPIPE, main_cleanup);
789 #endif
790 #ifdef SIGTERM
791 (void) SIG_register (SIGTERM, main_cleanup);
792 #endif
793 #endif /* !DONT_USE_SIGNALS */
794
795 gethostname(hostname, sizeof (hostname));
796
797 #ifdef KLUDGE_FOR_WNT_TESTSUITE
798 /* Probably the need for this will go away at some point once
799 we call fflush enough places (e.g. fflush (stdout) in
800 cvs_outerr). */
801 (void) setvbuf (stdout, (char *) NULL, _IONBF, 0);
802 (void) setvbuf (stderr, (char *) NULL, _IONBF, 0);
803 #endif /* KLUDGE_FOR_WNT_TESTSUITE */
804
805 if (use_cvsrc)
806 read_cvsrc (&argc, &argv, command_name);
807
808 #ifdef SERVER_SUPPORT
809 /* Fiddling with CVSROOT doesn't make sense if we're running
810 in server mode, since the client will send the repository
811 directory after the connection is made. */
812
813 if (!server_active)
814 #endif
815 {
816 char *CVSADM_Root;
817
818 /* See if we are able to find a 'better' value for CVSroot
819 in the CVSADM_ROOT directory. */
820
821 CVSADM_Root = NULL;
822
823 /* "cvs import" shouldn't check CVS/Root; in general it
824 ignores CVS directories and CVS/Root is likely to
825 specify a different repository than the one we are
826 importing to. */
827
828 if (!(cm->attr & CVS_CMD_IGNORE_ADMROOT)
829
830 /* -d overrides CVS/Root, so don't give an error if the
831 latter points to a nonexistent repository. */
832 && CVSroot_cmdline == NULL)
833 {
834 CVSADM_Root = Name_Root((char *) NULL, (char *) NULL);
835 }
836
837 if (CVSADM_Root != NULL)
838 {
839 if (CVSroot == NULL || !cvs_update_env)
840 {
841 CVSroot = CVSADM_Root;
842 cvs_update_env = 1; /* need to update environment */
843 }
844 }
845
846 /* Now we've reconciled CVSROOT from the command line, the
847 CVS/Root file, and the environment variable. Do the
848 last sanity checks on the variable. */
849
850 if (! CVSroot)
851 {
852 error (0, 0,
853 "No CVSROOT specified! Please use the `-d' option");
854 error (1, 0,
855 "or set the %s environment variable.", CVSROOT_ENV);
856 }
857
858 if (! *CVSroot)
859 {
860 error (0, 0,
861 "CVSROOT is set but empty! Make sure that the");
862 error (0, 0,
863 "specification of CVSROOT is legal, either via the");
864 error (0, 0,
865 "`-d' option, the %s environment variable, or the",
866 CVSROOT_ENV);
867 error (1, 0,
868 "CVS/Root file (if any).");
869 }
870 }
871
872 /* Here begins the big loop over unique cvsroot values. We
873 need to call do_recursion once for each unique value found
874 in CVS/Root. Prime the list with the current value. */
875
876 /* Create the list. */
877 assert (root_directories == NULL);
878 root_directories = getlist ();
879
880 /* Prime it. */
881 if (CVSroot != NULL)
882 {
883 Node *n;
884 n = getnode ();
885 n->type = NT_UNKNOWN;
886 n->key = xstrdup (CVSroot);
887 n->data = NULL;
888
889 if (addnode (root_directories, n))
890 error (1, 0, "cannot add initial CVSROOT %s", n->key);
891 }
892
893 assert (current_root == NULL);
894
895 /* If we're running the server, we want to execute this main
896 loop once and only once (we won't be serving multiple roots
897 from this connection, so there's no need to do it more than
898 once). To get out of the loop, we perform a "break" at the
899 end of things. */
900
901 while (
902 #ifdef SERVER_SUPPORT
903 server_active ||
904 #endif
905 walklist (root_directories, set_root_directory, NULL)
906 )
907 {
908 #ifdef SERVER_SUPPORT
909 /* Fiddling with CVSROOT doesn't make sense if we're running
910 in server mode, since the client will send the repository
911 directory after the connection is made. */
912
913 if (!server_active)
914 #endif
915 {
916 /* Now we're 100% sure that we have a valid CVSROOT
917 variable. Parse it to see if we're supposed to do
918 remote accesses or use a special access method. */
919
920 if (current_parsed_root != NULL)
921 free_cvsroot_t (current_parsed_root);
922 if ((current_parsed_root = parse_cvsroot (current_root)) == NULL)
923 error (1, 0, "Bad CVSROOT.");
924
925 if (current_parsed_root->method == pserver_method) {
926 if (strcmp(command_name, "login") == 0) {
927 if (pledge("stdio rpath wpath cpath fattr getpw inet dns tty", NULL) == -1)
928 error (1, errno, "pledge");
929 } else {
930 if (pledge("stdio rpath wpath cpath fattr getpw proc exec inet dns", NULL) == -1)
931 error (1, errno, "pledge");
932 }
933 } else {
934 if (pledge("stdio rpath wpath cpath fattr getpw proc exec", NULL) == -1)
935 error (1, errno, "pledge");
936 }
937
938 if (trace)
939 fprintf (stderr, "%s-> main loop with CVSROOT=%s\n",
940 CLIENT_SERVER_STR, current_root);
941
942 /*
943 * Check to see if the repository exists.
944 */
945 #ifdef CLIENT_SUPPORT
946 if (!current_parsed_root->isremote)
947 #endif /* CLIENT_SUPPORT */
948 {
949 char *path;
950 int save_errno;
951
952 path = xmalloc (strlen (current_parsed_root->directory)
953 + sizeof (CVSROOTADM)
954 + 20);
955 (void) sprintf (path, "%s/%s", current_parsed_root->directory, CVSROOTADM);
956 if (readonlyfs == 0 && !isaccessible (path, R_OK | X_OK))
957 {
958 save_errno = errno;
959 /* If this is "cvs init", the root need not exist yet. */
960 if (strcmp (command_name, "init") != 0)
961 {
962 error (1, save_errno, "%s", path);
963 }
964 }
965 free (path);
966 }
967
968 #ifdef HAVE_PUTENV
969 /* Update the CVSROOT environment variable if necessary. */
970 /* FIXME (njc): should we always set this with the CVSROOT from the command line? */
971 if (cvs_update_env)
972 {
973 static char *prev;
974 char *env;
975 env = xmalloc (strlen (CVSROOT_ENV) + strlen (CVSroot)
976 + 1 + 1);
977 (void) sprintf (env, "%s=%s", CVSROOT_ENV, CVSroot);
978 (void) putenv (env);
979 /* do not free env yet, as putenv has control of it */
980 /* but do free the previous value, if any */
981 if (prev != NULL)
982 free (prev);
983 prev = env;
984 }
985 #endif
986 }
987
988 /* Parse the CVSROOT/config file, but only for local. For the
989 server, we parse it after we know $CVSROOT. For the
990 client, it doesn't get parsed at all, obviously. The
991 presence of the parse_config call here is not mean to
992 predetermine whether CVSROOT/config overrides things from
993 read_cvsrc and other such places or vice versa. That sort
994 of thing probably needs more thought. */
995 if (1
996 #ifdef SERVER_SUPPORT
997 && !server_active
998 #endif
999 #ifdef CLIENT_SUPPORT
1000 && !current_parsed_root->isremote
1001 #endif
1002 )
1003 {
1004 /* If there was an error parsing the config file, parse_config
1005 already printed an error. We keep going. Why? Because
1006 if we didn't, then there would be no way to check in a new
1007 CVSROOT/config file to fix the broken one! */
1008 parse_config (current_parsed_root->directory);
1009 }
1010
1011 #ifdef CLIENT_SUPPORT
1012 /* Need to check for current_parsed_root != NULL here since
1013 * we could still be in server mode before the server function
1014 * gets called below and sets the root
1015 */
1016 if (current_parsed_root != NULL && current_parsed_root->isremote)
1017 {
1018 /* Create a new list for directory names that we've
1019 sent to the server. */
1020 if (dirs_sent_to_server != NULL)
1021 dellist (&dirs_sent_to_server);
1022 dirs_sent_to_server = getlist ();
1023 }
1024 #endif
1025
1026 err = (*(cm->func)) (argc, argv);
1027
1028 /* Mark this root directory as done. When the server is
1029 active, current_root will be NULL -- don't try and
1030 remove it from the list. */
1031
1032 if (current_root != NULL)
1033 {
1034 Node *n = findnode (root_directories, current_root);
1035 assert (n != NULL);
1036 n->data = (void *) 1;
1037 current_root = NULL;
1038 }
1039
1040 #if 0
1041 /* This will not work yet, since it tries to free (void *) 1. */
1042 dellist (&root_directories);
1043 #endif
1044
1045 #ifdef SERVER_SUPPORT
1046 if (server_active)
1047 break;
1048 #endif
1049 } /* end of loop for cvsroot values */
1050
1051 } /* end of stuff that gets done if the user DOESN'T ask for help */
1052
1053 Lock_Cleanup ();
1054
1055 free (program_path);
1056 if (CVSroot_cmdline != NULL)
1057 free (CVSroot_cmdline);
1058 if (free_CVSroot)
1059 free (CVSroot);
1060 if (free_Editor)
1061 free (Editor);
1062 if (free_Tmpdir)
1063 free (Tmpdir);
1064 root_allow_free ();
1065
1066 #ifdef SYSTEM_CLEANUP
1067 /* Hook for OS-specific behavior, for example socket subsystems on
1068 NT and OS2 or dealing with windows and arguments on Mac. */
1069 SYSTEM_CLEANUP ();
1070 #endif
1071
1072 /* This is exit rather than return because apparently that keeps
1073 some tools which check for memory leaks happier. */
1074 exit (err ? EXIT_FAILURE : 0);
1075 /* Keep picky/stupid compilers (e.g. Visual C++ 5.0) happy. */
1076 return 0;
1077 }
1078
1079 char *
Make_Date(rawdate)1080 Make_Date (rawdate)
1081 char *rawdate;
1082 {
1083 time_t unixtime;
1084
1085 unixtime = get_date (rawdate);
1086 if (unixtime == (time_t) - 1)
1087 error (1, 0, "Can't parse date/time: %s", rawdate);
1088 return date_from_time_t (unixtime);
1089 }
1090
1091 /* Convert a time_t to an RCS format date. This is mainly for the
1092 use of "cvs history", because the CVSROOT/history file contains
1093 time_t format dates; most parts of CVS will want to avoid using
1094 time_t's directly, and instead use RCS_datecmp, Make_Date, &c.
1095 Assuming that the time_t is in GMT (as it generally should be),
1096 then the result will be in GMT too.
1097
1098 Returns a newly malloc'd string. */
1099
1100 char *
date_from_time_t(unixtime)1101 date_from_time_t (unixtime)
1102 time_t unixtime;
1103 {
1104 struct tm *ftm;
1105 char date[MAXDATELEN];
1106 char *ret;
1107
1108 ftm = gmtime (&unixtime);
1109 if (ftm == NULL)
1110 /* This is a system, like VMS, where the system clock is in local
1111 time. Hopefully using localtime here matches the "zero timezone"
1112 hack I added to get_date (get_date of course being the relevant
1113 issue for Make_Date, and for history.c too I think). */
1114 ftm = localtime (&unixtime);
1115
1116 (void) sprintf (date, DATEFORM,
1117 ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
1118 ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
1119 ftm->tm_min, ftm->tm_sec);
1120 ret = xstrdup (date);
1121 return (ret);
1122 }
1123
1124 /* Convert a date to RFC822/1123 format. This is used in contexts like
1125 dates to send in the protocol; it should not vary based on locale or
1126 other such conventions for users. We should have another routine which
1127 does that kind of thing.
1128
1129 The SOURCE date is in our internal RCS format. DEST should point to
1130 storage managed by the caller, at least MAXDATELEN characters. */
1131 void
date_to_internet(dest,source)1132 date_to_internet (dest, source)
1133 char *dest;
1134 const char *source;
1135 {
1136 struct tm date;
1137
1138 date_to_tm (&date, source);
1139 tm_to_internet (dest, &date);
1140 }
1141
1142 void
date_to_tm(dest,source)1143 date_to_tm (dest, source)
1144 struct tm *dest;
1145 const char *source;
1146 {
1147 if (sscanf (source, SDATEFORM,
1148 &dest->tm_year, &dest->tm_mon, &dest->tm_mday,
1149 &dest->tm_hour, &dest->tm_min, &dest->tm_sec)
1150 != 6)
1151 /* Is there a better way to handle errors here? I made this
1152 non-fatal in case we are called from the code which can't
1153 deal with fatal errors. */
1154 error (0, 0, "internal error: bad date %s", source);
1155
1156 if (dest->tm_year > 100)
1157 dest->tm_year -= 1900;
1158
1159 dest->tm_mon -= 1;
1160 }
1161
1162 /* Convert a date to RFC822/1123 format. This is used in contexts like
1163 dates to send in the protocol; it should not vary based on locale or
1164 other such conventions for users. We should have another routine which
1165 does that kind of thing.
1166
1167 The SOURCE date is a pointer to a struct tm. DEST should point to
1168 storage managed by the caller, at least MAXDATELEN characters. */
1169 void
tm_to_internet(dest,source)1170 tm_to_internet (dest, source)
1171 char *dest;
1172 const struct tm *source;
1173 {
1174 /* Just to reiterate, these strings are from RFC822 and do not vary
1175 according to locale. */
1176 static const char *const month_names[] =
1177 {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
1178 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
1179
1180 sprintf (dest, "%d %s %d %02d:%02d:%02d -0000", source->tm_mday,
1181 source->tm_mon < 0 || source->tm_mon > 11 ? "???" : month_names[source->tm_mon],
1182 source->tm_year + 1900, source->tm_hour, source->tm_min, source->tm_sec);
1183 }
1184
1185 void
usage(cpp)1186 usage (cpp)
1187 register const char *const *cpp;
1188 {
1189 (void) fprintf (stderr, *cpp++, program_name, command_name);
1190 for (; *cpp; cpp++)
1191 (void) fprintf (stderr, *cpp);
1192 error_exit();
1193 }
1194