3 * This is the file utils.c for the Moira Client, which allows users
4 * to quickly and easily maintain most parts of the Moira database.
5 * It Contains: Many useful utility functions.
8 * By: Chris D. Peterson
10 * Copyright (C) 1988-1998 by the Massachusetts Institute of Technology.
11 * For copying and distribution information, please see the file
15 #include <mit-copyright.h>
18 #include <moira_site.h>
23 #include <sys/types.h>
26 #include <netinet/in.h>
27 #include <arpa/inet.h>
28 #include <netdb.h> /* for gethostbyname. */
39 /* Function Name: AddQueue
40 * Description: Adds an element to a queue
41 * Arguments: elem, pred - element and its predecessor.
45 void AddQueue(struct mqelem *elem, struct mqelem *pred)
54 elem->q_forw = pred->q_forw;
58 /* Function Name: RemoveQueue
59 * Description: removes an element from a queue.
64 static void RemoveQueue(struct mqelem *elem)
67 elem->q_forw->q_back = elem->q_back;
69 elem->q_back->q_forw = elem->q_forw;
73 /* CopyInfo: allocates memory for a copy of a NULL terminated array of
74 * strings <and returns a pointer to the copy.
77 char **CopyInfo(char **info)
82 ret = malloc(sizeof(char *) * (CountArgs(info) + 1));
85 for (i = 0; info[i]; i++)
86 ret[i] = strdup(info[i]);
92 /* Function Name: FreeInfo
93 * Description: Frees all elements of a NULL terminated arrary of char*'s
94 * Arguments: info - array who's elements we are to free.
98 void FreeInfo(char **info)
101 FreeAndClear(info++, TRUE);
104 /* Function Name: FreeAndClear - I couldn't resist the name.
105 * Description: Clears pointer and optionially frees it.
106 * Arguments: pointer - pointer to work with.
107 * free_it - if TRUE then free pointer.
111 void FreeAndClear(char **pointer, Bool free_it)
120 /* Function Name: QueueTop
121 * Description: returns a mqelem pointer that points to the top of
123 * Arguments: elem - any element of a queue.
124 * Returns: top element of a queue.
127 struct mqelem *QueueTop(struct mqelem *elem)
129 if (!elem) /* NULL returns NULL. */
136 /* Function Name: FreeQueueElem
137 * Description: Frees one element of the queue.
138 * Arguments: elem - the elem to free.
142 static void FreeQueueElem(struct mqelem *elem)
144 char **info = elem->q_data;
148 FreeInfo(info); /* free info fields */
149 free(elem->q_data); /* free info array itself. */
151 RemoveQueue(elem); /* remove this element from the queue */
152 free(elem); /* free its space. */
155 /* Function Name: FreeQueue
156 * Description: Cleans up the queue
157 * Arguments: elem - any element of the queue.
161 void FreeQueue(struct mqelem *elem)
163 struct mqelem *temp, *local = QueueTop(elem);
167 temp = local->q_forw;
168 FreeQueueElem(local);
173 /* Function Name: QueueCount
174 * Description: Counts the number of elements in a queue
175 * Arguments: elem - any element in the queue.
179 int QueueCount(struct mqelem *elem)
182 elem = QueueTop(elem);
191 /* Function Name: StoreInfo
192 * Description: Stores information from a moira query into a queue.
193 * Arguments: argc, argv, - information returned from the query returned
195 * data - the previous element on the queue, this data will be
196 * stored in a mqelem struct immediatly after this elem.
197 * If NULL then a new queue will be created. This value
198 * is updated to the current element at the end off the
200 * Returns: MR_CONT, or MR_ABORT if it has problems.
203 int StoreInfo(int argc, char **argv, void *data)
205 char **info = malloc(MAX_ARGS_SIZE * sizeof(char *));
206 struct mqelem **old_elem = data;
207 struct mqelem *new_elem = malloc(sizeof(struct mqelem));
210 if (!new_elem || !info)
212 Put_message("Could Not allocate more memory.");
213 FreeQueue(*old_elem);
218 for (count = 0; count < argc; count++)
219 info[count] = strdup(argv[count]);
220 info[count] = NULL; /* NULL terminate this sucker. */
222 new_elem->q_data = info;
223 AddQueue(new_elem, *old_elem);
225 *old_elem = new_elem;
229 /* Function Name: CountArgs
230 * Description: Retrieve the number of args in a null terminated
232 * Arguments: info - the argument list.
233 * Returns: number if args in the list.
236 int CountArgs(char **info)
249 /* Function Name: PromptWithDefault
250 * Description: allows a user to be prompted for input, and given a
252 * Arguments: prompt - the prompt string.
253 * buf, buflen - buffer to be returned and its MAX size?
254 * default value for the answer.
255 * Returns: zero on failure
258 int PromptWithDefault(char *prompt, char *buf, int buflen, char *def)
266 strncpy(buf, parsed_argv[0], buflen);
267 sprintf(tmp, "%s: %s", prompt, buf);
273 sprintf(tmp, "%s [%s]: ", prompt, def ? def : "");
274 ans = Prompt_input(tmp, buf, buflen);
277 else if (!strcmp(buf, "\"\""))
282 /* Function Name: YesNoQuestion
283 * Description: This prompts the user for the answer to a yes-no or
284 * true-false question.
285 * Arguments: prompt - the prompt for the user.
286 * bool_def - the default value either TRUE or FALSE.
287 * Returns: TRUE or FALSE or -1 on error
290 Bool YesNoQuestion(char *prompt, int bool_def)
296 if (!PromptWithDefault(prompt, ans, 2, bool_def ? "y" : "n"))
307 Put_message("Please answer 'y' or 'n'.");
313 /* Function Name: YesNoQuitQuestion
314 * Description: This prompts the user for the answer to a yes-no or
315 * true-false question, with a quit option.
316 * Arguments: prompt - the prompt for the user.
317 * bool_def - the default value either TRUE or FALSE.
318 * Returns: TRUE or FALSE or -1 on error or QUIT
319 * NOTE: It is not possible to have quit the default, but then I don't
320 * seem to need this functionality.
323 Bool YesNoQuitQuestion(char *prompt, int bool_def)
329 if (!PromptWithDefault(prompt, ans, 2, bool_def ? "y" : "n"))
343 Put_message("Please answer 'y', 'n' or 'q'.");
349 /* Function Name: Confirm
350 * Description: This function asks the user to confirm the action
351 * he is about to take, used for things like deleting.
352 * Arguments: prompt - the prompt string.
353 * Returns: TRUE/FALSE - wether or not the confirmation occured.
356 Bool Confirm(char *prompt)
358 return !verbose || (YesNoQuestion(prompt, FALSE) == TRUE);
361 /* Function Name: ValidName
362 * Description: This function checks to see if we have a valid list name.
363 * Arguments: s - the list name.
364 * Returns: TRUE if Valid.
367 Bool ValidName(char *s)
370 Put_message("Please use a non-empty name.");
371 else if (strchr(s, ' '))
372 Put_message("You cannot use space (' ') in this name.");
373 else if (strchr(s, '*') || strchr(s, '?'))
374 Put_message("Wildcards not accepted here.");
380 /* Function Name: ToggleVerboseMode
381 * Description: This function toggles the verbose mode.
383 * Returns: DM_NORMAL.
386 int ToggleVerboseMode(int argc, char **argv)
391 Put_message("Delete functions will first confirm");
393 Put_message("Delete functions will be silent");
398 /* Function Name: SlipInNewName
399 * Description: Slips the new name into the number 2 slot of a list, and
400 * returns a pointer to the new list.
401 * Arguments: info - list that needs this name slipped into it.
402 * name - the name to slip into the list.
403 * Returns: a pointer to the new list.
404 * NOTE: This screws up the numbers of the elements of the array in a
408 void SlipInNewName(char **info, char *name)
412 /* This also pushes the NULL down. */
413 for (i = CountArgs(info); i > 0; i--)
414 info[i + 1] = info[i];
415 info[1] = name; /* now slip in the name. */
418 /* Function Name: GetValueFromUser
419 * Description: This function gets a value from a user for the field
421 * Arguments: prompt - prompt for user.
422 * pointer - pointer to default value, will be returned
424 * Returns: SUB_ERROR if break hit (^C).
427 int GetValueFromUser(char *prompt, char **pointer)
431 if (PromptWithDefault(prompt, buf, BUFSIZ, *pointer) == 0)
435 * If these are the same then there is no need to allocate a new string.
437 * a difference that makes no difference, IS no difference.
442 if (strcmp(buf, *pointer))
445 *pointer = strdup(buf);
451 /* Function Name: GetYesNoValueFromUser
452 * Description: This function gets a value from a user for the field
454 * Arguments: prompt - prompt for user.
455 * pointer - pointer to default value, will be returned
457 * Returns: SUB_ERROR if break hit (^C).
460 int GetYesNoValueFromUser(char *prompt, char **pointer)
462 char user_prompt[BUFSIZ];
465 if (!strcmp(*pointer, DEFAULT_YES))
470 sprintf(user_prompt, "%s (y/n)", prompt);
472 switch (YesNoQuestion(user_prompt, default_val))
477 *pointer = strdup(DEFAULT_YES);
482 *pointer = strdup(DEFAULT_NO);
491 /* Function Name: GetFSVal
492 * Description: asks about a specific filesystem value.
493 * Arguments: name - string for this type of filesystem.
494 * mask - mask for this type of filesystem.
495 * current - current filesystem state. (for defaults).
496 * new - new filesystem state.
497 * Returns: TRUE if successful.
500 static Bool GetFSVal(char *name, int mask, int current, int *new)
502 char temp_buf[BUFSIZ];
503 sprintf(temp_buf, "Is this a %s filsystem", name);
504 switch (YesNoQuestion(temp_buf, ((mask & current) == mask)))
510 break; /* zero by default. */
517 /* Function Name: GetFSTypes
518 * Description: Allows user to specify filsystem types.
519 * Arguments: current - current value of filsystem, freed here.
520 * Returns: SUB_ERROR on ^C.
523 int GetFSTypes(char **current, int options)
525 int c_value, new_val = 0; /* current value of filesys type (int). */
526 char ret_value[BUFSIZ];
531 c_value = atoi(*current);
533 if (GetFSVal("student", MR_FS_STUDENT, c_value, &new_val) == FALSE)
535 if (GetFSVal("faculty", MR_FS_FACULTY, c_value, &new_val) == FALSE)
537 if (GetFSVal("staff", MR_FS_STAFF, c_value, &new_val) == FALSE)
539 if (GetFSVal("miscellaneous", MR_FS_MISC, c_value, &new_val) == FALSE)
543 if (GetFSVal("Group Quotas", MR_FS_GROUPQUOTA, c_value, &new_val) ==
548 FreeAndClear(current, TRUE);
549 sprintf(ret_value, "%d", new_val);
550 *current = strdup(ret_value);
554 /* atot: convert ASCII integer unix time into human readable date string */
556 char *atot(char *itime)
561 time = (time_t) atoi(itime);
568 /* Function Name: Print
569 * Description: prints out all the arguments on a single line.
570 * Arguments: argc, argv - the standard Moira arguments.
571 * callback - the callback function - NOT USED.
575 int Print(int argc, char **argv, void *callback)
581 strcpy(buf, argv[0]); /* no newline 'cause Put_message adds one */
582 for (i = 1; i < argc; i++)
583 sprintf(buf, "%s %s", buf, argv[i]);
589 /* Function Name: PrintByType
590 * Description: This function prints all members of the type specified
591 * by the callback arg, unless the callback is NULL, in which
592 * case it prints all members.
593 * Arguments: argc, argc - normal arguments for mr_callback function.
594 * callback - either a type of member or NULL.
595 * Returns: MR_CONT or MR_QUIT.
598 int PrintByType(int argc, char **argv, void *callback)
601 return Print(argc, argv, callback);
602 if (!strcmp(argv[0], callback))
603 return Print(argc, argv, callback);
607 /* Function Name: PrintHelp
608 * Description: Prints Help Information in a NULL terminated
610 * Arguments: message.
611 * Returns: DM_NORMAL.
614 int PrintHelp(char **message)
618 for (i = 0; i < CountArgs(message); i++)
619 Put_message(message[i]);
624 /* Function Name: Loop
625 * Description: This function goes through the entire queue, and
626 * and executes the given function on each element.
627 * Arguments: elem - top element of the queue.
628 * func - the function to execute.
632 void Loop(struct mqelem *elem, void (*func)(char **))
636 (*func) (elem->q_data);
642 /* Function Name: QueryLoop
643 * Description: This functions loops through a queue containing
644 * information about some item that we want to perform
645 * an operation on, and then calls the correct routine
646 * perform that operation.
647 * Arguments: top - top of the queue of information.
648 * print_func - print function.
649 * op_function - operation to be performed.
650 * query_string - string the prompts the user whether or not
651 * to perform this operation.
654 * print_opt - should expect one arguent, the info array
656 * is expected to return the name of the item.
657 * op_func - should expect two arguments.
658 * 1) the info array of char *'s.
659 * 2) a boolean the is true if there only
660 * one item in this queue, used for delete
662 * query_string - this should be of such a form that when the
663 * name of the object and '(y/n/q) ?' are appended
664 * then it should still make sense, an example is
668 void QueryLoop(struct mqelem *elem, char * (*print_func)(char **),
669 void (*op_func)(char **, Bool), char *query_string)
672 char temp_buf[BUFSIZ], *name;
674 elem = QueueTop(elem);
675 one_item = (QueueCount(elem) == 1);
678 char **info = elem->q_data;
681 (*op_func) (info, one_item);
684 name = (*print_func) (info); /* call print function. */
685 sprintf(temp_buf, "%s %s (y/n/q)", query_string, name);
686 switch (YesNoQuitQuestion(temp_buf, FALSE))
689 (*op_func) (info, one_item);
694 Put_message("Aborting...");
702 /* Function Name: NullPrint
703 * Description: print function that returns nothing.
704 * Arguments: info - a pointer to the info array - Not used.
708 char *NullPrint(char **info)
714 /* Function Name: GetTypeValues
715 * Description: gets legal values for a typed object, keeping a cache
716 * Arguments: type name
717 * Returns: argv of values
720 struct mqelem *GetTypeValues(char *tname)
723 char *argv[3], *p, **pp;
724 struct mqelem *elem, *oelem;
725 static struct mqelem *cache = NULL;
728 struct mqelem *cache_data;
731 for (elem = cache; elem; elem = elem->q_forw)
734 if (!strcmp(ce->cache_name, tname))
735 return ce->cache_data;
742 if ((stat = do_mr_query("get_alias", 3, argv, StoreInfo, &elem)))
744 com_err(program_name, stat, " in GetTypeValues");
748 for (elem = QueueTop(elem); elem; elem = elem->q_forw)
755 elem = malloc(sizeof(struct mqelem));
756 ce = malloc(sizeof(struct cache_elem));
757 ce->cache_name = strdup(tname);
758 ce->cache_data = QueueTop(oelem);
760 AddQueue(elem, cache);
761 cache = QueueTop(elem);
762 return ce->cache_data;
766 /* Function Name: GetTypeFromUser
767 * Description: gets a typed value from the user
768 * Arguments: prompt string, type name, buffer pointer
769 * Returns: SUB_ERROR if ^C, SUB_NORMAL otherwise
772 int GetTypeFromUser(char *prompt, char *tname, char **pointer)
774 char def[BUFSIZ], buffer[BUFSIZ], *p, *argv[3];
778 strcpy(def, *pointer);
779 strcpy(buffer, prompt);
780 strcat(buffer, " (");
781 for (elem = GetTypeValues(tname); elem; elem = elem->q_forw)
783 /* Make sure we don't blow up and get too long a prompt */
784 if (strlen(buffer) > 64)
786 strcat(buffer, elem->q_data);
788 strcat(buffer, ", ");
791 /* Trim the prompt if it is too long */
792 if (strlen(buffer) > 64)
793 sprintf(buffer, "%s (? for help)", prompt);
794 if (GetValueFromUser(buffer, pointer) == SUB_ERROR)
796 if (**pointer == '?')
798 sprintf(buffer, "Type %s is one of:", tname);
800 for (elem = GetTypeValues(tname); elem; elem = elem->q_forw)
801 Put_message(elem->q_data);
802 *pointer = strdup(def);
803 return GetTypeFromUser(prompt, tname, pointer);
805 for (elem = GetTypeValues(tname); elem; elem = elem->q_forw)
807 if (!strcasecmp(elem->q_data, *pointer))
809 strcpy(*pointer, elem->q_data);
813 sprintf(buffer, "\"%s\" is not a legal value for %s. Use one of:",
816 for (elem = GetTypeValues(tname); elem; elem = elem->q_forw)
817 Put_message(elem->q_data);
818 sprintf(buffer, "Are you sure you want \"%s\" to be a legal %s",
820 if (YesNoQuestion("Do you want this to be a new legal value", 0) == TRUE &&
821 YesNoQuestion(buffer, 0) == TRUE)
826 /* don't uppercase access flags. Do uppercase everything else */
827 if (strncmp(tname, "fs_access", 9))
829 for (p = argv[2]; *p; p++)
835 if ((stat = do_mr_query("add_alias", 3, argv, NULL, NULL)))
836 com_err(program_name, stat, " in add_alias");
839 elem = malloc(sizeof(struct mqelem));
840 elem->q_data = strdup(*pointer);
841 AddQueue(elem, GetTypeValues(tname));
842 Put_message("Done.");
845 *pointer = strdup(def);
846 return GetTypeFromUser(prompt, tname, pointer);
850 /* Function Name: GetAddressFromUser
851 * Description: gets an IP address from the user
852 * Arguments: prompt string, buffer pointer
853 * buffer contains default value as long int
854 * Returns: SUB_ERROR if ^C, SUB_NORMAL otherwise
857 int GetAddressFromUser(char *prompt, char **pointer)
859 char *value, buf[256];
863 addr.s_addr = htonl(atoi(*pointer));
864 value = strdup(inet_ntoa(addr));
865 ret = GetValueFromUser(prompt, &value);
866 if (ret == SUB_ERROR)
868 addr.s_addr = inet_addr(value);
870 sprintf(buf, "%ld", ntohl(addr.s_addr));
871 *pointer = strdup(buf);
876 int do_mr_query(char *name, int argc, char **argv,
877 int (*proc)(int, char **, void *), void *hint)
880 extern char *whoami, *moira_server;
883 status = mr_query(name, argc, argv, proc, hint);
884 if (status != MR_ABORTED && status != MR_NOT_CONNECTED)
886 status = mrcl_connect(moira_server, whoami, QUERY_VERSION, 0);
887 if (status != MRCL_SUCCESS)
889 com_err(whoami, status, " while re-connecting to server %s",
893 status = mr_auth(whoami);
896 com_err(whoami, status, " while re-authenticating to server %s",
901 status = mr_query(name, argc, argv, proc, hint);