2 * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
4 * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
7 * Portions Copyright (c) 1992, Brian Berliner and Jeff Polk
8 * Portions Copyright (c) 1989-1992, Brian Berliner
10 * You may distribute under the terms of the GNU General Public License as
11 * specified in the README file that comes with the CVS source distribution.
13 * Administration ("cvs admin")
18 #ifdef CVS_ADMIN_GROUP
22 __RCSID("$MirOS: src/gnu/usr.bin/cvs/src/admin.c,v 1.5 2010/09/19 19:43:01 tg Exp $");
24 static Dtype admin_dirproc (void *callerdat, const char *dir,
25 const char *repos, const char *update_dir,
27 static int admin_fileproc (void *callerdat, struct file_info *finfo);
29 static const char *const admin_usage[] =
31 "Usage: %s %s [options] files...\n",
32 "\t-a users Append (comma-separated) user names to access list.\n",
33 "\t-A file Append another file's access list.\n",
34 "\t-b[rev] Set default branch (highest branch on trunk if omitted).\n",
35 "\t-c string Set comment leader.\n",
36 "\t-e[users] Remove (comma-separated) user names from access list\n",
37 "\t (all names if omitted).\n",
38 "\t-I Run interactively.\n",
39 "\t-k subst Set keyword substitution mode:\n",
40 "\t kv (Default) Substitute keyword and value.\n",
41 "\t kvl Substitute keyword, value, and locker (if any).\n",
42 "\t k Substitute keyword only.\n",
43 "\t o Preserve original string.\n",
44 "\t b Like o, but mark file as binary.\n",
45 "\t v Substitute value only.\n",
46 "\t-l[rev] Lock revision (latest revision on branch,\n",
47 "\t latest revision on trunk if omitted).\n",
48 "\t-L Set strict locking.\n",
49 "\t-m rev:msg Replace revision's log message.\n",
50 "\t-n tag[:[rev]] Tag branch or revision. If :rev is omitted,\n",
51 "\t delete the tag; if rev is omitted, tag the latest\n",
52 "\t revision on the default branch.\n",
53 "\t-N tag[:[rev]] Same as -n except override existing tag.\n",
54 "\t-o range Delete (outdate) specified range of revisions:\n",
55 "\t rev1:rev2 Between rev1 and rev2, including rev1 and rev2.\n",
56 "\t rev1::rev2 Between rev1 and rev2, excluding rev1 and rev2.\n",
57 "\t rev: rev and following revisions on the same branch.\n",
58 "\t rev:: After rev on the same branch.\n",
59 "\t :rev rev and previous revisions on the same branch.\n",
60 "\t ::rev Before rev on the same branch.\n",
62 "\t-q Run quietly.\n",
63 "\t-s state[:rev] Set revision state (latest revision on branch,\n",
64 "\t latest revision on trunk if omitted).\n",
65 "\t-t[file] Get descriptive text from file (stdin if omitted).\n",
66 "\t-t-string Set descriptive text.\n",
67 "\t-u[rev] Unlock the revision (latest revision on branch,\n",
68 "\t latest revision on trunk if omitted).\n",
69 "\t-U Unset strict locking.\n",
70 "(Specify the --help global option for a list of other help options)\n",
74 /* This structure is used to pass information through start_recursion. */
77 /* Set default branch (-b). It is "-b" followed by the value
78 given, or NULL if not specified, or merely "-b" if -b is
79 specified without a value. */
82 /* Set comment leader (-c). It is "-c" followed by the value
83 given, or NULL if not specified. The comment leader is
84 relevant only for old versions of RCS, but we let people set it
88 /* Set strict locking (-L). */
91 /* Set nonstrict locking (-U). */
94 /* Delete revisions (-o). It is "-o" followed by the value specified. */
97 /* Keyword substitution mode (-k), e.g. "-kb". */
100 /* Description (-t). */
103 /* Interactive (-I). Problematic with client/server. */
106 /* This is the cheesy part. It is a vector with the options which
107 we don't deal with above (e.g. "-afoo" "-abar,baz"). In the future
108 this presumably will be replaced by other variables which break
109 out the data in a more convenient fashion. AV as well as each of
110 the strings it points to is malloc'd. */
116 /* Add an argument. OPT is the option letter, e.g. 'a'. ARG is the
117 argument to that option, or NULL if omitted (whether NULL can actually
118 happen depends on whether the option was specified as optional to
121 arg_add (struct admin_data *dat, int opt, char *arg)
123 char *newelt = Xasprintf ("-%c%s", opt, arg ? arg : "");
125 if (dat->av_alloc == 0)
128 dat->av = xnmalloc (dat->av_alloc, sizeof (*dat->av));
130 else if (dat->ac >= dat->av_alloc)
133 dat->av = xnrealloc (dat->av, 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);
158 * Cast any NULL arguments as appropriate pointers as this is an
159 * stdarg function and we need to be certain the caller gets what
162 cmdline = format_cmdline (
163 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
165 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
167 "c", "s", cvs_cmd_name,
168 "I", "s", global_session_id,
169 #ifdef SERVER_SUPPORT
170 "R", "s", referrer ? referrer->original : "NONE",
171 #endif /* SERVER_SUPPORT */
173 "r", "s", current_parsed_root->directory,
176 if (!cmdline || !strlen (cmdline))
178 if (cmdline) free (cmdline);
179 error (0, 0, "postadmin proc resolved to the empty string!");
187 /* FIXME - read the comment in verifymsg_proc() about why we use abs()
188 * below() and shouldn't.
190 return abs (run_exec (RUN_TTY, RUN_TTY, RUN_TTY,
191 RUN_NORMAL | RUN_SIGIGNORE));
197 * Call any postadmin procs.
200 admin_filesdoneproc (void *callerdat, int err, const char *repository,
201 const char *update_dir, List *entries)
203 TRACE (TRACE_FUNCTION, "admin_filesdoneproc (%d, %s, %s)", err, repository,
205 Parse_Info (CVSROOTADM_POSTADMIN, repository, postadmin_proc, PIOPT_ALL,
214 admin (int argc, char **argv)
217 #ifdef CVS_ADMIN_GROUP
219 struct group *getgrnam (const char *);
221 struct admin_data admin_data;
224 bool only_allowed_options;
231 memset (&admin_data, 0, sizeof admin_data);
233 /* TODO: get rid of `-' switch notation in admin_data. For
234 example, admin_data->branch should be not `-bfoo' but simply `foo'. */
237 only_allowed_options = true;
238 while ((c = getopt (argc, argv,
239 "+ib::c:a:A:e::l::u::LUn:N:m:o:s:t::IqxV:k:")) != -1)
242 # ifdef CLIENT_SUPPORT
243 !current_parsed_root->isremote &&
244 # endif /* CLIENT_SUPPORT */
245 c != 'q' && !strchr (config->UserAdminOptions, c)
247 only_allowed_options = false;
252 /* This has always been documented as useless in cvs.texinfo
253 and it really is--admin_fileproc silently does nothing
254 if vers->vn_user is NULL. */
255 error (0, 0, "the -i option to admin is not supported");
256 error (0, 0, "run add or import to create an RCS file");
260 if (admin_data.branch != NULL)
262 error (0, 0, "duplicate 'b' option");
266 admin_data.branch = xstrdup ("-b");
268 admin_data.branch = Xasprintf ("-b%s", optarg);
272 if (admin_data.comment != NULL)
274 error (0, 0, "duplicate 'c' option");
277 admin_data.comment = Xasprintf ("-c%s", 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 = Xasprintf ("-o%s", optarg);
373 /* Note that multiple -s options are valid. */
374 arg_add (&admin_data, 's', optarg);
378 if (admin_data.desc != NULL)
380 error (0, 0, "duplicate 't' option");
383 if (optarg != NULL && optarg[0] == '-')
384 admin_data.desc = xstrdup (optarg + 1);
390 get_file (optarg, optarg, "r", &admin_data.desc,
396 /* At least in RCS this can be specified several times,
397 with the same meaning as being specified once. */
398 admin_data.interactive = 1;
402 /* Silently set the global really_quiet flag. This keeps admin in
403 * sync with the RCS man page and allows us to silently support
404 * older servers when necessary.
406 * Some logic says we might want to output a deprecation warning
407 * here, but I'm opting not to in order to stay quietly in sync
408 * with the RCS man page.
414 error (0, 0, "the -x option has never done anything useful");
415 error (0, 0, "RCS files in CVS always end in ,v");
419 /* No longer supported. */
420 error (0, 0, "the `-V' option is obsolete");
424 if (admin_data.kflag != NULL)
426 error (0, 0, "duplicate '-k' option");
429 admin_data.kflag = RCS_check_kflag (optarg);
433 /* getopt will have printed an error message. */
436 /* Don't use cvs_cmd_name; it might be "server". */
437 error (1, 0, "specify %s -H admin for usage information",
444 #ifdef CVS_ADMIN_GROUP
445 /* The use of `cvs admin -k' is unrestricted. However, any other
446 option is restricted if the group CVS_ADMIN_GROUP exists on the
448 /* This is only "secure" on the server, since the user could edit the
449 * RCS file on a local host, but some people like this kind of
450 * check anyhow. The alternative would be to check only when
451 * (server_active) rather than when not on the client.
453 if (!current_parsed_root->isremote && !only_allowed_options &&
454 (grp = getgrnam(CVS_ADMIN_GROUP)) != NULL)
456 #ifdef HAVE_GETGROUPS
460 /* get number of auxiliary groups */
461 n = getgroups (0, NULL);
463 error (1, errno, "unable to get number of auxiliary groups");
464 grps = xnmalloc (n + 1, sizeof *grps);
465 n = getgroups (n, grps);
467 error (1, errno, "unable to get list of auxiliary groups");
469 for (i = 0; i <= n; i++)
470 if (grps[i] == grp->gr_gid) break;
473 error (1, 0, "usage is restricted to members of the group %s",
476 char *me = getcaller ();
479 for (grnam = grp->gr_mem; *grnam; grnam++)
480 if (strcmp (*grnam, me) == 0) break;
481 if (!*grnam && getgid () != grp->gr_gid)
482 error (1, 0, "usage is restricted to members of the group %s",
486 #endif /* defined CVS_ADMIN_GROUP */
488 for (i = 0; i < admin_data.ac; ++i)
490 assert (admin_data.av[i][0] == '-');
491 switch (admin_data.av[i][1])
496 check_numeric (&admin_data.av[i][2], argc, argv);
502 if (admin_data.branch != NULL)
503 check_numeric (admin_data.branch + 2, argc, argv);
504 if (admin_data.delete_revs != NULL)
508 check_numeric (admin_data.delete_revs + 2, argc, argv);
509 p = strchr (admin_data.delete_revs + 2, ':');
510 if (p != NULL && isdigit ((unsigned char) p[1]))
511 check_numeric (p + 1, argc, argv);
512 else if (p != NULL && p[1] == ':' && isdigit ((unsigned char) p[2]))
513 check_numeric (p + 2, argc, argv);
516 #ifdef CLIENT_SUPPORT
517 if (current_parsed_root->isremote)
519 /* We're the client side. Fire up the remote server. */
524 /* Note that option_with_arg does not work for us, because some
525 of the options must be sent without a space between the option
527 if (admin_data.interactive)
528 error (1, 0, "-I option not useful with client/server");
529 if (admin_data.branch != NULL)
530 send_arg (admin_data.branch);
531 if (admin_data.comment != NULL)
532 send_arg (admin_data.comment);
533 if (admin_data.set_strict)
535 if (admin_data.set_nonstrict)
537 if (admin_data.delete_revs != NULL)
538 send_arg (admin_data.delete_revs);
539 if (admin_data.desc != NULL)
541 char *p = admin_data.desc;
542 send_to_server ("Argument -t-", 0);
547 send_to_server ("\012Argumentx ", 0);
552 char *q = strchr (p, '\n');
553 if (q == NULL) q = p + strlen (p);
554 send_to_server (p, q - p);
558 send_to_server ("\012", 1);
560 /* Send this for all really_quiets since we know that it will be silently
561 * ignored when unneeded. This supports old servers.
565 if (admin_data.kflag != NULL)
566 send_arg (admin_data.kflag);
568 for (i = 0; i < admin_data.ac; ++i)
569 send_arg (admin_data.av[i]);
572 send_files (argc, argv, 0, 0, SEND_NO_CONTENTS);
573 send_file_names (argc, argv, SEND_EXPAND_WILD);
574 send_to_server ("admin\012", 0);
575 err = get_responses_and_close ();
578 #endif /* CLIENT_SUPPORT */
580 lock_tree_promotably (argc, argv, 0, W_LOCAL, 0);
582 err = start_recursion
583 (admin_fileproc, admin_filesdoneproc, admin_dirproc,
586 W_LOCAL, 0, CVS_LOCK_WRITE, NULL, 1, NULL);
590 /* This just suppresses a warning from -Wall. */
591 #ifdef CLIENT_SUPPORT
593 #endif /* CLIENT_SUPPORT */
594 if (admin_data.branch != NULL)
595 free (admin_data.branch);
596 if (admin_data.comment != NULL)
597 free (admin_data.comment);
598 if (admin_data.delete_revs != NULL)
599 free (admin_data.delete_revs);
600 if (admin_data.kflag != NULL)
601 free (admin_data.kflag);
602 if (admin_data.desc != NULL)
603 free (admin_data.desc);
604 for (i = 0; i < admin_data.ac; ++i)
605 free (admin_data.av[i]);
606 if (admin_data.av != NULL)
607 free (admin_data.av);
615 * Called to run "rcs" on a particular file.
619 admin_fileproc (void *callerdat, struct file_info *finfo)
621 struct admin_data *admin_data = (struct admin_data *) callerdat;
628 vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
630 version = vers->vn_user;
631 if (version != NULL && strcmp (version, "0") == 0)
633 error (0, 0, "cannot admin newly added file `%s'", finfo->file);
642 error (0, 0, "nothing known about %s", finfo->file);
647 if (rcs->flags & PARTIAL)
648 RCS_reparsercsfile (rcs, NULL, NULL);
652 cvs_output ("RCS file: ", 0);
653 cvs_output (rcs->path, 0);
654 cvs_output ("\n", 1);
657 if (admin_data->branch != NULL)
659 char *branch = &admin_data->branch[2];
660 if (*branch != '\0' && ! isdigit ((unsigned char) *branch))
662 branch = RCS_whatbranch (rcs, admin_data->branch + 2);
665 error (0, 0, "%s: Symbolic name %s is undefined.",
666 rcs->path, admin_data->branch + 2);
671 RCS_setbranch (rcs, branch);
672 if (branch != NULL && branch != &admin_data->branch[2])
675 if (admin_data->comment != NULL)
677 if (rcs->comment != NULL)
679 rcs->comment = xstrdup (admin_data->comment + 2);
681 if (admin_data->set_strict)
682 rcs->strict_locks = 1;
683 if (admin_data->set_nonstrict)
684 rcs->strict_locks = 0;
685 if (admin_data->delete_revs != NULL)
687 char *s, *t, *rev1, *rev2;
688 /* Set for :, clear for ::. */
692 s = admin_data->delete_revs + 2;
706 /* Note that we don't support '-' for ranges. RCS considers it
707 obsolete and it is problematic with tags containing '-'. "cvs log"
708 has made the same decision. */
726 *t = ':'; /* probably unnecessary */
735 if (rev1 == NULL && rev2 == NULL)
737 /* RCS segfaults if `-o:' is given */
738 error (0, 0, "no valid revisions specified in `%s' option",
739 admin_data->delete_revs);
744 status |= RCS_delete_revs (rcs, rev1, rev2, inclusive);
751 if (admin_data->desc != NULL)
754 rcs->desc = xstrdup (admin_data->desc);
756 if (admin_data->kflag != NULL)
758 char *kflag = admin_data->kflag + 2;
759 char *oldexpand = RCS_getexpand (rcs);
760 if (oldexpand == NULL || strcmp (oldexpand, kflag) != 0)
761 RCS_setexpand (rcs, kflag);
764 /* Handle miscellaneous options. TODO: decide whether any or all
765 of these should have their own fields in the admin_data
767 for (i = 0; i < admin_data->ac; ++i)
770 char *p, *rev, *revnum, *tag, *msg;
776 arg = admin_data->av[i];
779 case 'a': /* fall through */
781 line2argv (&argc, &users, arg + 2, " ,\t\n");
783 for (u = 0; u < argc; ++u)
784 RCS_addaccess (rcs, users[u]);
786 RCS_delaccess (rcs, NULL);
788 for (u = 0; u < argc; ++u)
789 RCS_delaccess (rcs, users[u]);
790 free_names (&argc, users);
794 /* See admin-19a-admin and friends in sanity.sh for
795 relative pathnames. It makes sense to think in
796 terms of a syntax which give pathnames relative to
797 the repository or repository corresponding to the
798 current directory or some such (and perhaps don't
799 include ,v), but trying to worry about such things
800 is a little pointless unless you first worry about
801 whether "cvs admin -A" as a whole makes any sense
802 (currently probably not, as access lists don't
803 affect the behavior of CVS). */
805 rcs2 = RCS_parsercsfile (arg + 2);
807 error (1, 0, "cannot continue");
809 p = xstrdup (RCS_getaccess (rcs2));
810 line2argv (&argc, &users, p, " \t\n");
814 for (u = 0; u < argc; ++u)
815 RCS_addaccess (rcs, users[u]);
816 free_names (&argc, users);
818 case 'n': /* fall through */
822 cvs_outerr ("missing symbolic name after ", 0);
824 cvs_outerr ("\n", 1);
827 p = strchr (arg, ':');
830 if (RCS_deltag (rcs, arg + 2) != 0)
832 error (0, 0, "%s: Symbolic name %s is undefined.",
841 tag = xstrdup (arg + 2);
844 /* Option `n' signals an error if this tag is already bound. */
847 n = findnode (RCS_symbols (rcs), tag);
851 "%s: symbolic name %s already bound to %s",
853 tag, (char *)n->data);
860 /* Attempt to perform the requested tagging. */
862 if ((*p == 0 && (rev = RCS_head (rcs)))
863 || (rev = RCS_tag2rev (rcs, p))) /* tag2rev may exit */
865 RCS_check_tag (tag); /* exit if not a valid tag */
866 RCS_settag (rcs, tag, rev);
873 "%s: Symbolic name or revision %s is undefined.",
880 p = strchr (arg, ':');
883 tag = xstrdup (arg + 2);
884 rev = RCS_head (rcs);
887 error (0, 0, "No head revision in archive file `%s'.",
896 tag = xstrdup (arg + 2);
900 revnum = RCS_gettag (rcs, rev, 0, NULL);
903 n = findnode (rcs->versions, revnum);
911 "%s: can't set state of nonexisting revision %s",
925 p = strchr (arg, ':');
928 error (0, 0, "%s: -m option lacks revision number",
933 *p = '\0'; /* temporarily make arg+2 its own string */
934 rev = RCS_gettag (rcs, arg + 2, 1, NULL); /* Force tag match */
937 error (0, 0, "%s: no such revision %s", rcs->path, arg+2);
939 *p = ':'; /* restore the full text of the -m argument */
944 n = findnode (rcs->versions, rev);
945 /* tags may exist against non-existing versions */
948 error (0, 0, "%s: no such revision %s: %s",
949 rcs->path, arg+2, rev);
951 *p = ':'; /* restore the full text of the -m argument */
955 *p = ':'; /* restore the full text of the -m argument */
959 if (delta->text == NULL)
961 delta->text = xmalloc (sizeof (Deltatext));
962 memset (delta->text, 0, sizeof (Deltatext));
964 delta->text->version = xstrdup (delta->version);
965 delta->text->log = make_message_rcsvalid (msg);
969 status |= RCS_lock (rcs, arg[2] ? arg + 2 : NULL, 0);
972 status |= RCS_unlock (rcs, arg[2] ? arg + 2 : NULL, 0);
974 default: assert(0); /* can't happen */
980 RCS_rewrite (rcs, NULL, NULL);
982 cvs_output ("done\n", 5);
986 /* Note that this message should only occur after another
987 message has given a more specific error. The point of this
988 additional message is to make it clear that the previous problems
989 caused CVS to forget about the idea of modifying the RCS file. */
991 error (0, 0, "RCS file for `%s' not modified.", finfo->file);
1003 * Print a warm fuzzy message
1007 admin_dirproc (void *callerdat, const char *dir, const char *repos,
1008 const char *update_dir, List *entries)
1011 error (0, 0, "Administrating %s", update_dir);