2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 #include <afsconfig.h>
11 #include <afs/param.h>
21 #include <sys/types.h>
26 #include <WINNT/afsevent.h>
28 #include <netinet/in.h>
30 #include <afs/cellconfig.h>
35 #include <afs/afsutil.h>
45 /* OK, this REALLY sucks bigtime, but I can't tell who is calling
46 * afsconf_CheckAuth easily, and only *SERVERS* should be calling osi_audit
47 * anyway. It's gonna give somebody fits to debug, I know, I know.
53 register struct cmd_syndesc *as;
55 register afs_int32 code;
61 if (!strcmp(as->name,"help")) return 0;
62 if (as->parms[16].items)
63 cell = as->parms[16].items->data;
65 if (as->parms[17].items)
68 if (as->parms[18].items) { /* testing? */
69 code = pr_Initialize(sec, AFSDIR_SERVER_ETC_DIRPATH, cell);
72 code = pr_Initialize(sec, AFSDIR_CLIENT_ETC_DIRPATH, cell);
75 com_err (whoami, code, "while initializing");
78 if (as->parms[19].items)
84 register struct cmd_syndesc *as;
86 if (!strcmp(as->name,"help")) return;
87 /* Need to shutdown the ubik_client & other connections */
95 register struct cmd_syndesc *as;
97 register afs_int32 code;
100 struct cmd_item *namei;
101 struct cmd_item *idi;
103 namei = as->parms[0].items;
104 idi = as->parms[2].items;
105 if (as->parms[1].items)
106 owner = as->parms[1].items->data;
111 code = util_GetInt32 (idi->data, &id);
113 com_err (whoami, code,
114 "because group id was: '%s'", idi->data);
119 com_err (whoami, code,
120 "because group id %d was not negative", id);
126 code = pr_CreateGroup (namei->data, owner, &id);
129 com_err (whoami, code,
130 "; unable to create group %s with id %d%s%s%s%s",
131 namei->data, id, owner ? " owned by '" : "",
134 (force ? " (ignored)" : ""));
135 else com_err (whoami, code,
136 "; unable to create group %s %s", namei->data, (force?"(ignored)":""));
140 printf ("group %s has id %d\n", namei->data, id);
147 register struct cmd_syndesc *as;
149 register afs_int32 code;
151 struct cmd_item *namei;
152 struct cmd_item *idi;
154 namei = as->parms[0].items;
155 idi = as->parms[1].items;
159 code = util_GetInt32 (idi->data, &id);
161 com_err (whoami, code, "because id was: '%s'", idi->data);
165 printf ("0 isn't a valid user id; aborting\n");
171 code = pr_CreateUser (namei->data, &id);
173 if (id) com_err (whoami, code,
174 "; unable to create user %s with id %d %s",
175 namei->data, id, (force?"(ignored)":""));
176 else com_err (whoami, code,
177 "; unable to create user %s %s", namei->data, (force?"(ignored)":""));
181 printf ("User %s has id %d\n", namei->data, id);
189 int GetNameOrId (as, lids, lnames)
190 register struct cmd_syndesc *as;
192 struct namelist *lnames;
194 register afs_int32 code = 0;
199 if (!(as->parms[0].items || as->parms[1].items)) {
200 com_err (whoami, 0, "must specify either a name or an id.");
203 if (as->parms[0].items && as->parms[1].items) {
204 com_err (whoami, 0, "can't specify both a name and id.");
209 lids->idlist_len = 0;
210 lids->idlist_val = 0;
212 if (as->parms[0].items) { /* name */
213 struct namelist names; /* local copy, if not ret. names */
216 names.namelist_val = 0; /* so it gets freed later if needed */
217 if (lnames) nl = lnames;
220 n = 0; /* count names */
221 for (i=as->parms[0].items; i; i=i->next) n++;
222 nl->namelist_val = (prname *)malloc(n*PR_MAXNAMELEN);
223 nl->namelist_len = n;
225 for (i=as->parms[0].items; i; i=i->next)
226 strncpy (nl->namelist_val[n++], i->data, PR_MAXNAMELEN);
228 code = pr_NameToId (nl, lids);
229 if (code) com_err (whoami, code, "so couldn't look up names");
231 for (n=0; n<lids->idlist_len; n++) {
232 if ((lids->idlist_val[n] == ANONYMOUSID)) {
233 com_err (whoami, PRNOENT,
234 "so couldn't look up id for %s", nl->namelist_val[n]);
238 /* treat things as working if any of the lookups worked */
239 if (goodCount==0) code = PRNOENT;
242 if (names.namelist_val) free (names.namelist_val);
244 else if (as->parms[1].items) { /* id */
246 for (i=as->parms[1].items; i; i=i->next) n++;
247 lids->idlist_val = (afs_int32 *)malloc(n*sizeof(afs_int32));
248 lids->idlist_len = n;
250 for (i=as->parms[1].items; i; i=i->next) {
251 code = util_GetInt32 (i->data, &lids->idlist_val[n]);
253 com_err (whoami, code=PRNOENT,
254 "because a bogus id '%s' was specified", i->data);
257 if (!code && lnames) {
258 lnames->namelist_val = 0;
259 lnames->namelist_len = 0;
260 code = pr_IdToName (lids, lnames);
261 if (code) com_err (whoami, code, "translating ids");
265 if (lids->idlist_val) free (lids->idlist_val);
273 int GetNameOrId (as, lids, lnames)
274 register struct cmd_syndesc *as;
276 struct namelist *lnames;
278 register afs_int32 code = 0;
279 int n = 0, nd = 0, nm = 0, id, x;
281 struct namelist names, tnames; /* local copy, if not ret. names */
282 struct idlist ids, tids; /* local copy, if not ret. ids */
285 for (i = as->parms[0].items; i; i = i->next) n++;
286 lids->idlist_val = (afs_int32 *)malloc(n*sizeof(afs_int32));
287 lids->idlist_len = n;
288 ids.idlist_val = (afs_int32 *)malloc(n*sizeof(afs_int32));
290 names.namelist_val = (prname *)malloc(n*PR_MAXNAMELEN);
291 names.namelist_len = n;
293 lnames->namelist_val = (prname *)malloc(n*PR_MAXNAMELEN);
294 lnames->namelist_len = 0;
296 for (i=as->parms[0].items; i; i=i->next) {
297 tnames.namelist_val = (prname *)malloc(PR_MAXNAMELEN);
298 strncpy (tnames.namelist_val[0], i->data, PR_MAXNAMELEN);
299 tnames.namelist_len = 1;
302 code = pr_NameToId(&tnames, &tids);
303 if ((!code && (tids.idlist_val[0] != 32766)) || (code = util_GetInt32 (i->data, &id))) {
304 /* Assume it's a name instead */
305 strncpy (names.namelist_val[nm++], i->data, PR_MAXNAMELEN);
307 ids.idlist_val[nd++] = id;
309 free(tnames.namelist_val);
311 names.namelist_len = nm;
313 tids.idlist_len = nd = nm = 0;
315 code = pr_NameToId (&names, &tids);
316 if (code) com_err (whoami, code, "so couldn't look up names");
318 for (n=0; n < tids.idlist_len; n++) {
319 if ((tids.idlist_val[n] == ANONYMOUSID)) {
320 com_err (whoami, PRNOENT,
321 "so couldn't look up id for %s", names.namelist_val[n]);
324 lids->idlist_val[nd] = tids.idlist_val[n];
326 strcpy(lnames->namelist_val[nd], names.namelist_val[n]);
330 for (x=0; x < ids.idlist_len; x++) {
331 lids->idlist_val[nd+x] = ids.idlist_val[x];
333 lids->idlist_len = nd+x;
334 if (!code && lnames) {
335 tnames.namelist_val = 0;
336 tnames.namelist_len = 0;
337 code = pr_IdToName (&ids, &tnames);
338 if (code) com_err (whoami, code, "translating ids");
341 for (x=0; x < ids.idlist_len; x++)
342 strcpy(lnames->namelist_val[nd+x], tnames.namelist_val[x]);
343 lnames->namelist_len = nd+x;
346 /* treat things as working if any of the lookups worked */
347 if (goodCount==0) code = PRNOENT;
349 if (lids->idlist_val) free (lids->idlist_val);
357 register struct cmd_syndesc *as;
359 register afs_int32 code;
360 struct cmd_item *u, *g;
362 for (u=as->parms[0].items; u; u=u->next) {
363 for (g=as->parms[1].items; g; g=g->next) {
364 code = pr_AddToGroup (u->data, g->data);
366 com_err (whoami, code,
367 "; unable to add user %s to group %s %s",
368 u->data, g->data, (force?"(ignored)":""));
378 register struct cmd_syndesc *as;
380 register afs_int32 code;
381 struct cmd_item *u, *g;
383 for (u=as->parms[0].items; u; u=u->next) {
384 for (g=as->parms[1].items; g; g=g->next) {
385 code = pr_RemoveUserFromGroup (u->data, g->data);
387 com_err (whoami, code,
388 "; unable to remove user %s from group %s %s",
389 u->data, g->data, (force?"(ignored)":""));
399 register struct cmd_syndesc *as;
401 register afs_int32 code;
408 if (GetNameOrId(as, &ids, &names)) return PRBADARG;
410 for (i=0; i<ids.idlist_len; i++) {
411 afs_int32 id = ids.idlist_val[i];
412 char *name = names.namelist_val[i];
414 if (id == ANONYMOUSID) continue; /* bad entry */
416 list.namelist_val = 0;
417 list.namelist_len = 0;
418 code = pr_IDListMembers(ids.idlist_val[i],&list);
420 com_err (whoami, code,
421 "; unable to get membership of %s (id: %d)", name, id);
424 if (id < 0) printf ("Members of %s (id: %d) are:\n", name, id);
425 else printf ("Groups %s (id: %d) is a member of:\n", name, id);
427 for (j=0; j<list.namelist_len; j++)
428 printf(" %s\n", list.namelist_val[j]);
429 if (list.namelist_val) free (list.namelist_val);
431 if (ids.idlist_val) free (ids.idlist_val);
432 if (names.namelist_val) free (names.namelist_val);
437 register struct cmd_syndesc *as;
439 register afs_int32 code;
444 if (GetNameOrId (as, &ids, &names)) return PRBADARG;
446 for (i=0; i<ids.idlist_len; i++) {
447 afs_int32 id = ids.idlist_val[i];
448 char *name = names.namelist_val[i];
450 if (id == ANONYMOUSID) continue;
452 code = pr_DeleteByID (id);
454 com_err (whoami, code, "deleting %s (id: %d) %s", name, id, (force?"(ignored)":""));
459 if (ids.idlist_val) free (ids.idlist_val);
460 if (names.namelist_val) free (names.namelist_val);
464 /* access bit translation info */
466 char *flags_upcase = "SOMA "; /* legal all access values */
467 char *flags_dncase = "s mar"; /* legal member acces values */
468 int flags_shift[5] = {2,1,2,2,1}; /* bits for each */
471 register struct cmd_syndesc *as;
473 register afs_int32 code;
475 int i, flag = 0, admin = 0;
476 namelist lnames, names;
479 struct prcheckentry aentry;
481 if (GetNameOrId (as, &ids, &names)) return PRBADARG;
484 lids.idlist_val = (afs_int32 *)malloc(sizeof(afs_int32)*2);
485 lnames.namelist_len = 0;
486 lnames.namelist_val = 0;
488 for (i=0; i<ids.idlist_len; i++) {
489 afs_int32 id = ids.idlist_val[i];
491 if (id == ANONYMOUSID) continue;
494 code = pr_ListEntry (id, &aentry);
497 com_err (whoami, code, "; unable to find entry for (id: %d)", id);
501 lids.idlist_val[0] = aentry.owner;
502 lids.idlist_val[1] = aentry.creator;
503 code = pr_IdToName (&lids, &lnames);
506 com_err (whoami, code,
507 "translating owner (%d) and creator (%d) ids",
508 aentry.owner, aentry.creator);
511 printf("Name: %s, id: %d, owner: %s, creator: %s,\n",
512 aentry.name, aentry.id,
513 lnames.namelist_val[0], lnames.namelist_val[1]);
514 printf (" membership: %d", aentry.count);
516 afs_int32 flags = aentry.flags;
519 access[5] = 0; /* null-terminate the string */
520 for (j=4; j>=0; j--) {
522 if (s == 1) new = flags & 1;
523 else new = flags & 3;
524 if (new == 0) c = '-';
527 if (c == ' ') c = flags_upcase[j];
528 } else if (new == 2) c = flags_upcase[j];
533 printf(", flags: %s", access);
535 if (aentry.id == SYSADMINID)
537 else if (!pr_IsAMemberOf (aentry.name, "system:administrators", &flag)) {
542 printf (", group quota: unlimited");
544 printf (", group quota: %d", aentry.ngroups);
546 printf (", foreign user quota=%d", aentry.nusers);
552 if (lnames.namelist_val) free(lnames.namelist_val);
553 if (lids.idlist_val) free(lids.idlist_val);
554 if (ids.idlist_val) free(ids.idlist_val);
560 struct cmd_syndesc *as;
563 afs_int32 flag, startindex, nentries, nextstartindex;
564 struct prlistentries *entriesp=0, *e;
568 if (as->parms[1].items) flag = PRGROUPS;
569 if (as->parms[0].items) flag |= PRUSERS;
571 printf("Name ID Owner Creator\n");
572 for (startindex = 0; startindex!=-1; startindex=nextstartindex) {
573 code = pr_ListEntries(flag, startindex, &nentries, &entriesp, &nextstartindex);
575 com_err (whoami, code, "; unable to list entries\n");
576 if (entriesp) free(entriesp);
580 /* Now display each of the entries we read */
581 for (i=0,e=entriesp; i<nentries; i++,e++) {
582 printf("%-25s %6d %6d %7d \n", e->name, e->id, e->owner, e->creator);
584 if (entriesp) free(entriesp);
590 register struct cmd_syndesc *as;
592 register afs_int32 code;
596 name = as->parms[0].items->data;
597 owner = as->parms[1].items->data;
598 code = pr_ChangeEntry (name, "", 0, owner);
599 if (code) com_err (whoami, code,
600 "; unable to change owner of %s to %s", name, owner);
605 register struct cmd_syndesc *as;
607 register afs_int32 code;
611 oldname = as->parms[0].items->data;
612 newname = as->parms[1].items->data;
613 code = pr_ChangeEntry (oldname, newname, 0, "");
614 if (code) com_err (whoami, code,
615 "; unable to change name of %s to %s",
621 register struct cmd_syndesc *as;
623 register afs_int32 code;
624 afs_int32 maxUser, maxGroup;
626 code = pr_ListMaxUserId (&maxUser);
628 com_err (whoami, code, "getting maximum user id");
630 code = pr_ListMaxGroupId (&maxGroup);
631 if (code) com_err (whoami, code, "getting maximum group id");
633 printf("Max user id is %d and max group id is %d.\n",
641 register struct cmd_syndesc *as;
643 register afs_int32 code;
647 if (as->parms[1].items) {
649 code = util_GetInt32 (as->parms[1].items->data, &maxid);
651 com_err (whoami, code,
652 "because id was: '%s'", as->parms[1].items->data);
655 code = pr_SetMaxUserId (maxid);
657 com_err (whoami, code,
658 "so couldn't set Max User Id to %d", maxid);
661 if (as->parms[0].items) {
663 code = util_GetInt32 (as->parms[0].items->data, &maxid);
665 com_err (whoami, code,
666 "because id was: '%s'", as->parms[0].items->data);
669 code = pr_SetMaxGroupId (maxid);
671 com_err (whoami, code,
672 "so couldn't set Max Group Id to %d", maxid);
675 if (!as->parms[0].items && !as->parms[1].items) {
677 printf("Must specify at least one of group or user.\n");
683 register struct cmd_syndesc *as;
685 register afs_int32 code;
689 afs_int32 mask, flags, ngroups, nusers;
691 if (GetNameOrId (as, &ids, &names)) return PRBADARG;
695 if (as->parms[1].items) { /* privacy bits */
696 char *access = as->parms[1].items->data;
699 if (strpbrk (access, "76543210") != 0) { /* all octal digits */
700 sscanf (access, "%lo", &flags);
701 } else { /* interpret flag bit names */
702 if (strlen(access) != 5) {
704 printf ("Access bits must be of the form 'somar', not %s\n", access);
707 if (strpbrk(access, "somar-") == 0) goto form_error;
709 for (i=0; i<5; i++) {
710 if (access[i] == flags_upcase[i]) new = 2;
711 else if (access[i] == flags_dncase[i]) new = 1;
712 else if (access[i] == '-') new = 0;
714 printf ("Access bits out of order or illegal:\n must be a combination of letters from '%s' or '%s' or hyphen, not %s\n", flags_upcase, flags_dncase, access);
717 flags <<= flags_shift[i];
718 if (flags_shift[i] == 1) {
724 mask |= PR_SF_ALLBITS;
726 if (as->parms[2].items) { /* limitgroups */
727 code = util_GetInt32 (as->parms[2].items->data, &ngroups);
729 com_err (whoami, code, "because ngroups was: '%s'",
730 as->parms[2].items->data);
733 mask |= PR_SF_NGROUPS;
736 if (as->parms[3].items) { /* limitgroups */
737 code = util_GetInt32 (as->parms[3].items->data, &nusers);
739 com_err (whoami, code, "because nusers was: '%s'",
740 as->parms[3].items->data);
743 mask |= PR_SF_NUSERS;
747 for (i=0; i<ids.idlist_len; i++) {
748 afs_int32 id = ids.idlist_val[i];
749 char *name = names.namelist_val[i];
750 if (id == ANONYMOUSID) continue;
751 code = pr_SetFieldsEntry (id, mask, flags, ngroups, nusers);
753 com_err (whoami, code,
754 "; unable to set fields for %s (id: %d)", name, id);
758 if (ids.idlist_val) free (ids.idlist_val);
759 if (names.namelist_val) free (names.namelist_val);
764 register struct cmd_syndesc *as;
765 { register afs_int32 code;
772 if (GetNameOrId (as, &ids, &names)) return PRBADARG;
774 for (i=0; i<ids.idlist_len; i++) {
775 afs_int32 oid = ids.idlist_val[i];
776 char *name = names.namelist_val[i];
778 if (oid == ANONYMOUSID) continue;
780 if (oid) printf("Groups owned by %s (id: %d) are:\n", name, oid);
781 else printf ("Orphaned groups are:\n");
784 list.namelist_val = 0;
785 list.namelist_len = 0;
786 code = pr_ListOwned(oid, &list, &more);
788 com_err (whoami, code,
789 "; unable to get owner list for %s (id: %d)",
794 for (j=0; j<list.namelist_len; j++)
795 printf (" %s\n", list.namelist_val[j]);
796 if (list.namelist_val) free(list.namelist_val);
800 if (ids.idlist_val) free (ids.idlist_val);
801 if (names.namelist_val) free (names.namelist_val);
805 static void add_std_args (ts)
806 register struct cmd_syndesc *ts;
808 char test_help[AFSDIR_PATH_MAX];
810 sprintf(test_help, "use config file in %s", AFSDIR_SERVER_ETC_DIRPATH);
813 cmd_AddParm(ts,"-cell",CMD_SINGLE,CMD_OPTIONAL,"cell name");
814 cmd_AddParm(ts,"-noauth",CMD_FLAG,CMD_OPTIONAL,"run unauthenticated");
815 cmd_AddParm(ts,"-test", CMD_FLAG,CMD_OPTIONAL|CMD_HIDE, test_help);
816 cmd_AddParm(ts,"-force",CMD_FLAG,CMD_OPTIONAL,"Continue oper despite reasonable errors");
820 static void add_NameOrId_args (ts)
821 register struct cmd_syndesc *ts;
823 cmd_AddParm(ts,"-name",CMD_LIST,CMD_OPTIONAL,"user or group name");
824 cmd_AddParm(ts,"-id",CMD_LIST,CMD_OPTIONAL,"user or group id");
828 #include "AFS_component_version_number.c"
830 int main (argc, argv)
834 register afs_int32 code;
835 register struct cmd_syndesc *ts;
841 WSAStartup(0x0101, &WSAjunk);
846 * The following signal action for AIX is necessary so that in case of a
847 * crash (i.e. core is generated) we can include the user's data section
848 * in the core dump. Unfortunately, by default, only a partial core is
849 * generated which, in many cases, isn't too useful.
851 struct sigaction nsa;
853 sigemptyset(&nsa.sa_mask);
854 nsa.sa_handler = SIG_DFL;
855 nsa.sa_flags = SA_FULLDUMP;
856 sigaction(SIGSEGV, &nsa, NULL);
859 ts = cmd_CreateSyntax("creategroup",CreateGroup,0,"create a new group");
860 cmd_AddParm(ts,"-name",CMD_LIST,0,"group name");
861 cmd_AddParm(ts,"-owner",CMD_SINGLE,CMD_OPTIONAL,"owner of the group");
862 cmd_AddParm(ts,"-id",CMD_LIST,CMD_OPTIONAL,"id (negated) for the group");
864 cmd_CreateAlias(ts,"cg");
866 ts = cmd_CreateSyntax("createuser",CreateUser,0,"create a new user");
867 cmd_AddParm(ts,"-name",CMD_LIST,0,"user name");
868 cmd_AddParm(ts,"-id",CMD_LIST,CMD_OPTIONAL,"user id");
870 cmd_CreateAlias(ts,"cu");
872 ts = cmd_CreateSyntax("adduser",AddToGroup,0,"add a user to a group");
873 cmd_AddParm(ts, "-user",CMD_LIST,0,"user name");
874 cmd_AddParm(ts, "-group",CMD_LIST,0,"group name");
877 ts = cmd_CreateSyntax("removeuser",RemoveFromGroup,0,
878 "remove a user from a group");
879 cmd_AddParm(ts,"-user",CMD_LIST,0,"user name");
880 cmd_AddParm(ts,"-group",CMD_LIST,0,"group name");
883 ts = cmd_CreateSyntax ("membership",ListMembership, 0,
884 "list membership of a user or group");
885 cmd_AddParm(ts,"-nameorid",CMD_LIST,0,"user or group name or id");
887 cmd_CreateAlias(ts,"groups");
889 ts = cmd_CreateSyntax ("delete", Delete, 0,
890 "delete a user or group from database");
891 cmd_AddParm(ts,"-nameorid",CMD_LIST,0,"user or group name or id");
894 ts = cmd_CreateSyntax ("examine", CheckEntry, 0, "examine an entry");
895 cmd_AddParm(ts,"-nameorid",CMD_LIST,0,"user or group name or id");
897 cmd_CreateAlias (ts, "check");
899 ts = cmd_CreateSyntax("chown",ChownGroup,0,"change ownership of a group");
900 cmd_AddParm(ts,"-name",CMD_SINGLE,0,"group name");
901 cmd_AddParm(ts,"-owner",CMD_SINGLE,0,"new owner");
904 ts = cmd_CreateSyntax("rename",ChangeName,0,"rename user or group");
905 cmd_AddParm(ts,"-oldname",CMD_SINGLE,0,"old name");
906 cmd_AddParm(ts,"-newname",CMD_SINGLE,0,"new name");
908 cmd_CreateAlias(ts, "chname");
910 ts = cmd_CreateSyntax("listmax",ListMax,0,"list max id");
913 ts = cmd_CreateSyntax("setmax",SetMax,0,"set max id");
914 cmd_AddParm(ts,"-group",CMD_SINGLE,CMD_OPTIONAL,"group max");
915 cmd_AddParm(ts,"-user",CMD_SINGLE,CMD_OPTIONAL,"user max");
918 ts = cmd_CreateSyntax("setfields",SetFields,0,"set fields for an entry");
919 cmd_AddParm(ts,"-nameorid",CMD_LIST,0,"user or group name or id");
920 cmd_AddParm (ts, "-access", CMD_SINGLE, CMD_OPTIONAL,
921 "set privacy flags");
922 cmd_AddParm (ts, "-groupquota", CMD_SINGLE, CMD_OPTIONAL,
923 "set limit on group creation");
925 cmd_AddParm (ts, "-userquota", CMD_SINGLE, CMD_OPTIONAL,
926 "set limit on foreign user creation");
930 ts = cmd_CreateSyntax
931 ("listowned", ListOwned, 0,
932 "list groups owned by an entry or zero id gets orphaned groups");
933 cmd_AddParm(ts,"-nameorid",CMD_LIST,0,"user or group name or id");
936 ts = cmd_CreateSyntax
937 ("listentries", ListEntries, 0,
938 "list users/groups in the protection database");
939 cmd_AddParm(ts, "-users", CMD_FLAG, CMD_OPTIONAL, "list user entries");
940 cmd_AddParm(ts, "-groups", CMD_FLAG, CMD_OPTIONAL, "list group entries");
943 cmd_SetBeforeProc(GetGlobals,0);
944 cmd_SetAfterProc(CleanUp,0);
945 code = cmd_Dispatch(argc,argv);