1 #if (!defined(lint) && !defined(SABER))
2 static char rcsid_module_c[] = "$Header$";
5 /* This is the file cluster.c for the MOIRA Client, which allows a nieve
6 * user to quickly and easily maintain most parts of the MOIRA database.
10 * By: Chris D. Peterson
16 * Copyright 1988 by the Massachusetts Institute of Technology.
18 * For further information on copyright and distribution
19 * see the file mit-copyright.h
22 /* BTW: for anyone who cares MCD is short for Machine, Cluster, Data. */
27 #include <moira_site.h>
30 #include "mit-copyright.h"
40 #define M_DEFAULT_TYPE DEFAULT_NONE
42 #define C_DEFAULT_DESCRIPT DEFAULT_NONE
43 #define C_DEFAULT_LOCATION DEFAULT_NONE
45 #define CD_DEFAULT_LABEL DEFAULT_NONE
46 #define CD_DEFAULT_DATA DEFAULT_NONE
48 /* -------------------- Set Defaults -------------------- */
50 /* Function Name: SetMachineDefaults
51 * Description: sets machine defaults.
52 * Arguments: info - an array to put the defaults into.
53 * name - Canonacalized name of the machine.
54 * Returns: info - the array.
58 SetMachineDefaults(info, name)
61 info[M_NAME] = Strsave(name);
62 info[M_TYPE] = Strsave(M_DEFAULT_TYPE);
63 info[M_MODBY] = info[M_MODTIME] = info[M_MODWITH] = info[M_END] = NULL;
67 /* Function Name: SetClusterDefaults
68 * Description: sets Cluster defaults.
69 * Arguments: info - an array to put the defaults into.
70 * name - name of the Cluster.
71 * Returns: info - the array.
75 SetClusterDefaults(info, name)
78 info[C_NAME] = Strsave(name);
79 info[C_DESCRIPT] = Strsave(C_DEFAULT_DESCRIPT);
80 info[C_LOCATION] = Strsave(C_DEFAULT_LOCATION);
81 info[C_MODBY] = info[C_MODTIME] = info[C_MODWITH] = info[C_END] = NULL;
85 /* -------------------- General Functions -------------------- */
87 /* Function Name: PrintMachInfo
88 * Description: This function Prints out the Machine info in
90 * Arguments: info - array of information about a machine.
91 * Returns: The name of the Machine
101 sprintf(buf, "Machine: %-30s Type: %s", info[M_NAME], info[M_TYPE]);
103 sprintf(buf, MOD_FORMAT, info[M_MODBY], info[M_MODTIME], info[M_MODWITH]);
105 return(info[M_NAME]);
108 /* Function Name: PrintClusterInfo
109 * Description: This function Prints out the cluster info
110 * in a coherent form.
111 * Arguments: info - array of information about a cluster.
112 * Returns: The name of the cluster.
116 PrintClusterInfo(info)
122 sprintf(buf, "Cluster: %s", info[C_NAME]);
124 sprintf(buf, "Description: %s", info[C_DESCRIPT]);
126 sprintf(buf, "Location: %s", info[C_LOCATION]);
128 sprintf(buf, MOD_FORMAT, info[C_MODBY], info[C_MODTIME], info[C_MODWITH]);
130 return(info[C_NAME]);
133 /* Function Name: PrintClusterData
134 * Description: Prints the Data on a cluster
135 * Arguments: info a pointer to the data array.
136 * Returns: The name of the cluster.
140 PrintClusterData(info)
146 sprintf(buf, "Cluster: %-20s Label: %-15s Data: %s",
147 info[CD_NAME], info[CD_LABEL], info[CD_DATA]);
149 return(info[CD_NAME]);
152 /* Function Name: PrintMCMap
153 * Description: Prints the data about a machine to cluster mapping.
154 * Arguments: info a pointer to the data array.
163 sprintf(buf, "Cluster: %-30s Machine: %-20s",
164 info[MAP_CLUSTER], info[MAP_MACHINE]);
166 return(""); /* Used by QueryLoop(). */
169 /* Function Name: GetMCInfo.
170 * Description: This function stores info about a machine.
171 * type - type of data we are trying to retrieve.
172 * name1 - the name of argv[0] for the call.
173 * name2 - the name of argv[1] for the call.
174 * Returns: the top element of a queue containing the data or NULL.
178 GetMCInfo(type, name1, name2)
180 char * name1, *name2;
184 struct qelem * elem = NULL;
189 if ( (stat = do_mr_query("get_machine", 1, &name1,
190 StoreInfo, (char *)&elem)) != 0) {
191 com_err(program_name, stat, " in get_machine.");
196 if ( (stat = do_mr_query("get_cluster", 1, &name1,
197 StoreInfo, (char *)&elem)) != 0) {
198 com_err(program_name, stat, " in get_cluster.");
203 args[MAP_MACHINE] = name1;
204 args[MAP_CLUSTER] = name2;
205 if ( (stat = do_mr_query("get_machine_to_cluster_map", 2, args,
206 StoreInfo, (char *)&elem)) != 0) {
207 com_err(program_name, stat, " in get_machine_to_cluster_map.");
212 args[CD_NAME] = name1;
213 args[CD_LABEL] = name2;
214 if ( (stat = do_mr_query("get_cluster_data", 2, args,
215 StoreInfo, (char *)&elem)) != 0) {
216 com_err(program_name, stat, " in get_cluster_data.");
220 return(QueueTop(elem));
223 /* Function Name: AskMCDInfo.
224 * Description: This function askes the user for information about a
225 * machine and saves it into a structure.
226 * Arguments: info - a pointer the information to ask about
227 * type - type of information - MACHINE
230 * name - T/F : change the name of this type.
235 AskMCDInfo(info, type, name)
240 char temp_buf[BUFSIZ], *newname;
244 sprintf(temp_buf, "Setting the information for the Machine %s.",
248 sprintf(temp_buf, "Setting the information for the Cluster %s.",
252 sprintf(temp_buf, "Setting the Data for the Cluster %s.",
256 Put_message(temp_buf);
261 newname = Strsave(info[M_NAME]);
262 GetValueFromUser("The new name for this machine? ", &newname);
263 newname = canonicalize_hostname(newname);
266 newname = Strsave(info[C_NAME]);
267 GetValueFromUser("The new name for this cluster? ",
271 Put_message("Unknown type in AskMCDInfo, programmer botch");
278 GetTypeFromUser("Machine's Type", "mac_type", &info[M_TYPE]);
279 FreeAndClear(&info[M_MODTIME], TRUE);
280 FreeAndClear(&info[M_MODBY], TRUE);
281 FreeAndClear(&info[M_MODWITH], TRUE);
284 GetValueFromUser("Cluster's Description:", &info[C_DESCRIPT]);
285 GetValueFromUser("Cluster's Location:", &info[C_LOCATION]);
286 FreeAndClear(&info[C_MODTIME], TRUE);
287 FreeAndClear(&info[C_MODBY], TRUE);
288 FreeAndClear(&info[C_MODWITH], TRUE);
291 GetValueFromUser("Label defining this data?", &info[CD_LABEL]);
292 GetValueFromUser("The data itself ? ", &info[CD_DATA]);
297 * Slide the newname into the #2 slot, this screws up all future references
301 SlipInNewName(info, newname);
306 /* ----------- Machine Menu ----------- */
308 /* Function Name: ShowMachineInfo
309 * Description: This function shows the information about a machine.
310 * Arguments: argc, argv - the name of the machine in argv[1].
311 * Returns: DM_NORMAL.
316 ShowMachineInfo(argc, argv)
323 tmpname = canonicalize_hostname(strsave(argv[1]));
324 top = GetMCInfo(MACHINE, tmpname, (char *) NULL);
325 Loop(top, ( (void *) PrintMachInfo) );
330 /* Function Name: AddMachine
331 * Description: This function adds a new machine to the database.
332 * Arguments: argc, argv - the name of the machine in argv[1].
333 * Returns: DM_NORMAL.
338 AddMachine(argc, argv)
342 char **args, *info[MAX_ARGS_SIZE], *name;
345 if (!ValidName(argv[1])) /* Checks for wildcards. */
348 * Check to see if this machine already exists.
350 name = canonicalize_hostname(strsave(argv[1]));
352 if ( (stat = do_mr_query("get_machine", 1, &name, NullFunc, NULL)) == 0) {
353 Put_message("This machine already exists.");
357 else if (stat != MR_NO_MATCH) {
358 com_err(program_name, stat, " in AddMachine.");
363 args = AskMCDInfo(SetMachineDefaults(info, name), MACHINE, FALSE);
366 * Actually create the new Machine.
369 if ( (stat = do_mr_query("add_machine", CountArgs(args),
370 args, Scream, NULL)) != 0)
371 com_err(program_name, stat, " in AddMachine.");
378 /* Function Name: RealUpdateMachine
379 * Description: Performs the actual update of the machine data.
380 * Arguments: info - the information on the machine to update.
381 * junk - an UNUSED Boolean.
387 RealUpdateMachine(info, junk)
392 char ** args = AskMCDInfo(info, MACHINE, TRUE);
393 if ( (stat = do_mr_query("update_machine", CountArgs(args),
394 args, Scream, NULL)) != 0)
395 com_err(program_name, stat, " in UpdateMachine.");
397 Put_message("Machine sucessfully updated.");
400 /* Function Name: UpdateMachine
401 * Description: This function adds a new machine to the database.
402 * Arguments: argc, argv - the name of the machine in argv[1].
403 * Returns: DM_NORMAL.
408 UpdateMachine(argc, argv)
415 tmpname = canonicalize_hostname(strsave(argv[1]));
416 top = GetMCInfo( MACHINE, tmpname, (char *) NULL);
417 QueryLoop(top, NullPrint, RealUpdateMachine, "Update the machine");
424 /* Function Name: CheckAndRemoveFromCluster
425 * Description: This func tests to see if a machine is in a cluster.
426 * and if so then removes it
427 * Arguments: name - name of the machine (already Canonocalized).
428 * ask_user- query the user before removing if from clusters?
429 * Returns: MR_ERROR if machine left in a cluster, or mr_error.
433 CheckAndRemoveFromCluster(name, ask_user)
437 register int stat, ret_value;
439 char *args[10], temp_buf[BUFSIZ], *ptr;
440 struct qelem *top, *elem = NULL;
442 ret_value = SUB_NORMAL; /* initialize ret_value. */
445 stat = do_mr_query("get_machine_to_cluster_map", 2, args,
446 StoreInfo, (char *)&elem);
447 if (stat && stat != MR_NO_MATCH) {
448 com_err(program_name, stat, " in get_machine_to_cluster_map.");
451 if (stat == MR_SUCCESS) {
452 elem = top = QueueTop(elem);
454 sprintf(temp_buf, "%s is assigned to the following clusters.",
456 Put_message(temp_buf);
457 Loop(top, (void *) PrintMCMap);
458 ptr = "Remove this machine from ** ALL ** these clusters?";
459 if (YesNoQuestion(ptr, FALSE) == TRUE) /* may return -1. */
462 Put_message("Aborting...");
471 while (elem != NULL) {
472 char **info = (char **) elem->q_data;
473 if ( (stat = do_mr_query( "delete_machine_from_cluster",
474 2, info, Scream, NULL)) != 0) {
475 ret_value = SUB_ERROR;
476 com_err(program_name, stat,
477 " in delete_machine_from_cluster.");
479 "Machine %s ** NOT ** removed from cluster %s.",
480 info[MAP_MACHINE], info[MAP_CLUSTER]);
481 Put_message(temp_buf);
490 /* Function Name: RealDeleteMachine
491 * Description: Actually Deletes the Machine.
492 * Arguments: info - nescessary information stored as an array of char *'s
493 * one_machine - a boolean, true if there is only one item in
499 RealDeleteMachine(info, one_machine)
504 char temp_buf[BUFSIZ];
506 sprintf(temp_buf, "Are you sure you want to delete the machine %s (y/n)? ",
508 if(!one_machine || Confirm(temp_buf)) {
509 if (CheckAndRemoveFromCluster(info[M_NAME], TRUE) != SUB_ERROR) {
510 if ( (stat = do_mr_query("delete_machine", 1,
511 &info[M_NAME], Scream, NULL)) != 0) {
512 com_err(program_name, stat, " in DeleteMachine.");
513 sprintf(temp_buf, "%s ** NOT ** deleted.",
515 Put_message(temp_buf);
518 sprintf(temp_buf, "%s successfully Deleted.", info[M_NAME]);
519 Put_message(temp_buf);
525 /* Function Name: DeleteMachine
526 * Description: This function removes a machine from the data base.
527 * Arguments: argc, argv - the machines name int argv[1].
528 * Returns: DM_NORMAL.
531 /* Perhaps we should remove the cluster if it has no machine now. */
535 DeleteMachine(argc, argv)
542 tmpname = canonicalize_hostname(strsave(argv[1]));
543 top = GetMCInfo(MACHINE, tmpname, (char *) NULL);
544 QueryLoop(top, PrintMachInfo, RealDeleteMachine, "Delete the machine");
550 /* Function Name: AddMachineToCluster
551 * Description: This function adds a machine to a cluster
552 * Arguments: argc, argv - The machine name is argv[1].
553 * The cluster name in argv[2].
554 * Returns: DM_NORMAL.
559 AddMachineToCluster(argc, argv)
564 char *machine, *cluster, temp_buf[BUFSIZ], *args[10];
565 Bool add_it, one_machine, one_cluster;
566 struct qelem * melem, *mtop, *celem, *ctop;
568 machine = canonicalize_hostname(strsave(argv[1]));
571 celem = ctop = GetMCInfo(CLUSTER, cluster, (char *) NULL);
572 melem = mtop = GetMCInfo(MACHINE, machine, (char *) NULL);
575 one_machine = (QueueCount(mtop) == 1);
576 one_cluster = (QueueCount(ctop) == 1);
578 /* No good way to use QueryLoop() here, sigh */
580 while (melem != NULL) {
581 char ** minfo = (char **) melem->q_data;
582 while (celem != NULL) {
583 char ** cinfo = (char **) celem->q_data;
584 if (one_machine && one_cluster)
587 sprintf(temp_buf,"Add machine %s to cluster %s (y/n/q) ?",
588 minfo[M_NAME], cinfo[C_NAME]);
589 switch (YesNoQuitQuestion(temp_buf, FALSE)) {
597 Put_message("Aborting...");
604 args[0] = minfo[M_NAME];
605 args[1] = cinfo[C_NAME];
606 stat = do_mr_query("add_machine_to_cluster", 2, args,
612 sprintf(temp_buf, "%s is already in cluster %s",
613 minfo[M_NAME], cinfo[C_NAME]);
614 Put_message(temp_buf);
617 com_err(program_name, stat, " in AddMachineToCluster.");
621 celem = celem->q_forw;
623 celem = ctop; /* reset cluster element. */
624 melem = melem->q_forw;
631 /* Function Name: RealRemoveMachineFromCluster
632 * Description: This function actually removes the machine from its
634 * Arguments: info - all information nescessary to perform the removal.
635 * one_map - True if there is only one case, and we should
641 RealRemoveMachineFromCluster(info, one_map)
645 char temp_buf[BUFSIZ];
648 sprintf(temp_buf, "Remove %s from the cluster %s",
649 info[MAP_MACHINE], info[MAP_CLUSTER]);
650 if (!one_map || Confirm(temp_buf)) {
651 if ( (stat = do_mr_query("delete_machine_from_cluster", 2,
652 info, Scream, NULL)) != 0 )
653 com_err(program_name, stat, " in delete_machine_from_cluster");
655 sprintf(temp_buf, "%s has been removed from the cluster %s.",
656 info[MAP_MACHINE], info[MAP_CLUSTER]);
657 Put_message(temp_buf);
661 Put_message("Machine not removed.");
664 /* Function Name: RemoveMachineFromCluster
665 * Description: Removes this machine form a specific cluster.
666 * Arguments: argc, argv - Name of machine in argv[1].
667 * Name of cluster in argv[2].
673 RemoveMachineFromCluster(argc, argv)
677 struct qelem *elem = NULL;
678 char buf[BUFSIZ], * args[10];
681 args[MAP_MACHINE] = canonicalize_hostname(strsave(argv[1]));
682 args[MAP_CLUSTER] = argv[2];
683 args[MAP_END] = NULL;
685 stat = do_mr_query("get_machine_to_cluster_map", CountArgs(args), args,
686 StoreInfo, (char *)&elem);
687 if (stat == MR_NO_MATCH) {
688 sprintf(buf, "The machine %s is not is the cluster %s.",
689 args[MAP_MACHINE], args[MAP_CLUSTER]);
691 free(args[MAP_MACHINE]);
694 if (stat != MR_SUCCESS)
695 com_err(program_name, stat, " in delete_machine_from_cluster");
697 elem = QueueTop(elem);
698 QueryLoop(elem, PrintMCMap, RealRemoveMachineFromCluster,
699 "Remove this machine from this cluster");
702 free(args[MAP_MACHINE]);
706 /* ---------- Cluster Menu -------- */
708 /* Function Name: ShowClusterInfo
709 * Description: Gets information about a cluser given its name.
710 * Arguments: argc, argc - the name of the cluster in in argv[1].
711 * Returns: DM_NORMAL.
716 ShowClusterInfo(argc, argv)
722 top = GetMCInfo(CLUSTER, argv[1], (char *) NULL);
723 Loop(top, (void *) PrintClusterInfo);
728 /* Function Name: AddCluster
729 * Description: Creates a new cluster.
730 * Arguments: argc, argv - the name of the new cluster is argv[1].
731 * Returns: DM_NORMAL.
736 AddCluster(argc, argv)
740 char **args, *info[MAX_ARGS_SIZE], *name = argv[1];
743 * Check to see if this cluster already exists.
745 if (!ValidName(name))
748 if ( (stat = do_mr_query("get_cluster", 1, &name,
749 NullFunc, NULL)) == MR_SUCCESS) {
750 Put_message("This cluster already exists.");
753 else if (stat != MR_NO_MATCH) {
754 com_err(program_name, stat, " in AddCluster.");
757 args = AskMCDInfo(SetClusterDefaults(info, name), CLUSTER, FALSE);
759 * Actually create the new Cluster.
761 if ( (stat = do_mr_query("add_cluster", CountArgs(args),
762 args, Scream, NULL)) != 0)
763 com_err(program_name, stat, " in AddCluster.");
769 /* Function Name: RealUpdateCluster
770 * Description: This function actually performs the cluster update.
771 * Arguments: info - all information nesc. for updating the cluster.
772 * junk - an UNUSED boolean.
778 RealUpdateCluster(info, junk)
783 char ** args = AskMCDInfo(info, CLUSTER, TRUE);
784 if ( (stat = do_mr_query("update_cluster", CountArgs(args),
785 args, Scream, NULL)) != 0)
786 com_err(program_name, stat, " in UpdateCluster.");
788 Put_message("Cluster successfully updated.");
791 /* Function Name: UpdateCluster
792 * Description: This Function Updates a cluster
793 * Arguments: name of the cluster in argv[1].
794 * Returns: DM_NORMAL.
799 UpdateCluster(argc, argv)
804 top = GetMCInfo( CLUSTER, argv[1], (char *) NULL );
805 QueryLoop(top, NullPrint, RealUpdateCluster, "Update the cluster");
811 /* Function Name: CheckAndRemoveMachine
812 * Description: This function checks and removes all machines from a
814 * Arguments: name - name of the cluster.
815 * ask_first - if TRUE, then we will query the user, before
817 * Returns: SUB_ERROR if all machines not removed.
821 CheckAndRemoveMachines(name, ask_first)
825 register int stat, ret_value;
827 char *args[10], temp_buf[BUFSIZ], *ptr;
828 struct qelem *top, *elem = NULL;
830 ret_value = SUB_NORMAL;
831 args[MAP_MACHINE] = "*";
832 args[MAP_CLUSTER] = name;
833 stat = do_mr_query("get_machine_to_cluster_map", 2, args,
834 StoreInfo, (char *)&elem);
835 if (stat && stat != MR_NO_MATCH) {
836 com_err(program_name, stat, " in get_machine_to_cluster_map.");
840 elem = top = QueueTop(elem);
843 "The cluster %s has the following machines in it:",
845 Put_message(temp_buf);
846 while (elem != NULL) {
847 char **info = (char **) elem->q_data;
848 Print(1, &info[MAP_MACHINE], (char *) NULL);
851 ptr = "Remove ** ALL ** these machines from this cluster?";
853 if (YesNoQuestion(ptr, FALSE) == TRUE) /* may return -1. */
856 Put_message("Aborting...");
867 char **info = (char **) elem->q_data;
868 if ( (stat = do_mr_query("delete_machine_from_cluster",
869 2, info, Scream, NULL)) != 0) {
870 ret_value = SUB_ERROR;
871 com_err(program_name, stat,
872 " in delete_machine_from_cluster.");
874 "Machine %s ** NOT ** removed from cluster %s.",
875 info[MAP_MACHINE], info[MAP_CLUSTER]);
876 Put_message(temp_buf);
885 /* Function Name: RealDeleteCluster
886 * Description: Actually performs the cluster deletion.
887 * Arguments: info - all information about this cluster.
888 * one_cluster - If true then there was only one cluster in
889 * the queue, and we should confirm.
894 RealDeleteCluster(info, one_cluster)
899 char temp_buf[BUFSIZ];
902 "Are you sure the you want to delete the cluster %s (y/n) ?",
904 if (!one_cluster || Confirm(temp_buf)) {
905 if (CheckAndRemoveMachines(info[C_NAME], TRUE) != SUB_ERROR) {
906 if ( (stat = do_mr_query("delete_cluster", 1,
907 &info[C_NAME], Scream, NULL)) != 0) {
908 com_err(program_name, stat, " in delete_cluster.");
909 sprintf(temp_buf, "Cluster %s ** NOT ** deleted.",
911 Put_message(temp_buf);
914 sprintf(temp_buf, "cluster %s sucesfully deleted.",
916 Put_message(temp_buf);
922 /* Function Name: DeleteCluster
923 * Description: This function removes a cluster from the database.
924 * Arguments: argc, argv - the name of the cluster is stored in argv[1].
925 * Returns: DM_NORMAL.
930 DeleteCluster(argc, argv)
936 top = GetMCInfo( CLUSTER, argv[1], (char *) NULL );
937 QueryLoop(top, PrintClusterInfo, RealDeleteCluster, "Delete the cluster");
943 /* ----------- Cluster Data Menu -------------- */
945 /* Function Name: ShowClusterData
946 * Description: This function shows the services for one cluster.
947 * Arguments: argc, argv - The name of the cluster is argv[1].
948 * The label of the data in argv[2].
949 * Returns: DM_NORMAL.
954 ShowClusterData(argc, argv)
958 struct qelem *elem, *top;
961 top = elem = GetMCInfo(DATA, argv[1], argv[2]);
962 while (elem != NULL) {
963 info = (char **) elem->q_data;
964 PrintClusterData(info);
971 /* Function Name: AddClusterData
972 * Description: This function adds some data to the cluster.
973 * Arguments: argv, argc: argv[1] - the name of the cluster.
974 * argv[2] - the label of the data.
975 * argv[3] - the data.
976 * Returns: DM_NORMAL.
981 AddClusterData(argc, argv)
987 if( (stat = do_mr_query("add_cluster_data", 3, argv + 1,
988 Scream, (char *) NULL)) != 0)
989 com_err(program_name, stat, " in AddClusterData.");
993 /* Function Name: RealRemoveClusterData
994 * Description: actually removes the cluster data.
995 * Arguments: info - all info necessary to remove the cluster, in an array
997 * one_item - if true then the queue has only one elem and we
1003 RealRemoveClusterData(info, one_item)
1011 temp_ptr = "Are you sure that you want to remove this cluster data (y/n) ?";
1012 PrintClusterData(info);
1013 if (!one_item || Confirm(temp_ptr)) {
1014 if( (stat = do_mr_query("delete_cluster_data", 3, info,
1015 Scream, (char *) NULL)) != 0) {
1016 com_err(program_name, stat, " in DeleteClusterData.");
1017 Put_message("Data not removed.");
1020 Put_message("Removal sucessful.");
1024 /* Function Name: RemoveClusterData
1025 * Description: This function removes data on a given cluster.
1026 * Arguments: argv, argc: argv[1] - the name of the cluster.
1027 * argv[2] - the label of the data.
1028 * argv[3] - the data.
1029 * Returns: DM_NORMAL.
1034 RemoveClusterData(argc, argv)
1040 top = GetMCInfo(DATA, argv[1], argv[2]);
1041 QueryLoop(top, PrintClusterData, RealRemoveClusterData,
1042 "Remove data from cluster");
1048 /* Function Name: MachineToClusterMap
1049 * Description: This Retrieves the mapping between machine and cluster
1050 * Arguments: argc, argv - argv[1] -> machine name or wildcard.
1051 * argv[2] -> cluster name or wildcard.
1057 MachineToClusterMap(argc,argv)
1061 struct qelem *elem, *top;
1064 tmpname = canonicalize_hostname(strsave(argv[1]));
1065 top = elem = GetMCInfo(MAP, tmpname, argv[2]);
1067 Put_message(""); /* blank line on screen */
1068 while (elem != NULL) {
1069 char ** info = (char **) elem->q_data;
1071 elem = elem->q_forw;