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 static Dtype admin_dirproc (void *callerdat, const char *dir,
23 const char *repos, const char *update_dir,
25 static int admin_fileproc (void *callerdat, struct file_info *finfo);
27 static const char *const admin_usage[] =
29 "Usage: %s %s [options] files...\n",
30 "\t-a users Append (comma-separated) user names to access list.\n",
31 "\t-A file Append another file's access list.\n",
32 "\t-b[rev] Set default branch (highest branch on trunk if omitted).\n",
33 "\t-c string Set comment leader.\n",
34 "\t-e[users] Remove (comma-separated) user names from access list\n",
35 "\t (all names if omitted).\n",
36 "\t-I Run interactively.\n",
37 "\t-k subst Set keyword substitution mode:\n",
38 "\t kv (Default) Substitute keyword and value.\n",
39 "\t kvl Substitute keyword, value, and locker (if any).\n",
40 "\t k Substitute keyword only.\n",
41 "\t o Preserve original string.\n",
42 "\t b Like o, but mark file as binary.\n",
43 "\t v Substitute value only.\n",
44 "\t-l[rev] Lock revision (latest revision on branch,\n",
45 "\t latest revision on trunk if omitted).\n",
46 "\t-L Set strict locking.\n",
47 "\t-m rev:msg Replace revision's log message.\n",
48 "\t-n tag[:[rev]] Tag branch or revision. If :rev is omitted,\n",
49 "\t delete the tag; if rev is omitted, tag the latest\n",
50 "\t revision on the default branch.\n",
51 "\t-N tag[:[rev]] Same as -n except override existing tag.\n",
52 "\t-o range Delete (outdate) specified range of revisions:\n",
53 "\t rev1:rev2 Between rev1 and rev2, including rev1 and rev2.\n",
54 "\t rev1::rev2 Between rev1 and rev2, excluding rev1 and rev2.\n",
55 "\t rev: rev and following revisions on the same branch.\n",
56 "\t rev:: After rev on the same branch.\n",
57 "\t :rev rev and previous revisions on the same branch.\n",
58 "\t ::rev Before rev on the same branch.\n",
60 "\t-q Run quietly.\n",
61 "\t-s state[:rev] Set revision state (latest revision on branch,\n",
62 "\t latest revision on trunk if omitted).\n",
63 "\t-t[file] Get descriptive text from file (stdin if omitted).\n",
64 "\t-t-string Set descriptive text.\n",
65 "\t-u[rev] Unlock the revision (latest revision on branch,\n",
66 "\t latest revision on trunk if omitted).\n",
67 "\t-U Unset strict locking.\n",
68 "(Specify the --help global option for a list of other help options)\n",
72 /* This structure is used to pass information through start_recursion. */
75 /* Set default branch (-b). It is "-b" followed by the value
76 given, or NULL if not specified, or merely "-b" if -b is
77 specified without a value. */
80 /* Set comment leader (-c). It is "-c" followed by the value
81 given, or NULL if not specified. The comment leader is
82 relevant only for old versions of RCS, but we let people set it
86 /* Set strict locking (-L). */
89 /* Set nonstrict locking (-U). */
92 /* Delete revisions (-o). It is "-o" followed by the value specified. */
95 /* Keyword substitution mode (-k), e.g. "-kb". */
98 /* Description (-t). */
101 /* Interactive (-I). Problematic with client/server. */
104 /* This is the cheesy part. It is a vector with the options which
105 we don't deal with above (e.g. "-afoo" "-abar,baz"). In the future
106 this presumably will be replaced by other variables which break
107 out the data in a more convenient fashion. AV as well as each of
108 the strings it points to is malloc'd. */
114 /* Add an argument. OPT is the option letter, e.g. 'a'. ARG is the
115 argument to that option, or NULL if omitted (whether NULL can actually
116 happen depends on whether the option was specified as optional to
119 arg_add (struct admin_data *dat, int opt, char *arg)
121 char *newelt = Xasprintf ("-%c%s", opt, arg ? arg : "");
123 if (dat->av_alloc == 0)
126 dat->av = xnmalloc (dat->av_alloc, sizeof (*dat->av));
128 else if (dat->ac >= dat->av_alloc)
131 dat->av = xnrealloc (dat->av, dat->av_alloc, sizeof (*dat->av));
133 dat->av[dat->ac++] = newelt;
139 * callback proc to run a script when admin finishes.
142 postadmin_proc (const char *repository, const char *filter, void *closure)
145 const char *srepos = Short_Repository (repository);
147 TRACE (TRACE_FUNCTION, "postadmin_proc (%s, %s)", repository, filter);
155 * Cast any NULL arguments as appropriate pointers as this is an
156 * stdarg function and we need to be certain the caller gets what
159 cmdline = format_cmdline (
160 #ifdef SUPPORT_OLD_INFO_FMT_STRINGS
162 #endif /* SUPPORT_OLD_INFO_FMT_STRINGS */
164 "c", "s", cvs_cmd_name,
165 #ifdef SERVER_SUPPORT
166 "R", "s", referrer ? referrer->original : "NONE",
167 #endif /* SERVER_SUPPORT */
169 "r", "s", current_parsed_root->directory,
172 if (!cmdline || !strlen (cmdline))
174 if (cmdline) free (cmdline);
175 error (0, 0, "postadmin proc resolved to the empty string!");
183 /* FIXME - read the comment in verifymsg_proc() about why we use abs()
184 * below() and shouldn't.
186 return abs (run_exec (RUN_TTY, RUN_TTY, RUN_TTY,
187 RUN_NORMAL | RUN_SIGIGNORE));
193 * Call any postadmin procs.
196 admin_filesdoneproc (void *callerdat, int err, const char *repository,
197 const char *update_dir, List *entries)
199 TRACE (TRACE_FUNCTION, "admin_filesdoneproc (%d, %s, %s)", err, repository,
201 Parse_Info (CVSROOTADM_POSTADMIN, repository, postadmin_proc, PIOPT_ALL,
210 admin (int argc, char **argv)
213 #ifdef CVS_ADMIN_GROUP
215 struct group *getgrnam (const char *);
217 struct admin_data admin_data;
220 bool only_allowed_options;
227 memset (&admin_data, 0, sizeof admin_data);
229 /* TODO: get rid of `-' switch notation in admin_data. For
230 example, admin_data->branch should be not `-bfoo' but simply `foo'. */
233 only_allowed_options = true;
234 while ((c = getopt (argc, argv,
235 "+ib::c:a:A:e::l::u::LUn:N:m:o:s:t::IqxV:k:")) != -1)
238 # ifdef CLIENT_SUPPORT
239 !current_parsed_root->isremote &&
240 # endif /* CLIENT_SUPPORT */
241 c != 'q' && !strchr (config->UserAdminOptions, c)
243 only_allowed_options = false;
248 /* This has always been documented as useless in cvs.texinfo
249 and it really is--admin_fileproc silently does nothing
250 if vers->vn_user is NULL. */
251 error (0, 0, "the -i option to admin is not supported");
252 error (0, 0, "run add or import to create an RCS file");
256 if (admin_data.branch != NULL)
258 error (0, 0, "duplicate 'b' option");
262 admin_data.branch = xstrdup ("-b");
264 admin_data.branch = Xasprintf ("-b%s", optarg);
268 if (admin_data.comment != NULL)
270 error (0, 0, "duplicate 'c' option");
273 admin_data.comment = Xasprintf ("-c%s", optarg);
277 arg_add (&admin_data, 'a', optarg);
281 /* In the client/server case, this is cheesy because
282 we just pass along the name of the RCS file, which
283 then will want to exist on the server. This is
284 accidental; having the client specify a pathname on
285 the server is not a design feature of the protocol. */
286 arg_add (&admin_data, 'A', optarg);
290 arg_add (&admin_data, 'e', optarg);
294 /* Note that multiple -l options are valid. */
295 arg_add (&admin_data, 'l', optarg);
299 /* Note that multiple -u options are valid. */
300 arg_add (&admin_data, 'u', optarg);
304 /* Probably could also complain if -L is specified multiple
305 times, although RCS doesn't and I suppose it is reasonable
306 just to have it mean the same as a single -L. */
307 if (admin_data.set_nonstrict)
309 error (0, 0, "-U and -L are incompatible");
312 admin_data.set_strict = 1;
316 /* Probably could also complain if -U is specified multiple
317 times, although RCS doesn't and I suppose it is reasonable
318 just to have it mean the same as a single -U. */
319 if (admin_data.set_strict)
321 error (0, 0, "-U and -L are incompatible");
324 admin_data.set_nonstrict = 1;
328 /* Mostly similar to cvs tag. Could also be parsing
329 the syntax of optarg, although for now we just pass
330 it to rcs as-is. Note that multiple -n options are
332 arg_add (&admin_data, 'n', optarg);
336 /* Mostly similar to cvs tag. Could also be parsing
337 the syntax of optarg, although for now we just pass
338 it to rcs as-is. Note that multiple -N options are
340 arg_add (&admin_data, 'N', optarg);
344 /* Change log message. Could also be parsing the syntax
345 of optarg, although for now we just pass it to rcs
346 as-is. Note that multiple -m options are valid. */
347 arg_add (&admin_data, 'm', optarg);
351 /* Delete revisions. Probably should also be parsing the
352 syntax of optarg, so that the client can give errors
353 rather than making the server take care of that.
354 Other than that I'm not sure whether it matters much
355 whether we parse it here or in admin_fileproc.
357 Note that multiple -o options are invalid, in RCS
360 if (admin_data.delete_revs != NULL)
362 error (0, 0, "duplicate '-o' option");
365 admin_data.delete_revs = Xasprintf ("-o%s", optarg);
369 /* Note that multiple -s options are valid. */
370 arg_add (&admin_data, 's', optarg);
374 if (admin_data.desc != NULL)
376 error (0, 0, "duplicate 't' option");
379 if (optarg != NULL && optarg[0] == '-')
380 admin_data.desc = xstrdup (optarg + 1);
386 get_file (optarg, optarg, "r", &admin_data.desc,
392 /* At least in RCS this can be specified several times,
393 with the same meaning as being specified once. */
394 admin_data.interactive = 1;
398 /* Silently set the global really_quiet flag. This keeps admin in
399 * sync with the RCS man page and allows us to silently support
400 * older servers when necessary.
402 * Some logic says we might want to output a deprecation warning
403 * here, but I'm opting not to in order to stay quietly in sync
404 * with the RCS man page.
410 error (0, 0, "the -x option has never done anything useful");
411 error (0, 0, "RCS files in CVS always end in ,v");
415 /* No longer supported. */
416 error (0, 0, "the `-V' option is obsolete");
420 if (admin_data.kflag != NULL)
422 error (0, 0, "duplicate '-k' option");
425 admin_data.kflag = RCS_check_kflag (optarg);
429 /* getopt will have printed an error message. */
432 /* Don't use cvs_cmd_name; it might be "server". */
433 error (1, 0, "specify %s -H admin for usage information",
440 #ifdef CVS_ADMIN_GROUP
441 /* The use of `cvs admin -k' is unrestricted. However, any other
442 option is restricted if the group CVS_ADMIN_GROUP exists on the
444 /* This is only "secure" on the server, since the user could edit the
445 * RCS file on a local host, but some people like this kind of
446 * check anyhow. The alternative would be to check only when
447 * (server_active) rather than when not on the client.
449 if (!current_parsed_root->isremote && !only_allowed_options &&
450 (grp = getgrnam(CVS_ADMIN_GROUP)) != NULL)
452 #ifdef HAVE_GETGROUPS
456 /* get number of auxiliary groups */
457 n = getgroups (0, NULL);
459 error (1, errno, "unable to get number of auxiliary groups");
460 grps = xnmalloc (n + 1, sizeof *grps);
461 n = getgroups (n, grps);
463 error (1, errno, "unable to get list of auxiliary groups");
465 for (i = 0; i <= n; i++)
466 if (grps[i] == grp->gr_gid) break;
469 error (1, 0, "usage is restricted to members of the group %s",
472 char *me = getcaller ();
475 for (grnam = grp->gr_mem; *grnam; grnam++)
476 if (strcmp (*grnam, me) == 0) break;
477 if (!*grnam && getgid () != grp->gr_gid)
478 error (1, 0, "usage is restricted to members of the group %s",
482 #endif /* defined CVS_ADMIN_GROUP */
484 for (i = 0; i < admin_data.ac; ++i)
486 assert (admin_data.av[i][0] == '-');
487 switch (admin_data.av[i][1])
492 check_numeric (&admin_data.av[i][2], argc, argv);
498 if (admin_data.branch != NULL)
499 check_numeric (admin_data.branch + 2, argc, argv);
500 if (admin_data.delete_revs != NULL)
504 check_numeric (admin_data.delete_revs + 2, argc, argv);
505 p = strchr (admin_data.delete_revs + 2, ':');
506 if (p != NULL && isdigit ((unsigned char) p[1]))
507 check_numeric (p + 1, argc, argv);
508 else if (p != NULL && p[1] == ':' && isdigit ((unsigned char) p[2]))
509 check_numeric (p + 2, argc, argv);
512 #ifdef CLIENT_SUPPORT
513 if (current_parsed_root->isremote)
515 /* We're the client side. Fire up the remote server. */
520 /* Note that option_with_arg does not work for us, because some
521 of the options must be sent without a space between the option
523 if (admin_data.interactive)
524 error (1, 0, "-I option not useful with client/server");
525 if (admin_data.branch != NULL)
526 send_arg (admin_data.branch);
527 if (admin_data.comment != NULL)
528 send_arg (admin_data.comment);
529 if (admin_data.set_strict)
531 if (admin_data.set_nonstrict)
533 if (admin_data.delete_revs != NULL)
534 send_arg (admin_data.delete_revs);
535 if (admin_data.desc != NULL)
537 char *p = admin_data.desc;
538 send_to_server ("Argument -t-", 0);
543 send_to_server ("\012Argumentx ", 0);
548 char *q = strchr (p, '\n');
549 if (q == NULL) q = p + strlen (p);
550 send_to_server (p, q - p);
554 send_to_server ("\012", 1);
556 /* Send this for all really_quiets since we know that it will be silently
557 * ignored when unneeded. This supports old servers.
561 if (admin_data.kflag != NULL)
562 send_arg (admin_data.kflag);
564 for (i = 0; i < admin_data.ac; ++i)
565 send_arg (admin_data.av[i]);
568 send_files (argc, argv, 0, 0, SEND_NO_CONTENTS);
569 send_file_names (argc, argv, SEND_EXPAND_WILD);
570 send_to_server ("admin\012", 0);
571 err = get_responses_and_close ();
574 #endif /* CLIENT_SUPPORT */
576 lock_tree_promotably (argc, argv, 0, W_LOCAL, 0);
578 err = start_recursion
579 (admin_fileproc, admin_filesdoneproc, admin_dirproc,
582 W_LOCAL, 0, CVS_LOCK_WRITE, NULL, 1, NULL);
586 /* This just suppresses a warning from -Wall. */
587 #ifdef CLIENT_SUPPORT
589 #endif /* CLIENT_SUPPORT */
590 if (admin_data.branch != NULL)
591 free (admin_data.branch);
592 if (admin_data.comment != NULL)
593 free (admin_data.comment);
594 if (admin_data.delete_revs != NULL)
595 free (admin_data.delete_revs);
596 if (admin_data.kflag != NULL)
597 free (admin_data.kflag);
598 if (admin_data.desc != NULL)
599 free (admin_data.desc);
600 for (i = 0; i < admin_data.ac; ++i)
601 free (admin_data.av[i]);
602 if (admin_data.av != NULL)
603 free (admin_data.av);
611 * Called to run "rcs" on a particular file.
615 admin_fileproc (void *callerdat, struct file_info *finfo)
617 struct admin_data *admin_data = (struct admin_data *) callerdat;
624 vers = Version_TS (finfo, NULL, NULL, NULL, 0, 0);
626 version = vers->vn_user;
627 if (version != NULL && strcmp (version, "0") == 0)
629 error (0, 0, "cannot admin newly added file `%s'", finfo->file);
638 error (0, 0, "nothing known about %s", finfo->file);
643 if (rcs->flags & PARTIAL)
644 RCS_reparsercsfile (rcs, NULL, NULL);
648 cvs_output ("RCS file: ", 0);
649 cvs_output (rcs->path, 0);
650 cvs_output ("\n", 1);
653 if (admin_data->branch != NULL)
655 char *branch = &admin_data->branch[2];
656 if (*branch != '\0' && ! isdigit ((unsigned char) *branch))
658 branch = RCS_whatbranch (rcs, admin_data->branch + 2);
661 error (0, 0, "%s: Symbolic name %s is undefined.",
662 rcs->path, admin_data->branch + 2);
667 RCS_setbranch (rcs, branch);
668 if (branch != NULL && branch != &admin_data->branch[2])
671 if (admin_data->comment != NULL)
673 if (rcs->comment != NULL)
675 rcs->comment = xstrdup (admin_data->comment + 2);
677 if (admin_data->set_strict)
678 rcs->strict_locks = 1;
679 if (admin_data->set_nonstrict)
680 rcs->strict_locks = 0;
681 if (admin_data->delete_revs != NULL)
683 char *s, *t, *rev1, *rev2;
684 /* Set for :, clear for ::. */
688 s = admin_data->delete_revs + 2;
702 /* Note that we don't support '-' for ranges. RCS considers it
703 obsolete and it is problematic with tags containing '-'. "cvs log"
704 has made the same decision. */
722 *t = ':'; /* probably unnecessary */
731 if (rev1 == NULL && rev2 == NULL)
733 /* RCS segfaults if `-o:' is given */
734 error (0, 0, "no valid revisions specified in `%s' option",
735 admin_data->delete_revs);
740 status |= RCS_delete_revs (rcs, rev1, rev2, inclusive);
747 if (admin_data->desc != NULL)
750 rcs->desc = xstrdup (admin_data->desc);
752 if (admin_data->kflag != NULL)
754 char *kflag = admin_data->kflag + 2;
755 char *oldexpand = RCS_getexpand (rcs);
756 if (oldexpand == NULL || strcmp (oldexpand, kflag) != 0)
757 RCS_setexpand (rcs, kflag);
760 /* Handle miscellaneous options. TODO: decide whether any or all
761 of these should have their own fields in the admin_data
763 for (i = 0; i < admin_data->ac; ++i)
766 char *p, *rev, *revnum, *tag, *msg;
772 arg = admin_data->av[i];
775 case 'a': /* fall through */
777 line2argv (&argc, &users, arg + 2, " ,\t\n");
779 for (u = 0; u < argc; ++u)
780 RCS_addaccess (rcs, users[u]);
782 RCS_delaccess (rcs, NULL);
784 for (u = 0; u < argc; ++u)
785 RCS_delaccess (rcs, users[u]);
786 free_names (&argc, users);
790 /* See admin-19a-admin and friends in sanity.sh for
791 relative pathnames. It makes sense to think in
792 terms of a syntax which give pathnames relative to
793 the repository or repository corresponding to the
794 current directory or some such (and perhaps don't
795 include ,v), but trying to worry about such things
796 is a little pointless unless you first worry about
797 whether "cvs admin -A" as a whole makes any sense
798 (currently probably not, as access lists don't
799 affect the behavior of CVS). */
801 rcs2 = RCS_parsercsfile (arg + 2);
803 error (1, 0, "cannot continue");
805 p = xstrdup (RCS_getaccess (rcs2));
806 line2argv (&argc, &users, p, " \t\n");
810 for (u = 0; u < argc; ++u)
811 RCS_addaccess (rcs, users[u]);
812 free_names (&argc, users);
814 case 'n': /* fall through */
818 cvs_outerr ("missing symbolic name after ", 0);
820 cvs_outerr ("\n", 1);
823 p = strchr (arg, ':');
826 if (RCS_deltag (rcs, arg + 2) != 0)
828 error (0, 0, "%s: Symbolic name %s is undefined.",
837 tag = xstrdup (arg + 2);
840 /* Option `n' signals an error if this tag is already bound. */
843 n = findnode (RCS_symbols (rcs), tag);
847 "%s: symbolic name %s already bound to %s",
849 tag, (char *)n->data);
856 /* Attempt to perform the requested tagging. */
858 if ((*p == 0 && (rev = RCS_head (rcs)))
859 || (rev = RCS_tag2rev (rcs, p))) /* tag2rev may exit */
861 RCS_check_tag (tag); /* exit if not a valid tag */
862 RCS_settag (rcs, tag, rev);
869 "%s: Symbolic name or revision %s is undefined.",
876 p = strchr (arg, ':');
879 tag = xstrdup (arg + 2);
880 rev = RCS_head (rcs);
883 error (0, 0, "No head revision in archive file `%s'.",
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 = xmalloc (sizeof (Deltatext));
958 memset (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);