2 * Copyright (c) 1992, Brian Berliner and Jeff Polk
3 * Copyright (c) 1989-1992, Brian Berliner
5 * You may distribute under the terms of the GNU General Public License as
6 * specified in the README file that comes with the CVS source distribution.
8 * Administration ("cvs admin")
13 #ifdef CVS_ADMIN_GROUP
17 static Dtype admin_dirproc (void *callerdat, const char *dir,
18 const char *repos, const char *update_dir,
20 static int admin_fileproc (void *callerdat, struct file_info *finfo);
22 static const char *const admin_usage[] =
24 "Usage: %s %s [options] files...\n",
25 "\t-a users Append (comma-separated) user names to access list.\n",
26 "\t-A file Append another file's access list.\n",
27 "\t-b[rev] Set default branch (highest branch on trunk if omitted).\n",
28 "\t-c string Set comment leader.\n",
29 "\t-e[users] Remove (comma-separated) user names from access list\n",
30 "\t (all names if omitted).\n",
31 "\t-I Run interactively.\n",
32 "\t-k subst Set keyword substitution mode:\n",
33 "\t kv (Default) Substitute keyword and value.\n",
34 "\t kvl Substitute keyword, value, and locker (if any).\n",
35 "\t k Substitute keyword only.\n",
36 "\t o Preserve original string.\n",
37 "\t b Like o, but mark file as binary.\n",
38 "\t v Substitute value only.\n",
39 "\t-l[rev] Lock revision (latest revision on branch,\n",
40 "\t latest revision on trunk if omitted).\n",
41 "\t-L Set strict locking.\n",
42 "\t-m rev:msg Replace revision's log message.\n",
43 "\t-n tag[:[rev]] Tag branch or revision. If :rev is omitted,\n",
44 "\t delete the tag; if rev is omitted, tag the latest\n",
45 "\t revision on the default branch.\n",
46 "\t-N tag[:[rev]] Same as -n except override existing tag.\n",
47 "\t-o range Delete (outdate) specified range of revisions:\n",
48 "\t rev1:rev2 Between rev1 and rev2, including rev1 and rev2.\n",
49 "\t rev1::rev2 Between rev1 and rev2, excluding rev1 and rev2.\n",
50 "\t rev: rev and following revisions on the same branch.\n",
51 "\t rev:: After rev on the same branch.\n",
52 "\t :rev rev and previous revisions on the same branch.\n",
53 "\t ::rev Before rev on the same branch.\n",
55 "\t-q Run quietly.\n",
56 "\t-s state[:rev] Set revision state (latest revision on branch,\n",
57 "\t latest revision on trunk if omitted).\n",
58 "\t-t[file] Get descriptive text from file (stdin if omitted).\n",
59 "\t-t-string Set descriptive text.\n",
60 "\t-u[rev] Unlock the revision (latest revision on branch,\n",
61 "\t latest revision on trunk if omitted).\n",
62 "\t-U Unset strict locking.\n",
63 "(Specify the --help global option for a list of other help options)\n",
67 /* This structure is used to pass information through start_recursion. */
70 /* Set default branch (-b). It is "-b" followed by the value
71 given, or NULL if not specified, or merely "-b" if -b is
72 specified without a value. */
75 /* Set comment leader (-c). It is "-c" followed by the value
76 given, or NULL if not specified. The comment leader is
77 relevant only for old versions of RCS, but we let people set it
81 /* Set strict locking (-L). */
84 /* Set nonstrict locking (-U). */
87 /* Delete revisions (-o). It is "-o" followed by the value specified. */
90 /* Keyword substitution mode (-k), e.g. "-kb". */
93 /* Description (-t). */
96 /* Interactive (-I). Problematic with client/server. */
99 /* This is the cheesy part. It is a vector with the options which
100 we don't deal with above (e.g. "-afoo" "-abar,baz"). In the future
101 this presumably will be replaced by other variables which break
102 out the data in a more convenient fashion. AV as well as each of
103 the strings it points to is malloc'd. */
109 /* Add an argument. OPT is the option letter, e.g. 'a'. ARG is the
110 argument to that option, or NULL if omitted (whether NULL can actually
111 happen depends on whether the option was specified as optional to
114 arg_add (struct admin_data *dat, int opt, char *arg)
116 char *newelt = xmalloc ((arg == NULL ? 0 : strlen (arg)) + 3);
117 strcpy (newelt, "-");
122 strcpy (newelt + 2, arg);
124 if (dat->av_alloc == 0)
127 dat->av = (char **) xmalloc (dat->av_alloc * sizeof (*dat->av));
129 else if (dat->ac >= dat->av_alloc)
132 dat->av = (char **) xrealloc (dat->av,
133 dat->av_alloc * sizeof (*dat->av));
135 dat->av[dat->ac++] = newelt;
141 * callback proc to run a script when admin finishes.
144 postadmin_proc (const char *repository, const char *filter, void *closure)
147 const char *srepos = Short_Repository (repository);
149 TRACE (TRACE_FUNCTION, "postadmin_proc (%s, %s)", repository, filter);
156 cmdline = format_cmdline (
157 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
159 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
161 "c", "s", cvs_cmd_name,
162 #ifdef SERVER_SUPPORT
163 "R", "s", referrer ? referrer->original : "NONE",
164 #endif /* SERVER_SUPPORT */
166 "r", "s", current_parsed_root->directory,
170 if (!cmdline || !strlen (cmdline))
172 if (cmdline) free (cmdline);
173 error (0, 0, "postadmin proc resolved to the empty string!");
181 /* FIXME - read the comment in verifymsg_proc() about why we use abs()
182 * below() and shouldn't.
184 return abs (run_exec (RUN_TTY, RUN_TTY, RUN_TTY,
185 RUN_NORMAL | RUN_SIGIGNORE));
191 * Call any postadmin procs.
194 admin_filesdoneproc (void *callerdat, int err, const char *repository,
195 const char *update_dir, List *entries)
197 TRACE (TRACE_FUNCTION, "admin_filesdoneproc (%d, %s, %s)", err, repository,
199 Parse_Info (CVSROOTADM_POSTADMIN, repository, postadmin_proc, PIOPT_ALL,
208 admin (int argc, char **argv)
211 #ifdef CVS_ADMIN_GROUP
213 struct group *getgrnam(const char *);
215 struct admin_data admin_data;
218 bool only_allowed_options;
225 memset (&admin_data, 0, sizeof admin_data);
227 /* TODO: get rid of `-' switch notation in admin_data. For
228 example, admin_data->branch should be not `-bfoo' but simply `foo'. */
231 only_allowed_options = true;
232 while ((c = getopt (argc, argv,
233 "+ib::c:a:A:e::l::u::LUn:N:m:o:s:t::IqxV:k:")) != -1)
236 # ifdef CLIENT_SUPPORT
237 !current_parsed_root->isremote &&
238 # endif /* CLIENT_SUPPORT */
239 c != 'q' && !strchr (config->UserAdminOptions, c)
241 only_allowed_options = false;
246 /* This has always been documented as useless in cvs.texinfo
247 and it really is--admin_fileproc silently does nothing
248 if vers->vn_user is NULL. */
249 error (0, 0, "the -i option to admin is not supported");
250 error (0, 0, "run add or import to create an RCS file");
254 if (admin_data.branch != NULL)
256 error (0, 0, "duplicate 'b' option");
260 admin_data.branch = xstrdup ("-b");
263 admin_data.branch = xmalloc (strlen (optarg) + 5);
264 strcpy (admin_data.branch, "-b");
265 strcat (admin_data.branch, optarg);
270 if (admin_data.comment != NULL)
272 error (0, 0, "duplicate 'c' option");
275 admin_data.comment = xmalloc (strlen (optarg) + 5);
276 strcpy (admin_data.comment, "-c");
277 strcat (admin_data.comment, optarg);
281 arg_add (&admin_data, 'a', optarg);
285 /* In the client/server case, this is cheesy because
286 we just pass along the name of the RCS file, which
287 then will want to exist on the server. This is
288 accidental; having the client specify a pathname on
289 the server is not a design feature of the protocol. */
290 arg_add (&admin_data, 'A', optarg);
294 arg_add (&admin_data, 'e', optarg);
298 /* Note that multiple -l options are valid. */
299 arg_add (&admin_data, 'l', optarg);
303 /* Note that multiple -u options are valid. */
304 arg_add (&admin_data, 'u', optarg);
308 /* Probably could also complain if -L is specified multiple
309 times, although RCS doesn't and I suppose it is reasonable
310 just to have it mean the same as a single -L. */
311 if (admin_data.set_nonstrict)
313 error (0, 0, "-U and -L are incompatible");
316 admin_data.set_strict = 1;
320 /* Probably could also complain if -U is specified multiple
321 times, although RCS doesn't and I suppose it is reasonable
322 just to have it mean the same as a single -U. */
323 if (admin_data.set_strict)
325 error (0, 0, "-U and -L are incompatible");
328 admin_data.set_nonstrict = 1;
332 /* Mostly similar to cvs tag. Could also be parsing
333 the syntax of optarg, although for now we just pass
334 it to rcs as-is. Note that multiple -n options are
336 arg_add (&admin_data, 'n', optarg);
340 /* Mostly similar to cvs tag. Could also be parsing
341 the syntax of optarg, although for now we just pass
342 it to rcs as-is. Note that multiple -N options are
344 arg_add (&admin_data, 'N', optarg);
348 /* Change log message. Could also be parsing the syntax
349 of optarg, although for now we just pass it to rcs
350 as-is. Note that multiple -m options are valid. */
351 arg_add (&admin_data, 'm', optarg);
355 /* Delete revisions. Probably should also be parsing the
356 syntax of optarg, so that the client can give errors
357 rather than making the server take care of that.
358 Other than that I'm not sure whether it matters much
359 whether we parse it here or in admin_fileproc.
361 Note that multiple -o options are invalid, in RCS
364 if (admin_data.delete_revs != NULL)
366 error (0, 0, "duplicate '-o' option");
369 admin_data.delete_revs = xmalloc (strlen (optarg) + 5);
370 strcpy (admin_data.delete_revs, "-o");
371 strcat (admin_data.delete_revs, optarg);
375 /* Note that multiple -s options are valid. */
376 arg_add (&admin_data, 's', optarg);
380 if (admin_data.desc != NULL)
382 error (0, 0, "duplicate 't' option");
385 if (optarg != NULL && optarg[0] == '-')
386 admin_data.desc = xstrdup (optarg + 1);
392 get_file (optarg, optarg, "r", &admin_data.desc,
398 /* At least in RCS this can be specified several times,
399 with the same meaning as being specified once. */
400 admin_data.interactive = 1;
404 /* Silently set the global really_quiet flag. This keeps admin in
405 * sync with the RCS man page and allows us to silently support
406 * older servers when necessary.
408 * Some logic says we might want to output a deprecation warning
409 * here, but I'm opting not to in order to stay quietly in sync
410 * with the RCS man page.
416 error (0, 0, "the -x option has never done anything useful");
417 error (0, 0, "RCS files in CVS always end in ,v");
421 /* No longer supported. */
422 error (0, 0, "the `-V' option is obsolete");
426 if (admin_data.kflag != NULL)
428 error (0, 0, "duplicate '-k' option");
431 admin_data.kflag = RCS_check_kflag (optarg);
435 /* getopt will have printed an error message. */
438 /* Don't use cvs_cmd_name; it might be "server". */
439 error (1, 0, "specify %s -H admin for usage information",
446 #ifdef CVS_ADMIN_GROUP
447 /* The use of `cvs admin -k' is unrestricted. However, any other
448 option is restricted if the group CVS_ADMIN_GROUP exists on the
451 # ifdef CLIENT_SUPPORT
452 /* This is only "secure" on the server, since the user could edit the
453 * RCS file on a local host, but some people like this kind of
454 * check anyhow. The alternative would be to check only when
455 * (server_active) rather than when not on the client.
457 !current_parsed_root->isremote &&
458 # endif /* CLIENT_SUPPORT */
459 !only_allowed_options
460 && (grp = getgrnam (CVS_ADMIN_GROUP)) != NULL)
462 #ifdef HAVE_GETGROUPS
466 /* get number of auxiliary groups */
467 n = getgroups (0, NULL);
469 error (1, errno, "unable to get number of auxiliary groups");
470 grps = xmalloc ((n + 1) * sizeof *grps);
471 n = getgroups (n, grps);
473 error (1, errno, "unable to get list of auxiliary groups");
475 for (i = 0; i <= n; i++)
476 if (grps[i] == grp->gr_gid) break;
479 error (1, 0, "usage is restricted to members of the group %s",
482 char *me = getcaller();
485 for (grnam = grp->gr_mem; *grnam; grnam++)
486 if (strcmp (*grnam, me) == 0) break;
487 if (!*grnam && getgid() != grp->gr_gid)
488 error (1, 0, "usage is restricted to members of the group %s",
492 #endif /* defined CVS_ADMIN_GROUP */
494 for (i = 0; i < admin_data.ac; ++i)
496 assert (admin_data.av[i][0] == '-');
497 switch (admin_data.av[i][1])
502 check_numeric (&admin_data.av[i][2], argc, argv);
508 if (admin_data.branch != NULL)
509 check_numeric (admin_data.branch + 2, argc, argv);
510 if (admin_data.delete_revs != NULL)
514 check_numeric (admin_data.delete_revs + 2, argc, argv);
515 p = strchr (admin_data.delete_revs + 2, ':');
516 if (p != NULL && isdigit ((unsigned char) p[1]))
517 check_numeric (p + 1, argc, argv);
518 else if (p != NULL && p[1] == ':' && isdigit ((unsigned char) p[2]))
519 check_numeric (p + 2, argc, argv);
522 #ifdef CLIENT_SUPPORT
523 if (current_parsed_root->isremote)
525 /* We're the client side. Fire up the remote server. */
530 /* Note that option_with_arg does not work for us, because some
531 of the options must be sent without a space between the option
533 if (admin_data.interactive)
534 error (1, 0, "-I option not useful with client/server");
535 if (admin_data.branch != NULL)
536 send_arg (admin_data.branch);
537 if (admin_data.comment != NULL)
538 send_arg (admin_data.comment);
539 if (admin_data.set_strict)
541 if (admin_data.set_nonstrict)
543 if (admin_data.delete_revs != NULL)
544 send_arg (admin_data.delete_revs);
545 if (admin_data.desc != NULL)
547 char *p = admin_data.desc;
548 send_to_server ("Argument -t-", 0);
553 send_to_server ("\012Argumentx ", 0);
558 char *q = strchr (p, '\n');
559 if (q == NULL) q = p + strlen (p);
560 send_to_server (p, q - p);
564 send_to_server ("\012", 1);
566 /* Send this for all really_quiets since we know that it will be silently
567 * ignored when unneeded. This supports old servers.
571 if (admin_data.kflag != NULL)
572 send_arg (admin_data.kflag);
574 for (i = 0; i < admin_data.ac; ++i)
575 send_arg (admin_data.av[i]);
578 send_files (argc, argv, 0, 0, SEND_NO_CONTENTS);
579 send_file_names (argc, argv, SEND_EXPAND_WILD);
580 send_to_server ("admin\012", 0);
581 err = get_responses_and_close ();
584 #endif /* CLIENT_SUPPORT */
586 lock_tree_promotably (argc, argv, 0, W_LOCAL, 0);
588 err = start_recursion
589 (admin_fileproc, admin_filesdoneproc, admin_dirproc,
592 W_LOCAL, 0, CVS_LOCK_WRITE, NULL, 1, NULL);
597 if (admin_data.branch != NULL)
598 free (admin_data.branch);
599 if (admin_data.comment != NULL)
600 free (admin_data.comment);
601 if (admin_data.delete_revs != NULL)
602 free (admin_data.delete_revs);
603 if (admin_data.kflag != NULL)
604 free (admin_data.kflag);
605 if (admin_data.desc != NULL)
606 free (admin_data.desc);
607 for (i = 0; i < admin_data.ac; ++i)
608 free (admin_data.av[i]);
609 if (admin_data.av != NULL)
610 free (admin_data.av);
618 * Called to run "rcs" on a particular file.
622 admin_fileproc (void *callerdat, struct file_info *finfo)
624 struct admin_data *admin_data = (struct admin_data *) callerdat;
631 vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
633 version = vers->vn_user;
634 if (version != NULL && strcmp (version, "0") == 0)
636 error (0, 0, "cannot admin newly added file `%s'", finfo->file);
645 error (0, 0, "nothing known about %s", finfo->file);
650 if (rcs->flags & PARTIAL)
651 RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
655 cvs_output ("RCS file: ", 0);
656 cvs_output (rcs->path, 0);
657 cvs_output ("\n", 1);
660 if (admin_data->branch != NULL)
662 char *branch = &admin_data->branch[2];
663 if (*branch != '\0' && ! isdigit ((unsigned char) *branch))
665 branch = RCS_whatbranch (rcs, admin_data->branch + 2);
668 error (0, 0, "%s: Symbolic name %s is undefined.",
669 rcs->path, admin_data->branch + 2);
674 RCS_setbranch (rcs, branch);
675 if (branch != NULL && branch != &admin_data->branch[2])
678 if (admin_data->comment != NULL)
680 if (rcs->comment != NULL)
682 rcs->comment = xstrdup (admin_data->comment + 2);
684 if (admin_data->set_strict)
685 rcs->strict_locks = 1;
686 if (admin_data->set_nonstrict)
687 rcs->strict_locks = 0;
688 if (admin_data->delete_revs != NULL)
690 char *s, *t, *rev1, *rev2;
691 /* Set for :, clear for ::. */
695 s = admin_data->delete_revs + 2;
709 /* Note that we don't support '-' for ranges. RCS considers it
710 obsolete and it is problematic with tags containing '-'. "cvs log"
711 has made the same decision. */
729 *t = ':'; /* probably unnecessary */
738 if (rev1 == NULL && rev2 == NULL)
740 /* RCS segfaults if `-o:' is given */
741 error (0, 0, "no valid revisions specified in `%s' option",
742 admin_data->delete_revs);
747 status |= RCS_delete_revs (rcs, rev1, rev2, inclusive);
754 if (admin_data->desc != NULL)
757 rcs->desc = xstrdup (admin_data->desc);
759 if (admin_data->kflag != NULL)
761 char *kflag = admin_data->kflag + 2;
762 char *oldexpand = RCS_getexpand (rcs);
763 if (oldexpand == NULL || strcmp (oldexpand, kflag) != 0)
764 RCS_setexpand (rcs, kflag);
767 /* Handle miscellaneous options. TODO: decide whether any or all
768 of these should have their own fields in the admin_data
770 for (i = 0; i < admin_data->ac; ++i)
773 char *p, *rev, *revnum, *tag, *msg;
779 arg = admin_data->av[i];
782 case 'a': /* fall through */
784 line2argv (&argc, &users, arg + 2, " ,\t\n");
786 for (u = 0; u < argc; ++u)
787 RCS_addaccess (rcs, users[u]);
789 RCS_delaccess (rcs, NULL);
791 for (u = 0; u < argc; ++u)
792 RCS_delaccess (rcs, users[u]);
793 free_names (&argc, users);
797 /* See admin-19a-admin and friends in sanity.sh for
798 relative pathnames. It makes sense to think in
799 terms of a syntax which give pathnames relative to
800 the repository or repository corresponding to the
801 current directory or some such (and perhaps don't
802 include ,v), but trying to worry about such things
803 is a little pointless unless you first worry about
804 whether "cvs admin -A" as a whole makes any sense
805 (currently probably not, as access lists don't
806 affect the behavior of CVS). */
808 rcs2 = RCS_parsercsfile (arg + 2);
810 error (1, 0, "cannot continue");
812 p = xstrdup (RCS_getaccess (rcs2));
813 line2argv (&argc, &users, p, " \t\n");
817 for (u = 0; u < argc; ++u)
818 RCS_addaccess (rcs, users[u]);
819 free_names (&argc, users);
821 case 'n': /* fall through */
825 cvs_outerr ("missing symbolic name after ", 0);
827 cvs_outerr ("\n", 1);
830 p = strchr (arg, ':');
833 if (RCS_deltag (rcs, arg + 2) != 0)
835 error (0, 0, "%s: Symbolic name %s is undefined.",
844 tag = xstrdup (arg + 2);
847 /* Option `n' signals an error if this tag is already bound. */
850 n = findnode (RCS_symbols (rcs), tag);
854 "%s: symbolic name %s already bound to %s",
856 tag, (char *)n->data);
863 /* Attempt to perform the requested tagging. */
865 if ((*p == 0 && (rev = RCS_head (rcs)))
866 || (rev = RCS_tag2rev (rcs, p))) /* tag2rev may exit */
868 RCS_check_tag (tag); /* exit if not a valid tag */
869 RCS_settag (rcs, tag, rev);
876 "%s: Symbolic name or revision %s is undefined.",
883 p = strchr (arg, ':');
886 tag = xstrdup (arg + 2);
887 rev = RCS_head (rcs);
892 tag = xstrdup (arg + 2);
896 revnum = RCS_gettag (rcs, rev, 0, NULL);
899 n = findnode (rcs->versions, revnum);
907 "%s: can't set state of nonexisting revision %s",
921 p = strchr (arg, ':');
924 error (0, 0, "%s: -m option lacks revision number",
929 *p = '\0'; /* temporarily make arg+2 its own string */
930 rev = RCS_gettag (rcs, arg + 2, 1, NULL); /* Force tag match */
933 error (0, 0, "%s: no such revision %s", rcs->path, arg+2);
935 *p = ':'; /* restore the full text of the -m argument */
940 n = findnode (rcs->versions, rev);
941 /* tags may exist against non-existing versions */
944 error (0, 0, "%s: no such revision %s: %s",
945 rcs->path, arg+2, rev);
947 *p = ':'; /* restore the full text of the -m argument */
951 *p = ':'; /* restore the full text of the -m argument */
955 if (delta->text == NULL)
957 delta->text = (Deltatext *) xmalloc (sizeof (Deltatext));
958 memset ((void *) delta->text, 0, sizeof (Deltatext));
960 delta->text->version = xstrdup (delta->version);
961 delta->text->log = make_message_rcsvalid (msg);
965 status |= RCS_lock (rcs, arg[2] ? arg + 2 : NULL, 0);
968 status |= RCS_unlock (rcs, arg[2] ? arg + 2 : NULL, 0);
970 default: assert(0); /* can't happen */
976 RCS_rewrite (rcs, NULL, NULL);
978 cvs_output ("done\n", 5);
982 /* Note that this message should only occur after another
983 message has given a more specific error. The point of this
984 additional message is to make it clear that the previous problems
985 caused CVS to forget about the idea of modifying the RCS file. */
987 error (0, 0, "RCS file for `%s' not modified.", finfo->file);
999 * Print a warm fuzzy message
1003 admin_dirproc (void *callerdat, const char *dir, const char *repos,
1004 const char *update_dir, List *entries)
1007 error (0, 0, "Administrating %s", update_dir);