3 * Command line oriented Moira List tool.
5 * by Mark Rosenstein, September 1988.
7 * Copyright 1989 by the Massachusetts Institute of Technology.
9 * (c) Copyright 1988 by the Massachusetts Institute of Technology.
10 * For copying and distribution information, please see the file
14 /* ### Aren't there a lot of sq abstraction barrier violations here?
15 Do we need to improve the support for queue operations? */
17 #include <mit-copyright.h>
24 #include <moira_site.h>
27 static char blanche_rcsid[] = "$Header$";
36 /* It is important to membercmp that M_USER < M_LIST < M_STRING */
43 /* argument parsing macro */
44 #define argis(a, b) (!strcmp(*arg + 1, a) || !strcmp(*arg + 1, b))
46 /* flags from command line */
47 int infoflg, verbose, syncflg, memberflg, recursflg, noauth;
48 int showusers, showstrings, showkerberos, showlists;
49 int createflag, setinfo, active, public, hidden, maillist, grouplist;
53 /* various member lists */
54 struct save_queue *addlist, *dellist, *memberlist, *synclist;
56 char *listname, *whoami;
58 int show_list_info(), show_list_count(), get_list_members(), scream();
59 int show_list_members(), membercmp(), save_list_info();
60 struct member *parse_member();
62 int main(int argc, char **argv)
66 char *membervec[3], *motd;
67 struct member *memberstruct;
68 char *server = NULL, *p;
70 /* clear all flags & lists */
71 infoflg = verbose = syncflg = memberflg = recursflg = 0;
72 noauth = showusers = showstrings = showkerberos = showlists = 0;
73 createflag = setinfo = 0;
74 active = public = hidden = maillist = grouplist = -1;
75 listname = newname = desc = NULL;
77 addlist = sq_create();
78 dellist = sq_create();
79 memberlist = sq_create();
80 synclist = sq_create();
85 /* parse args, building addlist, dellist, & synclist */
86 while (++arg - argv < argc)
90 if (argis("m", "members"))
92 else if (argis("u", "users"))
94 else if (argis("s", "strings"))
96 else if (argis("l", "lists"))
98 else if (argis("k", "kerberos"))
100 else if (argis("i", "info"))
102 else if (argis("n", "noauth"))
104 else if (argis("v", "verbose"))
106 else if (argis("r", "recursive"))
108 else if (argis("S", "server") || argis("db", "database"))
110 if (arg - argv < argc - 1)
118 else if (argis("a", "add"))
120 if (arg - argv < argc - 1)
123 if ((memberstruct = parse_member(*arg)))
124 sq_save_data(addlist, memberstruct);
129 else if (argis("al", "addlist"))
131 if (arg - argv < argc - 1)
134 get_members_from_file(*arg, addlist);
139 else if (argis("d", "delete"))
141 if (arg - argv < argc - 1)
144 if ((memberstruct = parse_member(*arg)))
145 sq_save_data(dellist, memberstruct);
150 else if (argis("dl", "deletelist"))
152 if (arg - argv < argc - 1)
155 get_members_from_file(*arg, dellist);
160 else if (argis("f", "file"))
162 if (arg - argv < argc - 1)
166 get_members_from_file(*arg, synclist);
171 else if (argis("C", "create"))
173 else if (argis("P", "public"))
178 else if (argis("NP", "private"))
183 else if (argis("A", "active"))
188 else if (argis("I", "inactive"))
193 else if (argis("V", "visible"))
198 else if (argis("H", "hidden"))
203 else if (argis("M", "mail"))
208 else if (argis("NM", "notmail"))
213 else if (argis("G", "group"))
218 else if (argis("NG", "notgroup"))
223 else if (argis("D", "desc"))
225 if (arg - argv < argc - 1)
234 else if (argis("O", "owner"))
236 if (arg - argv < argc - 1)
240 owner = parse_member(*arg);
245 else if (argis("R", "rename"))
247 if (arg - argv < argc - 1)
259 else if (listname == NULL)
264 if (listname == NULL)
267 /* if no other options specified, turn on list members flag */
268 if (!(infoflg || syncflg || createflag || setinfo ||
269 addlist->q_next != addlist || dellist->q_next != dellist))
272 /* If none of {users,strings,lists,kerberos} specified, turn them all on */
273 if (!(showusers || showstrings || showlists || showkerberos))
274 showusers = showstrings = showlists = showkerberos = 1;
277 if ((status = mr_connect(server)))
279 com_err(whoami, status, "unable to connect to the Moira server");
282 if ((status = mr_motd(&motd)))
284 com_err(whoami, status, "unable to check server status");
289 fprintf(stderr, "The Moira server is currently unavailable:\n%s\n",
295 if (!noauth && (status = mr_auth("blanche")))
297 if (status == MR_USER_AUTH)
298 com_err(whoami, status, "");
301 com_err(whoami, status, "unable to authenticate to Moira");
303 " Try the -noauth flag if you don't need authentication");
308 /* create if needed */
314 argv[1] = (active == 0) ? "0" : "1";
315 argv[2] = (public == 1) ? "1" : "0";
316 argv[3] = (hidden == 1) ? "1" : "0";
317 argv[4] = (maillist == 0) ? "0" : "1";
318 argv[5] = (grouplist == 1) ? "1" : "0";
319 argv[6] = UNIQUE_GID;
320 argv[9] = desc ? desc : "none";
324 argv[8] = owner->name;
330 status = mr_query("add_list", 10, argv, scream, NULL);
331 if (owner->type != M_ANY || status != MR_USER)
336 status = mr_query("add_list", 10, argv, scream, NULL);
340 argv[7] = "KERBEROS";
341 status = mr_query("add_list", 10, argv, scream, NULL);
348 argv[8] = getenv("USER");
350 status = mr_query("add_list", 10, argv, scream, NULL);
355 com_err(whoami, status, "while creating list.");
363 status = mr_query("get_list_info", 1, &listname,
364 save_list_info, (char *)argv);
367 com_err(whoami, status, "while getting list information");
375 argv[2] = active ? "1" : "0";
377 argv[3] = public ? "1" : "0";
379 argv[4] = hidden ? "1" : "0";
381 argv[5] = maillist ? "1" : "0";
383 argv[6] = grouplist ? "1" : "0";
389 argv[9] = owner->name;
395 status = mr_query("update_list", 11, argv, scream, NULL);
396 if (owner->type != M_ANY || status != MR_USER)
401 status = mr_query("update_list", 11, argv, scream, NULL);
405 argv[8] = "KERBEROS";
406 status = mr_query("update_list", 11, argv, scream, NULL);
411 status = mr_query("update_list", 11, argv, scream, NULL);
414 com_err(whoami, status, "while updating list.");
419 /* display list info if requested to */
422 status = mr_query("get_list_info", 1, &listname, show_list_info, NULL);
424 com_err(whoami, status, "while getting list information");
425 if (verbose && !memberflg)
427 status = mr_query("count_members_of_list", 1, &listname,
428 show_list_count, NULL);
430 com_err(whoami, status, "while getting list count");
434 /* if we're synchronizing to a file, we need to:
435 * get the current members of the list
436 * for each member of the sync file
437 * if they are on the list, remove them from the in-memory copy
438 * if they're not on the list, add them to add-list
439 * if anyone is left on the in-memory copy, put them on the delete-list
440 * lastly, reset memberlist so we can use it again later
444 status = mr_query("get_members_of_list", 1, &listname,
445 get_list_members, (char *)memberlist);
448 com_err(whoami, status, "getting members of list %s", listname);
451 while (sq_get_data(synclist, &memberstruct))
453 struct save_queue *q;
456 for (q = memberlist->q_next; q != memberlist; q = q->q_next)
458 if (membercmp(q->q_data, memberstruct) == 0)
460 q->q_prev->q_next = q->q_next;
461 q->q_next->q_prev = q->q_prev;
467 sq_save_data(addlist, memberstruct);
469 while (sq_get_data(memberlist, &memberstruct))
470 sq_save_data(dellist, memberstruct);
471 sq_destroy(memberlist);
472 memberlist = sq_create();
475 /* Process the add list */
476 while (sq_get_data(addlist, &memberstruct))
478 /* canonicalize string if necessary */
479 if (memberstruct->type == M_STRING &&
480 (p = strchr(memberstruct->name, '@')))
482 char *host = canonicalize_hostname(strsave(++p));
483 static char **mailhubs = NULL;
492 mailhubs = malloc(sizeof(char *));
494 status = mr_query("get_alias", 3, argv, collect,
496 if (status != MR_SUCCESS && status != MR_NO_MATCH)
498 com_err(whoami, status,
499 " while reading list of MAILHUB servers");
503 for (i = 0; (p = mailhubs[i]); i++)
505 if (!strcasecmp(p, host))
507 host = strsave(memberstruct->name);
508 *(strchr(memberstruct->name, '@')) = 0;
509 memberstruct->type = M_ANY;
510 fprintf(stderr, "Warning: \"STRING:%s\" converted to "
511 "\"%s\" because it is a local name.\n",
512 host, memberstruct->name);
518 /* now continue adding member */
519 membervec[0] = listname;
520 membervec[2] = memberstruct->name;
523 printf("Adding member ");
524 show_list_member(memberstruct);
526 switch (memberstruct->type)
530 membervec[1] = "USER";
531 status = mr_query("add_member_to_list", 3, membervec, scream, NULL);
532 if (status == MR_SUCCESS)
534 else if (status != MR_USER || memberstruct->type != M_ANY)
536 com_err(whoami, status, "while adding member %s to %s",
537 memberstruct->name, listname);
542 membervec[1] = "LIST";
543 status = mr_query("add_member_to_list", 3, membervec,
545 if (status == MR_SUCCESS)
547 if (!strcmp(membervec[0], getenv("USER")))
549 fprintf(stderr, "\nWARNING: \"LIST:%s\" was just added "
550 "to list \"%s\".\n", membervec[2], membervec[0]);
551 fprintf(stderr, "If you meant to add yourself to the list "
552 "\"%s\", type:\n", membervec[2]);
553 fprintf(stderr, "\tblanche %s -d %s\t(to undo this)\n",
554 membervec[0], membervec[2]);
555 fprintf(stderr, "\tblanche %s -a %s\t(to add yourself to "
556 "that list)\n", membervec[2], membervec[0]);
560 else if (status != MR_LIST || memberstruct->type != M_ANY)
562 com_err(whoami, status, "while adding member %s to %s",
563 memberstruct->name, listname);
568 if (memberstruct->type == M_ANY &&
569 !strchr(memberstruct->name, '@') &&
570 !strchr(memberstruct->name, '!') &&
571 !strchr(memberstruct->name, '%'))
573 /* if user is trying to add something which isn't a
574 remote string, or a list, or a user, and didn't
575 explicitly specify `STRING:', it's probably a typo */
576 com_err(whoami, MR_NO_MATCH, "while adding member %s to %s",
577 memberstruct->name, listname);
582 membervec[1] = "STRING";
583 status = mr_query("add_member_to_list", 3, membervec,
585 if (status != MR_SUCCESS)
587 com_err(whoami, status, "while adding member %s to %s",
588 memberstruct->name, listname);
593 membervec[1] = "KERBEROS";
594 status = mr_query("add_member_to_list", 3, membervec,
596 if (status != MR_SUCCESS)
598 com_err(whoami, status, "while adding member %s to %s",
599 memberstruct->name, listname);
605 /* Process the delete list */
606 while (sq_get_data(dellist, &memberstruct))
608 membervec[0] = listname;
609 membervec[2] = memberstruct->name;
612 printf("Deleting member ");
613 show_list_member(memberstruct);
615 switch (memberstruct->type)
619 membervec[1] = "USER";
620 status = mr_query("delete_member_from_list", 3, membervec,
622 if (status == MR_SUCCESS)
624 else if ((status != MR_USER && status != MR_NO_MATCH) ||
625 memberstruct->type != M_ANY)
627 com_err(whoami, status, "while deleting member %s from %s",
628 memberstruct->name, listname);
633 membervec[1] = "LIST";
634 status = mr_query("delete_member_from_list", 3, membervec,
636 if (status == MR_SUCCESS)
638 else if ((status != MR_LIST && status != MR_NO_MATCH) ||
639 memberstruct->type != M_ANY)
641 if (status == MR_PERM && memberstruct->type == M_ANY &&
642 !strcmp(membervec[2], getenv("USER")))
644 /* M_ANY means we've fallen through from the user
645 * case. The user is trying to remove himself from
646 * a list, but we got MR_USER or MR_NO_MATCH above,
647 * meaning he's not really on it, and we got MR_PERM
648 * when trying to remove LIST:$USER because he's not
649 * on the acl. That error is useless, so return
650 * MR_NO_MATCH instead. However, this will generate the
651 * wrong error if the user was trying to remove the list
652 * with his username from a list he doesn't administrate
653 * without explicitly specifying "list:".
655 status = MR_NO_MATCH;
657 com_err(whoami, status, "while deleting member %s from %s",
658 memberstruct->name, listname);
663 membervec[1] = "STRING";
664 status = mr_query("delete_member_from_list", 3, membervec,
666 if (status == MR_STRING && memberstruct->type == M_ANY)
668 com_err(whoami, 0, " Unable to find member %s to delete from %s",
669 memberstruct->name, listname);
671 if (!strcmp(membervec[0], getenv("USER")))
673 fprintf(stderr, "(If you were trying to remove yourself "
674 "from the list \"%s\",\n", membervec[2]);
675 fprintf(stderr, "the correct command is \"blanche %s -d "
676 "%s\".)\n", membervec[2], membervec[0]);
679 else if (status != MR_SUCCESS)
681 com_err(whoami, status, "while deleting member %s from %s",
682 memberstruct->name, listname);
687 membervec[1] = "KERBEROS";
688 status = mr_query("delete_member_from_list", 3, membervec,
690 if (status != MR_SUCCESS)
692 com_err(whoami, status, "while deleting member %s from %s",
693 memberstruct->name, listname);
699 /* Display the members of the list now, if requested */
703 recursive_display_list_members();
706 status = mr_query("get_members_of_list", 1, &listname,
707 get_list_members, (char *)memberlist);
709 com_err(whoami, status, "while getting members of list %s",
711 while (sq_get_data(memberlist, &memberstruct))
712 show_list_member(memberstruct);
718 exit(success ? 0 : 1);
723 fprintf(stderr, "Usage: %s listname [options]\n", argv[0]);
724 fprintf(stderr, "Options are\n");
725 fprintf(stderr, " %-39s%-39s\n", "-v | -verbose",
727 fprintf(stderr, " %-39s%-39s\n", "-m | -members",
728 "-R | -rename newname");
729 fprintf(stderr, " %-39s%-39s\n", "-u | -users",
731 fprintf(stderr, " %-39s%-39s\n", "-l | -lists",
733 fprintf(stderr, " %-39s%-39s\n", "-s | -strings",
735 fprintf(stderr, " %-39s%-39s\n", "-k | -kerberos",
737 fprintf(stderr, " %-39s%-39s\n", "-i | -info",
739 fprintf(stderr, " %-39s%-39s\n", "-r | -recursive",
741 fprintf(stderr, " %-39s%-39s\n", "-a | -add member",
743 fprintf(stderr, " %-39s%-39s\n", "-d | -delete member",
745 fprintf(stderr, " %-39s%-39s\n", "-al | -addlist filename",
747 fprintf(stderr, " %-39s%-39s\n", "-dl | -deletelist filename",
749 fprintf(stderr, " %-39s%-39s\n", "-f | -file filename",
750 "-D | -desc description");
751 fprintf(stderr, " %-39s%-39s\n", "-n | -noauth",
752 "-O | -owner owner");
753 fprintf(stderr, " %-39s%-39s\n", "-db | -database host[:port]",
759 /* Display the members stored in the queue */
761 show_list_member(struct member *memberstruct)
765 switch (memberstruct->type)
788 printf("%s\n", memberstruct->name);
793 printf("%s:%s\n", s, memberstruct->name);
796 if (memberstruct->type == M_LIST)
797 printf("LIST:%s\n", memberstruct->name);
798 else if (memberstruct->type == M_KERBEROS)
799 printf("KERBEROS:%s\n", memberstruct->name);
800 else if (memberstruct->type == M_STRING &&
801 !strchr(memberstruct->name, '@'))
802 printf("STRING:%s\n", memberstruct->name);
804 printf("%s\n", memberstruct->name);
809 /* Show the retrieved information about a list */
811 int show_list_info(int argc, char **argv, int hint)
813 printf("List: %s\n", argv[0]);
814 printf("Description: %s\n", argv[9]);
815 printf("Flags: %s, %s, and %s\n",
816 atoi(argv[1]) ? "active" : "inactive",
817 atoi(argv[2]) ? "public" : "private",
818 atoi(argv[3]) ? "hidden" : "visible");
819 printf("%s is %sa maillist and is %sa group", argv[0],
820 atoi(argv[4]) ? "" : "not ",
821 atoi(argv[5]) ? "" : "not ");
823 printf(" with GID %d\n", atoi(argv[6]));
826 printf("Owner: %s %s\n", argv[7], argv[8]);
827 printf("Last modified by %s with %s on %s\n", argv[11], argv[12], argv[10]);
832 /* Copy retrieved information about a list into a new argv */
834 int save_list_info(int argc, char **argv, int hint)
836 char **nargv = (char **)hint;
838 for (argc = 0; argc < 10; argc++)
839 nargv[argc + 1] = strdup(argv[argc]);
843 /* Show the retrieve list member count */
845 show_list_count(int argc, char **argv, int hint)
847 printf("Members: %s\n", argv[0]);
851 /* Recursively find all of the members of listname, and then display them */
853 recursive_display_list_members(void)
855 int status, count, savecount;
856 struct save_queue *lists, *members;
857 struct member *m, *m1, *data;
860 members = sq_create();
861 m = malloc(sizeof(struct member));
864 sq_save_data(lists, m);
866 while (sq_get_data(lists, &m))
868 sq_destroy(memberlist);
869 memberlist = sq_create();
870 status = mr_query("get_members_of_list", 1, &(m->name),
871 get_list_members, (char *)memberlist);
873 com_err(whoami, status, "while getting members of list %s", m->name);
874 while (sq_get_data(memberlist, &m1))
876 if (m1->type == M_LIST)
877 unique_add_member(lists, m1);
879 unique_add_member(members, m1);
882 savecount = count = sq_count_elts(members);
883 data = malloc(count * sizeof(struct member));
885 while (sq_get_data(members, &m))
886 memcpy(&data[count++], m, sizeof(struct member));
887 qsort(data, count, sizeof(struct member), membercmp);
888 for (count = 0; count < savecount; count++)
889 show_list_member(&data[count]);
893 /* add a struct member to a queue if that member isn't already there. */
895 unique_add_member(struct save_queue *q, struct member *m)
897 struct save_queue *qp;
899 for (qp = q->q_next; qp != q; qp = qp->q_next)
901 if (!membercmp(qp->q_data, m))
908 /* Collect the retrieved members of the list */
910 int get_list_members(int argc, char **argv, struct save_queue *q)
914 m = malloc(sizeof(struct member));
927 m->type = M_KERBEROS;
930 m->name = strsave(argv[1]);
936 /* Called only if a query returns a value that we weren't expecting */
940 fprintf(stderr, "Programmer botch\n");
945 /* Open file, parse members from file, and put them on the specified queue */
946 get_members_from_file(char *filename, struct save_queue *queue)
950 struct member *memberstruct;
952 if (!strcmp(filename, "-"))
956 in = fopen(filename, "r");
959 com_err(whoami, errno, "while opening %s for input", filename);
964 while (fgets(buf, BUFSIZ, in))
966 if ((memberstruct = parse_member(buf)))
967 sq_save_data(queue, memberstruct);
971 com_err(whoami, errno, "while reading from %s", filename);
977 /* Collect the possible expansions of the alias MAILHUB */
979 int collect(int argc, char **argv, char ***list)
983 for (i = 0; (*list)[i]; i++)
985 *list = realloc(*list, (i + 2) * sizeof(char *));
986 (*list)[i] = strsave(argv[2]);
987 (*list)[i + 1] = NULL;
992 /* Parse a line of input, fetching a member. NULL is returned if a member
993 * is not found. ';' is a comment character.
996 struct member *parse_member(char *s)
1001 while (*s && isspace(*s))
1004 while (*p && *p != '\n' && *p != ';')
1006 if (isprint(*p) && !isspace(*p))
1013 if (p == s || strlen(s) == 0)
1016 if (!(m = malloc(sizeof(struct member))))
1019 if ((p = strchr(s, ':')))
1023 if (!strcasecmp("user", s))
1025 else if (!strcasecmp("list", s))
1027 else if (!strcasecmp("string", s))
1029 else if (!strcasecmp("kerberos", s))
1030 m->type = M_KERBEROS;
1037 m->name = strsave(m->name);
1041 m->name = strsave(s);
1049 * This routine two compares members by the following rules:
1050 * 1. A USER is less than a LIST
1051 * 2. A LIST is less than a STRING
1052 * 3. If two members are of the same type, the one alphabetically first
1053 * is less than the other
1054 * It returs < 0 if the first member is less, 0 if they are identical, and
1055 * > 0 if the second member is less (the first member is greater).
1058 int membercmp(struct member *m1, struct member *m2)
1060 if (m1->type == M_ANY || m2->type == M_ANY || (m1->type == m2->type))
1061 return strcmp(m1->name, m2->name);
1063 return m1->type - m2->type;
1067 int sq_count_elts(struct save_queue *q)
1073 while (sq_get_data(q, &foo))