]> andersk Git - moira.git/blob - clients/moira/cluster.c
3f2f9e3e00229b7ebd2c4ed271bc23635d835229
[moira.git] / clients / moira / cluster.c
1 /* $Id$
2  *
3  *      This is the file cluster.c for the Moira Client, which allows users
4  *      to quickly and easily maintain most parts of the Moira database.
5  *      It Contains:
6  *
7  *      Created:        4/22/88
8  *      By:             Chris D. Peterson
9  *
10  * Copyright (C) 1988-1998 by the Massachusetts Institute of Technology.
11  * For copying and distribution information, please see the file
12  * <mit-copyright.h>.
13  */
14
15 /* BTW: for anyone who cares MCD is short for Machine, Cluster, Data. */
16
17 #include <mit-copyright.h>
18 #include <moira.h>
19 #include <moira_site.h>
20 #include <mrclient.h>
21
22 #include "defs.h"
23 #include "f_defs.h"
24 #include "globals.h"
25
26 #include <sys/types.h>
27
28 #ifdef HAVE_UNAME
29 #include <sys/utsname.h>
30 #endif
31
32 #ifndef _WIN32
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
35 #include <netdb.h>
36 #endif /* _WIN32 */
37
38 #include <ctype.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42
43 void PrintAliases(char **info);
44 static void PrintMachine(char **info);
45 struct mqelem *GetMCInfo(int type, char *name1, char *name2);
46 struct mqelem *GetMachineByOwner(char *type, char *name);
47 char **AskMCDInfo(char **info, int type, Bool name);
48 int CheckAndRemoveFromCluster(char *name, Bool ask_user);
49 int CheckAndRemoveCnames(char *name, Bool ask_user);
50 int CheckAndRemoveMachines(char *name, Bool ask_first);
51
52 static char *PrintContainerInfo(char **info);
53 static void RealUpdateContainer(char **info, Bool junk);
54 static void RealDeleteContainer(char **info, Bool one_container);
55 static void RealRemoveMachineFromContainer(char **info, Bool one_contmap);
56
57 #define MACHINE   0
58 #define CLUSTER   1
59 #define DATA      2
60 #define MAP       3
61 #define SUBNET    4
62 #define CNAME     5
63 #define CONTAINER 6
64 #define CONTMAP   7
65
66 #define M_DEFAULT_TYPE     DEFAULT_NONE
67
68 #define C_DEFAULT_DESCRIPT DEFAULT_NONE
69 #define C_DEFAULT_LOCATION DEFAULT_NONE
70
71 #define CON_DEFAULT_TYPE   DEFAULT_NONE
72
73 #define CD_DEFAULT_LABEL   DEFAULT_NONE
74 #define CD_DEFAULT_DATA    DEFAULT_NONE
75
76 #define S_DEFAULT_LOW   "18.0.0.20"
77 #define S_DEFAULT_HIGH  "18.0.2.249"
78
79 static char *states[] = {
80   "Reserved (0)",
81   "Active (1)",
82   "None (2)",
83   "Deleted (3)"
84 };
85
86 static char *subnet_states[] = {
87   "Reserved (0)",
88   "Billable (1)",
89   "Private, 10 Mbps (2)",
90   "Private, 100 Mbps (3)",
91   "Private, Other (4)",
92   "Resnet, Dorm (5)",
93   "Infrastructure (6)",
94   "Private, 1000 Mbps (7)",
95   "Resnet, FSILG (8)"
96 };
97
98 static char *MacState(int state)
99 {
100   static char buf[BUFSIZ];
101
102   if (state < 0 || state > 3)
103     {
104       sprintf(buf, "Unknown (%d)", state);
105       return buf;
106     }
107   return states[state];
108 }
109
110 static char *SubnetState(int state)
111 {
112   static char buf[BUFSIZ];
113
114   if (state < 0 || state > 8)
115     {
116       sprintf(buf, "Unknown (%d)", state);
117       return buf;
118     }
119   return subnet_states[state];
120 }
121
122 /* -------------------- Set Defaults -------------------- */
123
124 /*      Function Name: SetMachineDefaults
125  *      Description: sets machine defaults.
126  *      Arguments: info - an array to put the defaults into.
127  *                 name - Canonacalized name of the machine.
128  *      Returns: info - the array.
129  */
130
131 static char **SetMachineDefaults(char **info, char *name)
132 {
133   info[M_NAME] = strdup(name);
134   info[M_VENDOR] = strdup(M_DEFAULT_TYPE);
135   info[M_MODEL] = strdup(M_DEFAULT_TYPE);
136   info[M_OS] = strdup(M_DEFAULT_TYPE);
137   info[M_LOC] = strdup(M_DEFAULT_TYPE);
138   info[M_CONTACT] = strdup(M_DEFAULT_TYPE);
139   info[M_BILL_CONTACT] = strdup(M_DEFAULT_TYPE);
140   info[M_ACCT_NUMBER] = strdup("");
141   info[M_USE] = strdup("0");
142   info[M_STAT] = strdup("1");
143   info[M_SUBNET] = strdup("NONE");
144   info[M_ADDR] = strdup("unique");
145   info[M_OWNER_TYPE] = strdup("NONE");
146   info[M_OWNER_NAME] = strdup("NONE");
147   info[M_ACOMMENT] = strdup("");
148   info[M_OCOMMENT] = strdup("");
149   info[17] = info[18] = NULL;
150   return info;
151 }
152
153 /*      Function Name: SetClusterDefaults
154  *      Description: sets Cluster defaults.
155  *      Arguments: info - an array to put the defaults into.
156  *                 name - name of the Cluster.
157  *      Returns: info - the array.
158  */
159
160 static char **SetClusterDefaults(char **info, char *name)
161 {
162   info[C_NAME] = strdup(name);
163   info[C_DESCRIPT] = strdup(C_DEFAULT_DESCRIPT);
164   info[C_LOCATION] = strdup(C_DEFAULT_LOCATION);
165   info[C_MODBY] = info[C_MODTIME] = info[C_MODWITH] = info[C_END] = NULL;
166   return info;
167 }
168
169 static char **SetContainerDefaults(char **info, char *name)
170 {
171   info[CON_NAME] = strdup(name);
172   info[CON_PUBLIC] = strdup(DEFAULT_NO);
173   info[CON_DESCRIPT] = strdup(CON_DEFAULT_TYPE);
174   info[CON_LOCATION] = strdup(CON_DEFAULT_TYPE);
175   info[CON_CONTACT] = strdup(CON_DEFAULT_TYPE);
176   info[CON_OWNER_TYPE] = strdup("NONE");
177   info[CON_OWNER_NAME] = strdup("NONE");
178   info[CON_MEMACE_TYPE] = strdup("NONE");
179   info[CON_MEMACE_NAME] = strdup("NONE");
180   info[CON_MODBY] = info[CON_MODTIME] = info[CON_MODWITH] = NULL;
181   info[CON_END] = NULL;
182   return info;
183 }
184
185 /*      Function Name: SetSubnetDefaults
186  *      Description: sets Subnet defaults.
187  *      Arguments: info - an array to put the defaults into.
188  *                 name - name of the Subnet.
189  *      Returns: info - the array.
190  */
191
192 static char **SetSubnetDefaults(char **info, char *name)
193 {
194   char buf[256];
195
196   info[SN_NAME] = strdup(name);
197   info[SN_DESC] = strdup("");
198   info[SN_STATUS] = strdup("1");
199   info[SN_CONTACT] = strdup(DEFAULT_NONE);
200   info[SN_ACCT_NUMBER] = strdup("");
201   sprintf(buf, "%ld", ntohl(inet_addr("18.255.0.0")));
202   info[SN_ADDRESS] = strdup(buf);
203   sprintf(buf, "%ld", ntohl(inet_addr("255.255.0.0")));
204   info[SN_MASK] = strdup(buf);
205   sprintf(buf, "%ld", ntohl(inet_addr(S_DEFAULT_LOW)));
206   info[SN_LOW] = strdup(buf);
207   sprintf(buf, "%ld", ntohl(inet_addr(S_DEFAULT_HIGH)));
208   info[SN_HIGH] = strdup(buf);
209   info[SN_PREFIX] = strdup("");
210   info[SN_ACE_TYPE] = strdup("LIST");
211   info[SN_ACE_NAME] = strdup("network");
212   info[SN_MODBY] = info[SN_MODTIME] = info[SN_MODWITH] = info[SN_END] = NULL;
213   return info;
214 }
215
216 /* -------------------- General Functions -------------------- */
217
218 static char aliasbuf[256];
219
220 void PrintAliases(char **info)
221 {
222   if (strlen(aliasbuf) == 0)
223     sprintf(aliasbuf, "Aliases:  %s", info[0]);
224   else
225     {
226       strcat(aliasbuf, ", ");
227       strcat(aliasbuf, info[0]);
228     }
229 }
230
231
232 /*      Function Name: PrintMachInfo
233  *      Description: This function Prints out the Machine info in
234  *                   a coherent form.
235  *      Arguments: info - array of information about a machine.
236  *      Returns: The name of the Machine
237  */
238
239 static char *PrintMachInfo(char **info)
240 {
241   char buf[BUFSIZ], tbuf[256];
242   char *args[3];
243   struct mqelem *elem = NULL, *elem2 = NULL;
244   int stat;
245
246   Put_message("");
247   sprintf(buf, "Machine:  %s", info[M_NAME]);
248   Put_message(buf);
249   args[0] = "*";
250   args[1] = info[M_NAME];
251   if ((stat = do_mr_query("get_hostalias", 2, args, StoreInfo, &elem)))
252     {
253       if (stat != MR_NO_MATCH)
254         com_err(program_name, stat, " looking up aliases");
255     }
256   else
257     {
258       aliasbuf[0] = 0;
259       Loop(QueueTop(elem), (void (*)(char **)) PrintAliases);
260       FreeQueue(elem);
261       Put_message(aliasbuf);
262     }
263   sprintf(tbuf, "%s %s", info[M_OWNER_TYPE],
264           strcmp(info[M_OWNER_TYPE], "NONE") ? info[M_OWNER_NAME] : "");
265   sprintf(buf, "Address:  %-16s    Network:    %-16s",
266           info[M_ADDR], info[M_SUBNET]);
267   Put_message(buf);
268   sprintf(buf, "Owner:    %-16s    Use data:   %s", tbuf, info[M_INUSE]);
269   Put_message(buf);
270   sprintf(buf, "Status:   %-16s    Changed:    %s",
271           MacState(atoi(info[M_STAT])), info[M_STAT_CHNG]);
272   Put_message(buf);
273   Put_message("");
274   sprintf(buf, "Vendor:   %-16s    Location:        %s", info[M_VENDOR], 
275           info[M_LOC]);
276   Put_message(buf);
277   sprintf(buf, "Model:    %-16s    Contact:         %s", info[M_MODEL], 
278           info[M_CONTACT]);
279   Put_message(buf);
280   sprintf(buf, "OS:       %-16s    Billing Contact: %s", info[M_OS],
281           info[M_BILL_CONTACT]);
282   Put_message(buf);
283   sprintf(buf, "Opt:      %-16s    Account Number:  %s", info[M_USE],
284           info[M_ACCT_NUMBER]);
285   Put_message(buf);
286   Put_message("");
287   sprintf(buf, "Adm cmt: %s", info[M_ACOMMENT]);
288   Put_message(buf);
289   sprintf(buf, "Op cmt:  %s", info[M_OCOMMENT]);
290   Put_message(buf);
291   Put_message("");
292   sprintf(buf, "Created  by %s on %s", info[M_CREATOR], info[M_CREATED]);
293   Put_message(buf);
294   sprintf(buf, MOD_FORMAT, info[M_MODBY], info[M_MODTIME], info[M_MODWITH]);
295   Put_message(buf);
296   /* Do a get_subnet for the machine's subnet.  We need to know if it's
297    *  Private.
298    */
299   args[0] = info[M_SUBNET];
300   stat = do_mr_query("get_subnet", 1, args, StoreInfo, &elem2);
301   if (stat)
302     com_err(program_name, stat, " looking up subnet info");
303   else if (atoi(((char **)elem2->q_data)[2]) == SNET_STATUS_PRIVATE_10MBPS ||
304            atoi(((char **)elem2->q_data)[2]) == SNET_STATUS_PRIVATE_100MBPS ||
305            atoi(((char **)elem2->q_data)[2]) == SNET_STATUS_PRIVATE_1000MBPS)
306     {
307       Put_message("");
308       sprintf(buf, "Warning:  This host is on a private subnet.");
309       Put_message(buf);
310       sprintf(buf, "Billing information shown is superseded by billing information for the subnet.");
311       Put_message(buf);
312       FreeQueue(elem2);
313     }
314
315   return info[M_NAME];
316 }
317
318 /*      Function Name: PrintMachine
319  *      Description: Prints the name of a machine record
320  *      Arguments: info - array of information about the machine.
321  *      Returns: nothing.
322  */
323
324 static void PrintMachine(char **info)
325 {
326      char buf[BUFSIZ];
327
328      sprintf(buf, "Machine: %s", info[M_NAME]);
329      Put_message(buf);
330 }
331
332 /*      Function Name: PrintCname
333  *      Description: Prints the Data on a host alias
334  *      Arguments: info a pointer to the data array.
335  *      Returns: The name of the alias.
336  */
337
338 static char *PrintCname(char **info)
339 {
340   char buf[BUFSIZ];
341
342   sprintf(buf, "Alias: %-32s Canonical Name: %s", info[0], info[1]);
343   Put_message(buf);
344   return info[0];
345 }
346
347 /*      Function Name: PrintClusterInfo
348  *      Description: This function Prints out the cluster info
349  *                   in a coherent form.
350  *      Arguments: info - array of information about a cluster.
351  *      Returns: The name of the cluster.
352  */
353
354 static char *PrintClusterInfo(char **info)
355 {
356   char buf[BUFSIZ];
357
358   Put_message("");
359   sprintf(buf, "Cluster:     %s", info[C_NAME]);
360   Put_message(buf);
361   sprintf(buf, "Description: %s", info[C_DESCRIPT]);
362   Put_message(buf);
363   sprintf(buf, "Location:    %s", info[C_LOCATION]);
364   Put_message(buf);
365   sprintf(buf, MOD_FORMAT, info[C_MODBY], info[C_MODTIME], info[C_MODWITH]);
366   Put_message(buf);
367   return info[C_NAME];
368 }
369
370 /*      Function Name: PrintClusterData
371  *      Description: Prints the Data on a cluster
372  *      Arguments: info a pointer to the data array.
373  *      Returns: The name of the cluster.
374  */
375
376 static char *PrintClusterData(char **info)
377 {
378   char buf[BUFSIZ];
379
380   Put_message("");
381   sprintf(buf, "Cluster: %-20s Label: %-15s Data: %s",
382           info[CD_NAME], info[CD_LABEL], info[CD_DATA]);
383   Put_message(buf);
384   return info[CD_NAME];
385 }
386
387 /*      Function Name: PrintMCMap
388  *      Description: Prints the data about a machine to cluster mapping.
389  *      Arguments: info a pointer to the data array.
390  *      Returns: none
391  */
392
393 static char *PrintMCMap(char **info)
394 {
395   char buf[BUFSIZ];
396   sprintf(buf, "Cluster: %-30s Machine: %-20s",
397           info[MAP_CLUSTER], info[MAP_MACHINE]);
398   Put_message(buf);
399   return "";                    /* Used by QueryLoop(). */
400 }
401
402 /*      Function Name: PrintSubnetInfo
403  *      Description: This function Prints out the subnet info
404  *                   in a coherent form.
405  *      Arguments: info - array of information about a subnet.
406  *      Returns: The name of the subnet.
407  */
408
409 static char *PrintSubnetInfo(char **info)
410 {
411   char buf[BUFSIZ];
412   struct in_addr addr, mask, low, high;
413
414   Put_message("");
415   sprintf(buf, "        Network:  %s", info[SN_NAME]);
416   Put_message(buf);
417   sprintf(buf, "    Description:  %s", info[SN_DESC]);
418   Put_message(buf);
419   sprintf(buf, "         Status:  %s", SubnetState(atoi(info[SN_STATUS])));
420   Put_message(buf);
421   sprintf(buf, "        Contact:  %s", info[SN_CONTACT]);
422   Put_message(buf);
423   sprintf(buf, " Account Number:  %s", info[SN_ACCT_NUMBER]);
424   Put_message(buf);
425   addr.s_addr = htonl(atoi(info[SN_ADDRESS]));
426   mask.s_addr = htonl(atoi(info[SN_MASK]));
427   low.s_addr = htonl(atoi(info[SN_LOW]));
428   high.s_addr = htonl(atoi(info[SN_HIGH]));
429   /* screwy sequence is here because inet_ntoa returns a pointer to
430      a static buf.  If it were all one sprintf, the last value would
431      appear 4 times. */
432   sprintf(buf, "        Address:  %s        Mask:  ", inet_ntoa(addr));
433   strcat(buf, inet_ntoa(mask));
434   strcat(buf, "\n           High:  ");
435   strcat(buf, inet_ntoa(high));
436   strcat(buf, "       Low:  ");
437   strcat(buf, inet_ntoa(low));
438   Put_message(buf);
439   sprintf(buf, "Hostname prefix:  %s", info[SN_PREFIX]);
440   Put_message(buf);
441   sprintf(buf, "          Owner:  %s %s\n", info[SN_ACE_TYPE],
442           strcmp(info[SN_ACE_TYPE], "NONE") ? info[SN_ACE_NAME] : "");
443   Put_message(buf);
444   sprintf(buf, MOD_FORMAT, info[SN_MODBY], info[SN_MODTIME], info[SN_MODWITH]);
445   Put_message(buf);
446   return info[SN_NAME];
447 }
448
449 /*      Function Name: GetMCInfo.
450  *      Description: This function stores info about a machine.
451  *                   type - type of data we are trying to retrieve.
452  *                   name1 - the name of argv[0] for the call.
453  *                   name2 - the name of argv[1] for the call.
454  *      Returns: the top element of a queue containing the data or NULL.
455  */
456
457 struct mqelem *GetMCInfo(int type, char *name1, char *name2)
458 {
459   int stat;
460   struct mqelem *elem = NULL;
461   char *args[5];
462
463   switch (type)
464     {
465     case MACHINE:
466       args[0] = name1;
467       args[1] = args[2] = args[3] = "*";
468       if ((stat = do_mr_query("get_host", 4, args, StoreInfo, &elem)))
469         {
470           if (stat == MR_NO_MATCH)
471             {
472               char buf[128];
473               sprintf(buf, "Machine '%s' is not in the database.", name1);
474               Put_message(buf);
475             }
476           else
477             com_err(program_name, stat, " in get_machine.");
478           return NULL;
479         }
480       break;
481     case CNAME:
482       args[0] = name1;
483       args[1] = name2;
484       if ((stat = do_mr_query("get_hostalias", 2, args, StoreInfo, &elem)))
485         {
486           com_err(program_name, stat, " in get_hostalias.");
487           return NULL;
488         }
489       break;
490     case SUBNET:
491       if ((stat = do_mr_query("get_subnet", 1, &name1, StoreInfo, &elem)))
492         {
493           if (stat == MR_NO_MATCH)
494             {
495               char buf[128];
496               sprintf(buf, "Network '%s' is not in the database.", name1);
497               Put_message(buf);
498             }
499           else
500             com_err(program_name, stat, " in get_subnet.");
501           return NULL;
502         }
503       break;
504     case CLUSTER:
505       if ((stat = do_mr_query("get_cluster", 1, &name1, StoreInfo, &elem)))
506         {
507           com_err(program_name, stat, " in get_cluster.");
508           return NULL;
509         }
510       break;
511     case MAP:
512       args[MAP_MACHINE] = name1;
513       args[MAP_CLUSTER] = name2;
514       if ((stat = do_mr_query("get_machine_to_cluster_map", 2, args,
515                               StoreInfo, &elem)))
516         {
517           com_err(program_name, stat, " in get_machine_to_cluster_map.");
518           return NULL;
519         }
520       break;
521     case DATA:
522       args[CD_NAME] = name1;
523       args[CD_LABEL] = name2;
524       if ((stat = do_mr_query("get_cluster_data", 2, args, StoreInfo, &elem)))
525         {
526           com_err(program_name, stat, " in get_cluster_data.");
527           return NULL;
528         }
529       break;
530     case CONTAINER:
531       args[CON_NAME] = name1;
532       if ((stat = do_mr_query("get_container", 1, &name1, StoreInfo, &elem)))
533         {
534           com_err(program_name, stat, " in get_container.");
535           return NULL;
536         }
537       break;
538     case CONTMAP:
539       args[0] = name1;
540       if ((stat = do_mr_query("get_machine_to_container_map", 1, &name1,
541                               StoreInfo, &elem)))
542         {
543           com_err(program_name, stat, " in get_machine_to_container_map.");
544           return NULL;
545         }
546     }
547   return QueueTop(elem);
548 }
549
550 /*      Function Name: AskMCDInfo.
551  *      Description: This function askes the user for information about a
552  *                   machine and saves it into a structure.
553  *      Arguments: info - a pointer the information to ask about
554  *                 type - type of information - MACHINE
555  *                                              CLUSTER
556  *                                              DATA
557  *                                              CONTAINER
558  *                 name - T/F : change the name of this type.
559  *      Returns: none.
560  */
561
562 char **AskMCDInfo(char **info, int type, Bool name)
563 {
564   char temp_buf[BUFSIZ], *newname, *oldnewname;
565   int status;
566
567   switch (type)
568     {
569     case MACHINE:
570       sprintf(temp_buf, "\nSetting the information for the Machine %s...",
571               info[M_NAME]);
572       break;
573     case SUBNET:
574       sprintf(temp_buf, "Setting the information for the Network %s...",
575               info[SN_NAME]);
576       break;
577     case CLUSTER:
578       sprintf(temp_buf, "Setting the information for the Cluster %s...",
579               info[C_NAME]);
580       break;
581     case DATA:
582       sprintf(temp_buf, "Setting the Data for the Cluster %s...",
583               info[CD_NAME]);
584       break;
585     case CONTAINER:
586       sprintf(temp_buf, "Setting the Data for the Container %s...",
587               info[CON_NAME]);
588       break;
589     }
590   Put_message(temp_buf);
591
592   if (name)
593     {
594       switch (type)
595         {
596         case MACHINE:
597           newname = strdup(info[M_NAME]);
598           if (GetValueFromUser("The new name for this machine? ", &newname) ==
599               SUB_ERROR)
600             return NULL;
601           oldnewname = strdup(newname);
602           newname = canonicalize_hostname(newname);
603           if (strcasecmp(newname, oldnewname) && *oldnewname != '"')
604             {
605               sprintf(temp_buf, "Warning: '%s' canonicalized to '%s'\n",
606                       oldnewname, newname);
607               Put_message(temp_buf);
608             }
609           free(oldnewname);
610           break;
611         case SUBNET:
612           newname = strdup(info[SN_NAME]);
613           if (GetValueFromUser("The new name for this network? ", &newname) ==
614               SUB_ERROR)
615             return NULL;
616           break;
617         case CLUSTER:
618           newname = strdup(info[C_NAME]);
619           if (GetValueFromUser("The new name for this cluster? ", &newname) ==
620               SUB_ERROR)
621             return NULL;
622           break;
623         case CONTAINER:
624           newname = strdup(info[CON_NAME]);
625           if (GetValueFromUser("The new name for this container? ", &newname)
626               == SUB_ERROR)
627             return NULL;
628           break;
629         default:
630           Put_message("Unknown type in AskMCDInfo, programmer botch");
631           return NULL;
632         }
633     }
634
635   switch (type)
636     {
637     case MACHINE:
638       if (GetValueFromUser("Machine's vendor", &info[M_VENDOR]) == SUB_ERROR)
639         return NULL;
640       if (GetValueFromUser("Machine's model", &info[M_MODEL]) == SUB_ERROR)
641         return NULL;
642       if (GetValueFromUser("Machine's operating system", &info[M_OS]) ==
643           SUB_ERROR)
644         return NULL;
645       if (GetValueFromUser("Machine's location", &info[M_LOC]) == SUB_ERROR)
646         return NULL;
647       if (GetValueFromUser("Machine's contact", &info[M_CONTACT]) ==
648           SUB_ERROR)
649         return NULL;
650       if (GetValueFromUser("Machine's billing contact", 
651                            &info[M_BILL_CONTACT]) == SUB_ERROR)
652         return NULL;
653       if (GetValueFromUser("Machine's billing account number",
654                            &info[M_ACCT_NUMBER]) == SUB_ERROR)
655         return NULL;
656       while (1)
657         {
658           int i;
659           if (GetValueFromUser("Machine's status (? for help)",
660                                  &info[M_STAT]) == SUB_ERROR)
661             return NULL;
662           if (isdigit(info[M_STAT][0]))
663             break;
664           Put_message("Valid status numbers:");
665           for (i = 0; i < 4; i++)
666             Put_message(states[i]);
667         }
668
669       /* there appears to be some argument mismatch between the client
670        * and the server.. so here is this argument shuffler.
671        * I have since modified this to always shuffle the arguments..
672        * not just do so when performing a modify all fields request.
673        * The SetMachinedefaults() has been changed to reflect this.
674        * pray for us and may we attain enlightenment through structures.
675        */
676
677       if (name)
678         {
679           /* info did not come from SetMachineDefaults(), which does not
680            * initialize entry 10 (M_STAT_CHNG), therefore we can
681            * free it.
682            */
683           /* This is an update of an existing machine and the structure
684            * was filled in thru a query to the db which does fill in this
685            * field.
686            */
687           free(info[10]);
688         }
689
690       info[10] = strdup(info[M_SUBNET]);
691       info[11] = strdup(info[M_ADDR]);
692       info[12] = strdup(info[M_OWNER_TYPE]);
693       info[13] = strdup(info[M_OWNER_NAME]);
694       info[14] = strdup(info[M_ACOMMENT]);
695       info[15] = strdup(info[M_OCOMMENT]);
696
697       if (name)
698         {
699           if (GetValueFromUser("Machine's network (or 'none')", &info[10])
700               == SUB_ERROR)
701             return NULL;
702         }
703       if (GetValueFromUser("Machine's address (or 'unassigned' or 'unique')",
704                            &info[11]) == SUB_ERROR)
705         return NULL;
706       if (GetTypeFromUser("Machine's owner type", "ace_type", &info[12]) ==
707           SUB_ERROR)
708         return NULL;
709       if (strcmp(info[12], "NONE") &&
710           GetValueFromUser("Owner's Name", &info[13]) == SUB_ERROR)
711         return NULL;
712       if (!strcmp(info[12], "KERBEROS"))
713           {
714             char *canon;
715
716             status = mrcl_validate_kerberos_member(info[13], &canon);
717             if (mrcl_get_message())
718               Put_message(mrcl_get_message());
719             if (status == MRCL_REJECT)
720               return NULL;
721             free(info[13]);
722             info[13] = canon;
723           }
724       if (GetValueFromUser("Administrative comment", &info[14]) == SUB_ERROR)
725         return NULL;
726       if (GetValueFromUser("Operational comment", &info[15]) == SUB_ERROR)
727         return NULL;
728       info[16] = NULL;
729       FreeAndClear(&info[17], TRUE);
730       FreeAndClear(&info[18], TRUE);
731       break;
732     case SUBNET:
733       if (GetValueFromUser("Network description", &info[SN_DESC]) == SUB_ERROR)
734         return NULL;
735       while (1)
736         {
737           int i;
738           if (GetValueFromUser("Network's status (? for help)",
739                                &info[SN_STATUS]) == SUB_ERROR)
740             return NULL;
741           if (isdigit(info[SN_STATUS][0]))
742             break;
743           Put_message("Valid status numbers:");
744           for (i = 0; i < 9; i++)
745             Put_message(subnet_states[i]);
746         }
747       if (GetValueFromUser("Network's contact", &info[SN_CONTACT]) == SUB_ERROR)
748         return NULL;
749       if (GetValueFromUser("Network's billing account number", 
750                            &info[SN_ACCT_NUMBER]) == SUB_ERROR)
751         return NULL;
752       if (GetAddressFromUser("Network address", &info[SN_ADDRESS]) == SUB_ERROR)
753         return NULL;
754       if (GetAddressFromUser("Network mask", &info[SN_MASK]) == SUB_ERROR)
755         return NULL;
756       if (atoi(info[SN_LOW]) == (int)ntohl(inet_addr(S_DEFAULT_LOW)))
757         {
758           struct in_addr low;
759           unsigned long mask, addr;
760
761           addr = atoi(info[SN_ADDRESS]);
762           mask = atoi(info[SN_MASK]);
763           low.s_addr = atoi(info[SN_LOW]);
764           low.s_addr = (low.s_addr & ~mask) | (addr & mask);
765           free(info[SN_LOW]);
766           sprintf(temp_buf, "%ld", low.s_addr);
767           info[SN_LOW] = strdup(temp_buf);
768         }
769       if (GetAddressFromUser("Lowest assignable address", &info[SN_LOW]) ==
770           SUB_ERROR)
771         return NULL;
772       if (atoi(info[SN_HIGH]) == (int)ntohl(inet_addr(S_DEFAULT_HIGH)))
773         {
774           struct in_addr high;
775           unsigned long mask, addr;
776
777           addr = atoi(info[SN_ADDRESS]);
778           mask = atoi(info[SN_MASK]);
779           high.s_addr = atoi(info[SN_HIGH]);
780           high.s_addr = (high.s_addr & ~mask) | (addr & mask);
781           free(info[SN_HIGH]);
782           sprintf(temp_buf, "%ld", high.s_addr);
783           info[SN_HIGH] = strdup(temp_buf);
784         }
785       if (GetAddressFromUser("Highest assignable address", &info[SN_HIGH]) ==
786           SUB_ERROR)
787         return NULL;
788       if (GetValueFromUser("Hostname prefix", &info[SN_PREFIX]) == SUB_ERROR)
789         return NULL;
790       if (GetTypeFromUser("Owner type", "ace_type", &info[SN_ACE_TYPE]) ==
791           SUB_ERROR)
792         return NULL;
793       if (strcmp(info[SN_ACE_TYPE], "NONE") &&
794           GetValueFromUser("Owner name", &info[SN_ACE_NAME]) == SUB_ERROR)
795         return NULL;
796       if (!strcmp(info[SN_ACE_TYPE], "KERBEROS"))
797           {
798             char *canon;
799
800             status = mrcl_validate_kerberos_member(info[SN_ACE_NAME], &canon);
801             if (mrcl_get_message())
802               Put_message(mrcl_get_message());
803             if (status == MRCL_REJECT)
804               return NULL;
805             free(info[SN_ACE_NAME]);
806             info[SN_ACE_NAME] = canon;
807           }
808       FreeAndClear(&info[SN_MODTIME], TRUE);
809       FreeAndClear(&info[SN_MODBY], TRUE);
810       FreeAndClear(&info[SN_MODWITH], TRUE);
811       break;
812     case CLUSTER:
813       if (GetValueFromUser("Cluster's Description:", &info[C_DESCRIPT]) ==
814           SUB_ERROR)
815         return NULL;
816       if (GetValueFromUser("Cluster's Location:", &info[C_LOCATION]) ==
817           SUB_ERROR)
818         return NULL;
819       FreeAndClear(&info[C_MODTIME], TRUE);
820       FreeAndClear(&info[C_MODBY], TRUE);
821       FreeAndClear(&info[C_MODWITH], TRUE);
822       break;
823     case DATA:
824       if (GetValueFromUser("Label defining this data?", &info[CD_LABEL]) ==
825           SUB_ERROR)
826         return NULL;
827       if (GetValueFromUser("The data itself ? ", &info[CD_DATA]) == SUB_ERROR)
828         return NULL;
829       break;
830     case CONTAINER:
831       if (GetYesNoValueFromUser("Is this a public container", 
832                                 &info[CON_PUBLIC]) == SUB_ERROR)
833         return NULL;
834       if (GetValueFromUser("Container's Description:", &info[CON_DESCRIPT]) ==
835           SUB_ERROR)
836         return NULL;
837       if (GetValueFromUser("Container's Location:", &info[CON_LOCATION]) ==
838           SUB_ERROR)
839         return NULL;
840       if (GetValueFromUser("Container's Contact:", &info[CON_CONTACT]) == 
841           SUB_ERROR)
842         return NULL;
843       if (GetTypeFromUser("Container's owner type", "ace_type", 
844                           &info[CON_OWNER_TYPE]) == SUB_ERROR)
845         return NULL;
846       if (strcmp(info[CON_OWNER_TYPE], "NONE") &&
847           GetValueFromUser("Owner's Name", &info[CON_OWNER_NAME]) == SUB_ERROR)
848         return NULL;
849       if (!strcmp(info[CON_OWNER_TYPE], "KERBEROS"))
850         {
851           char *canon;
852
853           mrcl_validate_kerberos_member(info[CON_OWNER_NAME], &canon);
854           if (mrcl_get_message())
855             Put_message(mrcl_get_message());
856           if (status == MRCL_REJECT)
857             return NULL;
858           free(info[CON_OWNER_NAME]);
859           info[CON_OWNER_NAME] = canon;
860         }
861       if (GetTypeFromUser("Container's Membership ACL", "ace_type",
862                           &info[CON_MEMACE_TYPE]) == SUB_ERROR)
863         return NULL;
864       if (strcmp(info[CON_MEMACE_TYPE], "NONE") &&
865           GetValueFromUser("Membership ACL", &info[CON_MEMACE_NAME])
866           == SUB_ERROR)
867         return NULL;
868       if (!strcmp(info[CON_MEMACE_TYPE], "KERBEROS"))
869         {
870           char *canon;
871
872           mrcl_validate_kerberos_member(info[CON_MEMACE_NAME], &canon);
873           if (mrcl_get_message())
874             Put_message(mrcl_get_message());
875           if (status == MRCL_REJECT)
876             return NULL;
877           free(info[CON_MEMACE_NAME]);
878           info[CON_MEMACE_NAME] = canon;
879         }
880       FreeAndClear(&info[CON_MODTIME], TRUE);
881       FreeAndClear(&info[CON_MODBY], TRUE);
882       FreeAndClear(&info[CON_MODWITH], TRUE);
883       break;
884     }
885
886   /*
887    * Slide the newname into the #2 slot, this screws up all future references
888    * to this list.
889    */
890   if (name)
891     SlipInNewName(info, newname);
892
893   return info;
894 }
895
896 /* -----------  Machine Menu ----------- */
897
898 /*      Function Name: ShowMachineInfo
899  *      Description: This function shows the information about a machine.
900  *      Arguments: argc, argv - the name of the machine in argv[1].
901  *      Returns: DM_NORMAL.
902  */
903
904 int ShowMachineInfo(int argc, char **argv)
905 {
906   struct mqelem *top;
907   char *tmpname;
908
909   tmpname = canonicalize_hostname(strdup(argv[1]));
910   top = GetMCInfo(MACHINE, tmpname, NULL);
911   Loop(top, ((void (*)(char **)) PrintMachInfo));
912   FreeQueue(top);
913   return DM_NORMAL;
914 }
915
916 /*      Function Name: ShowMachineQuery
917  *      Description: This function shows the information about a machine.
918  *              or group of machines, which may be selected through a
919  *              number of criteria.
920  *      Arguments: argc, argv - the name of the machine in argv[1],
921  *              the address of the machine in argv[2],
922  *              the location of the machine in argv[3],
923  *              and the contact name in argv[4].
924  *           any of these may be wildcards.
925  *      Returns: DM_NORMAL.
926  */
927
928 int ShowMachineQuery(int argc, char **argv)
929 {
930   int stat;
931   struct mqelem *top, *elem = NULL;
932   char *args[5];
933
934   if (!strcmp(argv[1], "") && !strcmp(argv[2], "") &&
935       !strcmp(argv[3], "") && !strcmp(argv[4], ""))
936     {
937       Put_message("You must specify at least one parameter of the query.");
938       return DM_NORMAL;
939     }
940
941   if (*argv[1])
942     args[0] = canonicalize_hostname(strdup(argv[1]));
943   else
944     args[0] = "*";
945   if (*argv[2])
946     args[1] = argv[2];
947   else
948     args[1] = "*";
949   if (*argv[3])
950     args[2] = argv[3];
951   else
952     args[2] = "*";
953   if (*argv[4])
954     args[3] = argv[4];
955   else
956     args[3] = "*";
957
958   if ((stat = do_mr_query("get_host", 4, args, StoreInfo, &elem)))
959     {
960       if (stat == MR_NO_MATCH)
961         Put_message("No machine(s) found matching query in the database.");
962       else
963         com_err(program_name, stat, " in get_machine.");
964       return DM_NORMAL;
965     }
966   top = QueueTop(elem);
967   Loop(top, ((void (*)(char **)) PrintMachInfo));
968   FreeQueue(top);
969   return DM_NORMAL;
970 }
971
972 /*      Function Name: AddMachine
973  *      Description: This function adds a new machine to the database.
974  *      Arguments: argc, argv - the name of the network in argv[1].
975  *      Returns: DM_NORMAL.
976  */
977
978 int AddMachine(int argc, char **argv)
979 {
980   char **args, *info[MAX_ARGS_SIZE], *name, buf[256], *xargs[5];
981   char **rinfo;
982   struct mqelem *elem = NULL;
983   int stat;
984
985   if (!ValidName(argv[1]))      /* Checks for wildcards. */
986     return DM_NORMAL;
987
988   /*
989    * get the network record
990    */
991
992   if (strcasecmp(argv[1], "none") &&
993       (stat = do_mr_query("get_subnet", 1, &argv[1], StoreInfo, &elem)))
994     {
995       if (stat == MR_NO_MATCH)
996         {
997           char buf[128];
998           sprintf(buf, "Network '%s' is not in the database.", argv[1]);
999           Put_message(buf);
1000         } else
1001           com_err(program_name, stat, " in get_subnet.");
1002       return DM_NORMAL;
1003     }
1004
1005   /*
1006    * Check to see if this machine already exists.
1007    */
1008
1009   name = strdup(""); /* want to put prefix here */
1010   if (GetValueFromUser("Machine name", &name) == SUB_ERROR)
1011     return 0;
1012
1013   name = canonicalize_hostname(strdup(name));
1014
1015   xargs[0] = name;
1016   xargs[1] = xargs[2] = xargs[3] = "*";
1017   if (!(stat = do_mr_query("get_host", 4, xargs, NULL, NULL)))
1018     {
1019       sprintf(buf, "The machine '%s' already exists.", name);
1020       Put_message(buf);
1021       free(name);
1022       return DM_NORMAL;
1023     }
1024   else if (stat != MR_NO_MATCH)
1025     {
1026       com_err(program_name, stat,
1027               " while checking machine '%s' in AddMachine.", name);
1028       free(name);
1029       return DM_NORMAL;
1030     }
1031   rinfo = SetMachineDefaults(info, name);
1032   rinfo[M_SUBNET] = strdup(argv[1]);
1033   if (!(args = AskMCDInfo(rinfo, MACHINE, FALSE)))
1034     {
1035       Put_message("Aborted.");
1036       return DM_NORMAL;
1037     }
1038
1039   /*
1040    * Actually create the new Machine.
1041    */
1042
1043   if ((stat = do_mr_query("add_host", CountArgs(args), args, NULL, NULL)))
1044     com_err(program_name, stat, " in AddMachine.");
1045
1046   FreeInfo(info);
1047   free(name);
1048   return DM_NORMAL;
1049 }
1050
1051 /*      Function Name: RealUpdateMachine
1052  *      Description: Performs the actual update of the machine data.
1053  *      Arguments: info - the information on the machine to update.
1054  *                 junk - an UNUSED Boolean.
1055  *      Returns: none.
1056  */
1057
1058 static void RealUpdateMachine(char **info, Bool junk)
1059 {
1060   int stat;
1061   char **args = AskMCDInfo(info, MACHINE, TRUE);
1062   if (!args)
1063     {
1064       Put_message("Aborted.");
1065       return;
1066     }
1067   if ((stat = do_mr_query("update_host", CountArgs(args), args, NULL, NULL)))
1068     com_err(program_name, stat, " in UpdateMachine.");
1069   else
1070     Put_message("Machine successfully updated.");
1071 }
1072
1073 /*      Function Name: UpdateMachine
1074  *      Description: This function adds a new machine to the database.
1075  *      Arguments: argc, argv - the name of the machine in argv[1].
1076  *      Returns: DM_NORMAL.
1077  */
1078
1079 int UpdateMachine(int argc, char **argv)
1080 {
1081   struct mqelem *top;
1082   char *tmpname;
1083
1084   tmpname = canonicalize_hostname(strdup(argv[1]));
1085   top = GetMCInfo(MACHINE, tmpname, NULL);
1086   QueryLoop(top, NullPrint, RealUpdateMachine, "Update the machine");
1087
1088   FreeQueue(top);
1089   free(tmpname);
1090   return DM_NORMAL;
1091 }
1092
1093 /*      Function Name: CheckAndRemoveFromCluster
1094  *      Description: This func tests to see if a machine is in a cluster.
1095  *                   and if so then removes it
1096  *      Arguments: name - name of the machine (already Canonicalized).
1097  *                 ask_user- query the user before removing if from clusters?
1098  *      Returns: MR_ERROR if machine left in a cluster, or mr_error.
1099  */
1100
1101 int CheckAndRemoveFromCluster(char *name, Bool ask_user)
1102 {
1103   int stat, ret_value;
1104   Bool delete_it;
1105   char *args[10], temp_buf[BUFSIZ], *ptr;
1106   struct mqelem *top, *elem = NULL;
1107
1108   ret_value = SUB_NORMAL;       /* initialize ret_value. */
1109   args[0] = name;
1110   args[1] = "*";
1111   stat = do_mr_query("get_machine_to_cluster_map", 2, args, StoreInfo, &elem);
1112   if (stat && stat != MR_NO_MATCH)
1113     {
1114       com_err(program_name, stat, " in get_machine_to_cluster_map.");
1115       return DM_NORMAL;
1116     }
1117   if (stat == MR_SUCCESS)
1118     {
1119       elem = top = QueueTop(elem);
1120       if (ask_user)
1121         {
1122           sprintf(temp_buf, "%s is assigned to the following clusters.", name);
1123           Put_message(temp_buf);
1124           Loop(top, (void (*)(char **)) PrintMCMap);
1125           ptr = "Remove this machine from ** ALL ** these clusters?";
1126           if (YesNoQuestion(ptr, FALSE) == TRUE) /* may return -1. */
1127             delete_it = TRUE;
1128           else
1129             {
1130               Put_message("Aborting...");
1131               FreeQueue(top);
1132               return SUB_ERROR;
1133             }
1134         }
1135       else
1136         delete_it = TRUE;
1137
1138       if (delete_it)
1139         {
1140           while (elem)
1141             {
1142               char **info = elem->q_data;
1143               if ((stat = do_mr_query("delete_machine_from_cluster",
1144                                        2, info, NULL, NULL)))
1145                 {
1146                   ret_value = SUB_ERROR;
1147                   com_err(program_name, stat,
1148                           " in delete_machine_from_cluster.");
1149                   sprintf(temp_buf,
1150                           "Machine %s ** NOT ** removed from cluster %s.",
1151                           info[MAP_MACHINE], info[MAP_CLUSTER]);
1152                   Put_message(temp_buf);
1153                 }
1154               elem = elem->q_forw;
1155             }
1156         }
1157     }
1158   return ret_value;
1159 }
1160
1161 /*      Function Name: CheckAndRemoveCnames
1162  *      Description: This func tests to see if a machine has cnames, 
1163  *                   and if so then removes them.
1164  *      Arguments: name - name of the machine (already Canonicalized).
1165  *                 ask_user- query the user before removing cnames?
1166  *      Returns: MR_ERROR if machine left with a cname, or mr_error.
1167  */
1168
1169 int CheckAndRemoveCnames(char *name, Bool ask_user)
1170 {
1171   int stat, ret_value;
1172   Bool delete_it;
1173   char *args[10], temp_buf[BUFSIZ], *ptr;
1174   struct mqelem *top, *elem = NULL;
1175
1176   ret_value = SUB_NORMAL;
1177   args[0] = "*";
1178   args[1] = name;
1179   stat = do_mr_query("get_hostalias", 2, args, StoreInfo, &elem);
1180   if (stat && stat != MR_NO_MATCH)
1181     {
1182       com_err(program_name, stat, " in get_hostalias.");
1183       return DM_NORMAL;
1184     }
1185   if (stat == MR_SUCCESS)
1186     {
1187       elem = top = QueueTop(elem);
1188       if (ask_user)
1189         {
1190           sprintf(temp_buf, "%s has the following cnames.", name);
1191           Put_message(temp_buf);
1192           Loop(top, (void (*)(char **)) PrintCname);
1193           ptr = "Remove ** ALL ** these cnames?";
1194           if (YesNoQuestion(ptr, FALSE) == TRUE) /* may return -1. */
1195             delete_it = TRUE;
1196           else
1197             {
1198               Put_message("Aborting...");
1199               FreeQueue(top);
1200               return SUB_ERROR;
1201             }
1202         }
1203       else
1204         delete_it = TRUE;
1205
1206       if (delete_it)
1207         {
1208           while (elem)
1209             {
1210               char **info = elem->q_data;
1211               if ((stat = do_mr_query("delete_hostalias", 2, info, 
1212                                       NULL, NULL)))
1213                 {
1214                   ret_value = SUB_ERROR;
1215                   com_err(program_name, stat, " in delete_hostalias.");
1216                   sprintf(temp_buf, 
1217                           "Cname %s ** NOT ** removed from host %s.",
1218                           info[0], info[1]);
1219                   Put_message(temp_buf);
1220                 }
1221               elem = elem->q_forw;
1222             }
1223         }
1224     }
1225   return ret_value;
1226 }
1227
1228 /*      Function Name: RealDeleteMachine
1229  *      Description: Actually Deletes the Machine.
1230  *      Arguments: info - nescessary information stored as an array of char *'s
1231  *                 one_machine - a boolean, true if there is only one item in
1232  *                               the query.
1233  *      Returns: none.
1234  */
1235
1236 static void RealDeleteMachine(char **info, Bool one_machine)
1237 {
1238   int stat;
1239   char temp_buf[BUFSIZ];
1240
1241   sprintf(temp_buf, "Are you sure you want to delete the machine %s (y/n)? ",
1242           info[M_NAME]);
1243   if (!one_machine || Confirm(temp_buf))
1244     {
1245       if (CheckAndRemoveFromCluster(info[M_NAME], TRUE) != SUB_ERROR)
1246         {
1247           if (CheckAndRemoveCnames(info[M_NAME], TRUE) != SUB_ERROR)
1248             {
1249               if ((stat = do_mr_query("delete_host", 1,
1250                                       &info[M_NAME], NULL, NULL)))
1251                 {
1252                   com_err(program_name, stat, " in DeleteMachine.");
1253                   sprintf(temp_buf, "%s ** NOT ** deleted.",
1254                           info[M_NAME]);
1255                   Put_message(temp_buf);
1256                 }
1257               else
1258                 {
1259                   sprintf(temp_buf, "%s successfully Deleted.", info[M_NAME]);
1260                   Put_message(temp_buf);
1261                 }
1262             }
1263         }
1264     }
1265 }
1266
1267 /*      Function Name: DeleteMachine
1268  *      Description: This function removes a machine from the data base.
1269  *      Arguments: argc, argv - the machines name int argv[1].
1270  *      Returns: DM_NORMAL.
1271  */
1272
1273 /* Perhaps we should remove the cluster if it has no machine now. */
1274
1275 int DeleteMachine(int argc, char **argv)
1276 {
1277   struct mqelem *top;
1278   char *tmpname;
1279
1280   tmpname = canonicalize_hostname(strdup(argv[1]));
1281   top = GetMCInfo(MACHINE, tmpname, (char *) NULL);
1282   QueryLoop(top, PrintMachInfo, RealDeleteMachine, "Delete the machine");
1283   FreeQueue(top);
1284   free(tmpname);
1285   return DM_NORMAL;
1286 }
1287
1288
1289 /*      Function Name: ShowCname
1290  *      Description: This function shows machine aliases
1291  *      Arguments: argc, argv - the alias argv[1], the real name in argv[2]
1292  *      Returns: DM_NORMAL.
1293  */
1294
1295 int ShowCname(int argc, char **argv)
1296 {
1297   struct mqelem *top;
1298   char *tmpalias, *tmpname;
1299
1300   tmpalias = partial_canonicalize_hostname(strdup(argv[1]));
1301   tmpname = canonicalize_hostname(strdup(argv[2]));
1302   top = GetMCInfo(CNAME, tmpalias, tmpname);
1303   Put_message("");              /* blank line on screen */
1304   Loop(top, ((void (*)(char **)) PrintCname));
1305   FreeQueue(top);
1306   return DM_NORMAL;
1307 }
1308
1309
1310 int AddCname(int argc, char **argv)
1311 {
1312   int stat;
1313   char *args[10];
1314
1315   args[0] = partial_canonicalize_hostname(strdup(argv[1]));
1316   args[1] = canonicalize_hostname(strdup(argv[2]));
1317   stat = do_mr_query("add_hostalias", 2, args, NULL, NULL);
1318   switch (stat)
1319     {
1320     case MR_SUCCESS:
1321       break;
1322     case MR_EXISTS:
1323       Put_message("That alias name is already in use.");
1324       break;
1325     case MR_PERM:
1326       Put_message("Permission denied.  "
1327                   "(Regular users can only add two aliases to a host.");
1328       break;
1329     default:
1330       com_err(program_name, stat, " in add_hostalias");
1331     }
1332   return DM_NORMAL;
1333 }
1334
1335
1336 int DeleteCname(int argc, char **argv)
1337 {
1338   int stat;
1339   char *args[10];
1340
1341   args[0] = partial_canonicalize_hostname(strdup(argv[1]));
1342   args[1] = canonicalize_hostname(strdup(argv[2]));
1343   stat = do_mr_query("delete_hostalias", 2, args, NULL, NULL);
1344   if (stat)
1345     com_err(program_name, stat, " in delete_hostalias");
1346   return DM_NORMAL;
1347 }
1348
1349
1350 /*      Function Name: AddMachineToCluster
1351  *      Description: This function adds a machine to a cluster
1352  *      Arguments: argc, argv - The machine name is argv[1].
1353  *                              The cluster name in argv[2].
1354  *      Returns: DM_NORMAL.
1355  */
1356
1357 int AddMachineToCluster(int argc, char **argv)
1358 {
1359   int stat;
1360   char *machine, *cluster, temp_buf[BUFSIZ], *args[10];
1361   Bool add_it, one_machine, one_cluster;
1362   struct mqelem *melem, *mtop, *celem, *ctop;
1363
1364   machine = canonicalize_hostname(strdup(argv[1]));
1365   if (strcasecmp(machine, argv[1]) && *argv[1] != '"')
1366     {
1367       sprintf(temp_buf, "Warning: '%s' canonicalized to '%s'.",
1368               argv[1], machine);
1369       Put_message(temp_buf);
1370     }
1371   cluster = argv[2];
1372
1373   celem = ctop = GetMCInfo(CLUSTER, cluster, NULL);
1374   melem = mtop = GetMCInfo(MACHINE, machine, NULL);
1375   free(machine);
1376
1377   one_machine = (QueueCount(mtop) == 1);
1378   one_cluster = (QueueCount(ctop) == 1);
1379
1380   /* No good way to use QueryLoop() here, sigh */
1381
1382   while (melem)
1383     {
1384       char **minfo = melem->q_data;
1385       while (celem)
1386         {
1387           char **cinfo = celem->q_data;
1388           if (one_machine && one_cluster)
1389             add_it = TRUE;
1390           else
1391             {
1392               sprintf(temp_buf, "Add machine %s to cluster %s (y/n/q) ?",
1393                       minfo[M_NAME], cinfo[C_NAME]);
1394               switch (YesNoQuitQuestion(temp_buf, FALSE))
1395                 {
1396                 case TRUE:
1397                   add_it = TRUE;
1398                   break;
1399                 case FALSE:
1400                   add_it = FALSE;
1401                   break;
1402                 default:
1403                   Put_message("Aborting...");
1404                   FreeQueue(ctop);
1405                   FreeQueue(mtop);
1406                   return DM_NORMAL;
1407                 }
1408             }
1409           if (add_it)
1410             {
1411               args[0] = minfo[M_NAME];
1412               args[1] = cinfo[C_NAME];
1413               stat = do_mr_query("add_machine_to_cluster", 2, args,
1414                                  NULL, NULL);
1415               switch (stat)
1416                 {
1417                 case MR_SUCCESS:
1418                   break;
1419                 case MR_EXISTS:
1420                   sprintf(temp_buf, "%s is already in cluster %s",
1421                           minfo[M_NAME], cinfo[C_NAME]);
1422                   Put_message(temp_buf);
1423                   break;
1424                 default:
1425                   com_err(program_name, stat, " in AddMachineToCluster.");
1426                   break;
1427                 }
1428             }
1429           celem = celem->q_forw;
1430         }
1431       celem = ctop;             /* reset cluster element. */
1432       melem = melem->q_forw;
1433     }
1434   FreeQueue(ctop);
1435   FreeQueue(mtop);
1436   return DM_NORMAL;
1437 }
1438
1439 /*      Function Name: RealRemoveMachineFromCluster
1440  *      Description: This function actually removes the machine from its
1441  *                   cluster.
1442  *      Arguments: info - all information nescessary to perform the removal.
1443  *                 one_map - True if there is only one case, and we should
1444  *                           confirm.
1445  *      Returns: none.
1446  */
1447
1448 static void RealRemoveMachineFromCluster(char **info, Bool one_map)
1449 {
1450   char temp_buf[BUFSIZ];
1451   int stat;
1452
1453   sprintf(temp_buf, "Remove %s from the cluster %s",
1454           info[MAP_MACHINE], info[MAP_CLUSTER]);
1455   if (!one_map || Confirm(temp_buf))
1456     {
1457       if ((stat = do_mr_query("delete_machine_from_cluster", 2,
1458                               info, NULL, NULL)))
1459         com_err(program_name, stat, " in delete_machine_from_cluster");
1460       else
1461         {
1462           sprintf(temp_buf, "%s has been removed from the cluster %s.",
1463                   info[MAP_MACHINE], info[MAP_CLUSTER]);
1464           Put_message(temp_buf);
1465         }
1466     }
1467   else
1468     Put_message("Machine not removed.");
1469 }
1470
1471 /*      Function Name: RemoveMachineFromCluster
1472  *      Description: Removes this machine form a specific cluster.
1473  *      Arguments: argc, argv - Name of machine in argv[1].
1474  *                              Name of cluster in argv[2].
1475  *      Returns: none.
1476  */
1477
1478 int RemoveMachineFromCluster(int argc, char **argv)
1479 {
1480   struct mqelem *elem = NULL;
1481   char buf[BUFSIZ], *args[10];
1482   int stat;
1483
1484   args[MAP_MACHINE] = canonicalize_hostname(strdup(argv[1]));
1485   if (strcasecmp(args[MAP_MACHINE], argv[1]) && *argv[1] != '"')
1486     {
1487       sprintf(buf, "Warning: '%s' canonicalized to '%s'.",
1488               argv[1], args[MAP_MACHINE]);
1489       Put_message(buf);
1490     }
1491   args[MAP_CLUSTER] = argv[2];
1492   args[MAP_END] = NULL;
1493
1494   stat = do_mr_query("get_machine_to_cluster_map", CountArgs(args), args,
1495                      StoreInfo, &elem);
1496   if (stat == MR_NO_MATCH)
1497     {
1498       sprintf(buf, "The machine %s is not in the cluster %s.",
1499               args[MAP_MACHINE], args[MAP_CLUSTER]);
1500       Put_message(buf);
1501       free(args[MAP_MACHINE]);
1502       return DM_NORMAL;
1503     }
1504   if (stat != MR_SUCCESS)
1505     com_err(program_name, stat, " in delete_machine_from_cluster");
1506
1507   elem = QueueTop(elem);
1508   QueryLoop(elem, PrintMCMap, RealRemoveMachineFromCluster,
1509             "Remove this machine from this cluster");
1510
1511   FreeQueue(elem);
1512   free(args[MAP_MACHINE]);
1513   return DM_NORMAL;
1514 }
1515
1516 /* ---------- Subnet Menu -------- */
1517
1518 /*      Function Name: ShowSubnetInfo
1519  *      Description: Gets information about a subnet given its name.
1520  *      Arguments: argc, argc - the name of the subnet in in argv[1].
1521  *      Returns: DM_NORMAL.
1522  */
1523
1524 int ShowSubnetInfo(int argc, char **argv)
1525 {
1526   struct mqelem *top;
1527
1528   top = GetMCInfo(SUBNET, argv[1], (char *) NULL);
1529   Loop(top, (void (*)(char **)) PrintSubnetInfo);
1530   FreeQueue(top);
1531   return DM_NORMAL;
1532 }
1533
1534 /*      Function Name: AddSubnet
1535  *      Description: Creates a new subnet.
1536  *      Arguments: argc, argv - the name of the new subnet is argv[1].
1537  *      Returns: DM_NORMAL.
1538  */
1539
1540 int AddSubnet(int argc, char **argv)
1541 {
1542   char **args, *info[MAX_ARGS_SIZE], *name = argv[1];
1543   int stat;
1544
1545   /*
1546    * Check to see if this subnet already exists.
1547    */
1548   if (!ValidName(name))
1549     return DM_NORMAL;
1550
1551   if ((stat = do_mr_query("get_subnet", 1, &name, NULL, NULL)) == MR_SUCCESS)
1552     {
1553       Put_message("This subnet already exists.");
1554       return DM_NORMAL;
1555     }
1556   else if (stat != MR_NO_MATCH)
1557     {
1558       com_err(program_name, stat, " in AddSubnet.");
1559       return DM_NORMAL;
1560     }
1561   if (!(args = AskMCDInfo(SetSubnetDefaults(info, name), SUBNET, FALSE)))
1562     {
1563       Put_message("Aborted.");
1564       FreeInfo(info);
1565       return DM_NORMAL;
1566     }
1567
1568   /*
1569    * Actually create the new Subnet.
1570    */
1571   if ((stat = do_mr_query("add_subnet", CountArgs(args), args, NULL, NULL)))
1572     com_err(program_name, stat, " in AddSubnet.");
1573
1574   FreeInfo(info);
1575   return DM_NORMAL;
1576 }
1577
1578 /*      Function Name: RealUpdateSubnet
1579  *      Description: This function actually performs the subnet update.
1580  *      Arguments: info - all information nesc. for updating the subnet.
1581  *                 junk - an UNUSED boolean.
1582  *      Returns: none.
1583  */
1584
1585 static void RealUpdateSubnet(char **info, Bool junk)
1586 {
1587   int stat;
1588   char **args = AskMCDInfo(info, SUBNET, TRUE);
1589   if (!args)
1590     {
1591       Put_message("Aborted.");
1592       return;
1593     }
1594   if ((stat = do_mr_query("update_subnet", CountArgs(args), args, NULL, NULL)))
1595     com_err(program_name, stat, " in UpdateSubnet.");
1596   else
1597     Put_message("Subnet successfully updated.");
1598 }
1599
1600 /*      Function Name: UpdateSubnet
1601  *      Description: This Function Updates a subnet
1602  *      Arguments: name of the subnet in argv[1].
1603  *      Returns: DM_NORMAL.
1604  */
1605
1606 int UpdateSubnet(int argc, char **argv)
1607 {
1608   struct mqelem *top;
1609   top = GetMCInfo(SUBNET, argv[1], NULL);
1610   QueryLoop(top, NullPrint, RealUpdateSubnet, "Update the subnet");
1611
1612   FreeQueue(top);
1613   return DM_NORMAL;
1614 }
1615
1616 /*      Function Name: RealDeleteSubnet
1617  *      Description: Actually performs the subnet deletion.
1618  *      Arguments: info - all information about this subnet.
1619  *                 one_subnet - If true then there was only one subnet in
1620  *                               the queue, and we should confirm.
1621  *      Returns: none.
1622  */
1623
1624 static void RealDeleteSubnet(char **info, Bool one_subnet)
1625 {
1626   int stat;
1627   char temp_buf[BUFSIZ];
1628
1629   sprintf(temp_buf,
1630           "Are you sure the you want to delete the subnet %s (y/n) ?",
1631           info[C_NAME]);
1632   if (!one_subnet || Confirm(temp_buf))
1633     {
1634       if ((stat = do_mr_query("delete_subnet", 1, &info[C_NAME], NULL, NULL)))
1635         {
1636           com_err(program_name, stat, " in delete_subnet.");
1637           sprintf(temp_buf, "Subnet %s ** NOT ** deleted.", info[C_NAME]);
1638           Put_message(temp_buf);
1639         }
1640       else
1641         {
1642           sprintf(temp_buf, "subnet %s successfully deleted.",
1643                   info[C_NAME]);
1644           Put_message(temp_buf);
1645         }
1646     }
1647 }
1648
1649 /*      Function Name: DeleteSubnet
1650  *      Description: This function removes a subnet from the database.
1651  *      Arguments: argc, argv - the name of the subnet is stored in argv[1].
1652  *      Returns: DM_NORMAL.
1653  */
1654
1655 int DeleteSubnet(int argc, char **argv)
1656 {
1657   struct mqelem *top;
1658
1659   top = GetMCInfo(SUBNET, argv[1], NULL);
1660   QueryLoop(top, PrintSubnetInfo, RealDeleteSubnet, "Delete the subnet");
1661
1662   FreeQueue(top);
1663   return DM_NORMAL;
1664 }
1665
1666 /* ---------- Cluster Menu -------- */
1667
1668 /*      Function Name: ShowClusterInfo
1669  *      Description: Gets information about a cluser given its name.
1670  *      Arguments: argc, argc - the name of the cluster in in argv[1].
1671  *      Returns: DM_NORMAL.
1672  */
1673
1674 int ShowClusterInfo(int argc, char **argv)
1675 {
1676   struct mqelem *top;
1677
1678   top = GetMCInfo(CLUSTER, argv[1], NULL);
1679   Loop(top, (void (*)(char **)) PrintClusterInfo);
1680   FreeQueue(top);
1681   return DM_NORMAL;
1682 }
1683
1684 /*      Function Name: AddCluster
1685  *      Description: Creates a new cluster.
1686  *      Arguments: argc, argv - the name of the new cluster is argv[1].
1687  *      Returns: DM_NORMAL.
1688  */
1689
1690 int AddCluster(int argc, char **argv)
1691 {
1692   char **args, *info[MAX_ARGS_SIZE], *name = argv[1];
1693   int stat;
1694
1695   /*
1696    * Check to see if this cluster already exists.
1697    */
1698   if (!ValidName(name))
1699     return DM_NORMAL;
1700
1701   if ((stat = do_mr_query("get_cluster", 1, &name, NULL, NULL)) == MR_SUCCESS)
1702     {
1703       Put_message("This cluster already exists.");
1704       return DM_NORMAL;
1705     }
1706   else if (stat != MR_NO_MATCH)
1707     {
1708       com_err(program_name, stat, " in AddCluster.");
1709       return DM_NORMAL;
1710     }
1711   if (!(args = AskMCDInfo(SetClusterDefaults(info, name), CLUSTER, FALSE)))
1712     {
1713       Put_message("Aborted.");
1714       FreeInfo(info);
1715       return DM_NORMAL;
1716     }
1717
1718   /*
1719    * Actually create the new Cluster.
1720    */
1721   if ((stat = do_mr_query("add_cluster", CountArgs(args), args, NULL, NULL)))
1722     com_err(program_name, stat, " in AddCluster.");
1723
1724   FreeInfo(info);
1725   return DM_NORMAL;
1726 }
1727
1728 /*      Function Name: RealUpdateCluster
1729  *      Description: This function actually performs the cluster update.
1730  *      Arguments: info - all information nesc. for updating the cluster.
1731  *                 junk - an UNUSED boolean.
1732  *      Returns: none.
1733  */
1734
1735 static void RealUpdateCluster(char **info, Bool junk)
1736 {
1737   int stat;
1738   char **args = AskMCDInfo(info, CLUSTER, TRUE);
1739
1740   if (!args)
1741     {
1742       Put_message("Aborted.");
1743       return;
1744     }
1745   if ((stat = do_mr_query("update_cluster", CountArgs(args),
1746                           args, NULL, NULL)))
1747     com_err(program_name, stat, " in UpdateCluster.");
1748   else
1749     Put_message("Cluster successfully updated.");
1750 }
1751
1752 /*      Function Name: UpdateCluster
1753  *      Description: This Function Updates a cluster
1754  *      Arguments: name of the cluster in argv[1].
1755  *      Returns: DM_NORMAL.
1756  */
1757
1758 int UpdateCluster(int argc, char **argv)
1759 {
1760   struct mqelem *top;
1761   top = GetMCInfo(CLUSTER, argv[1], NULL);
1762   QueryLoop(top, NullPrint, RealUpdateCluster, "Update the cluster");
1763
1764   FreeQueue(top);
1765   return DM_NORMAL;
1766 }
1767
1768 /*      Function Name: CheckAndRemoveMachine
1769  *      Description: This function checks and removes all machines from a
1770  *                   cluster.
1771  *      Arguments: name - name of the cluster.
1772  *                 ask_first - if TRUE, then we will query the user, before
1773  *                             deletion.
1774  *      Returns: SUB_ERROR if all machines not removed.
1775  */
1776
1777 int CheckAndRemoveMachines(char *name, Bool ask_first)
1778 {
1779   int stat, ret_value;
1780   Bool delete_it;
1781   char *args[10], temp_buf[BUFSIZ], *ptr;
1782   struct mqelem *top, *elem = NULL;
1783
1784   ret_value = SUB_NORMAL;
1785   args[MAP_MACHINE] = "*";
1786   args[MAP_CLUSTER] = name;
1787   stat = do_mr_query("get_machine_to_cluster_map", 2, args, StoreInfo, &elem);
1788   if (stat && stat != MR_NO_MATCH)
1789     {
1790       com_err(program_name, stat, " in get_machine_to_cluster_map.");
1791       return DM_NORMAL;
1792     }
1793   if (stat == MR_SUCCESS)
1794     {
1795       elem = top = QueueTop(elem);
1796       if (ask_first)
1797         {
1798           sprintf(temp_buf, "The cluster %s has the following machines in it:",
1799                   name);
1800           Put_message(temp_buf);
1801           while (elem)
1802             {
1803               char **info = elem->q_data;
1804               Print(1, &info[MAP_MACHINE], (char *) NULL);
1805               elem = elem->q_forw;
1806             }
1807           ptr = "Remove ** ALL ** these machines from this cluster?";
1808
1809           if (YesNoQuestion(ptr, FALSE) == TRUE) /* may return -1. */
1810             delete_it = TRUE;
1811           else
1812             {
1813               Put_message("Aborting...");
1814               FreeQueue(top);
1815               return SUB_ERROR;
1816             }
1817         }
1818       else
1819         delete_it = TRUE;
1820
1821       if (delete_it)
1822         {
1823           elem = top;
1824           while (elem)
1825             {
1826               char **info = elem->q_data;
1827               if ((stat = do_mr_query("delete_machine_from_cluster",
1828                                       2, info, NULL, NULL)))
1829                 {
1830                   ret_value = SUB_ERROR;
1831                   com_err(program_name, stat,
1832                           " in delete_machine_from_cluster.");
1833                   sprintf(temp_buf,
1834                           "Machine %s ** NOT ** removed from cluster %s.",
1835                           info[MAP_MACHINE], info[MAP_CLUSTER]);
1836                   Put_message(temp_buf);
1837                 }
1838               elem = elem->q_forw;
1839             }
1840         }
1841     }
1842   return ret_value;
1843 }
1844
1845 /*      Function Name: RealDeleteCluster
1846  *      Description: Actually performs the cluster deletion.
1847  *      Arguments: info - all information about this cluster.
1848  *                 one_cluster - If true then there was only one cluster in
1849  *                               the queue, and we should confirm.
1850  *      Returns: none.
1851  */
1852
1853 static void RealDeleteCluster(char **info, Bool one_cluster)
1854 {
1855   int stat;
1856   char temp_buf[BUFSIZ];
1857
1858   sprintf(temp_buf,
1859           "Are you sure the you want to delete the cluster %s (y/n) ?",
1860           info[C_NAME]);
1861   if (!one_cluster || Confirm(temp_buf))
1862     {
1863       if (CheckAndRemoveMachines(info[C_NAME], TRUE) != SUB_ERROR)
1864         {
1865           if ((stat = do_mr_query("delete_cluster", 1,
1866                                   &info[C_NAME], NULL, NULL)))
1867             {
1868               com_err(program_name, stat, " in delete_cluster.");
1869               sprintf(temp_buf, "Cluster %s ** NOT ** deleted.", info[C_NAME]);
1870               Put_message(temp_buf);
1871             }
1872           else
1873             {
1874               sprintf(temp_buf, "cluster %s successfully deleted.",
1875                       info[C_NAME]);
1876               Put_message(temp_buf);
1877             }
1878         }
1879     }
1880 }
1881
1882 /*      Function Name: DeleteCluster
1883  *      Description: This function removes a cluster from the database.
1884  *      Arguments: argc, argv - the name of the cluster is stored in argv[1].
1885  *      Returns: DM_NORMAL.
1886  */
1887
1888 int DeleteCluster(int argc, char **argv)
1889 {
1890   struct mqelem *top;
1891
1892   top = GetMCInfo(CLUSTER, argv[1], NULL);
1893   QueryLoop(top, PrintClusterInfo, RealDeleteCluster, "Delete the cluster");
1894
1895   FreeQueue(top);
1896   return DM_NORMAL;
1897 }
1898
1899 /* ----------- Cluster Data Menu -------------- */
1900
1901 /*      Function Name: ShowClusterData
1902  *      Description: This function shows the services for one cluster.
1903  *      Arguments: argc, argv - The name of the cluster is argv[1].
1904  *                              The label of the data in argv[2].
1905  *      Returns: DM_NORMAL.
1906  */
1907
1908 int ShowClusterData(int argc, char **argv)
1909 {
1910   struct mqelem *elem, *top;
1911   char **info;
1912
1913   top = elem = GetMCInfo(DATA, argv[1], argv[2]);
1914   while (elem)
1915     {
1916       info = elem->q_data;
1917       PrintClusterData(info);
1918       elem = elem->q_forw;
1919     }
1920   FreeQueue(top);
1921   return DM_NORMAL;
1922 }
1923
1924 /*      Function Name: AddClusterData
1925  *      Description: This function adds some data to the cluster.
1926  *      Arguments: argv, argc:   argv[1] - the name of the cluster.
1927  *                               argv[2] - the label of the data.
1928  *                               argv[3] - the data.
1929  *      Returns: DM_NORMAL.
1930  */
1931
1932 int AddClusterData(int argc, char **argv)
1933 {
1934   int stat, i;
1935
1936   for (i = 1; i < 4; i++)
1937     {
1938       if (IS_EMPTY(argv[i]))
1939         {
1940           Put_message("Cluster data cannot be an empty string.");
1941           return DM_NORMAL;
1942         }
1943     }
1944   if ((stat = do_mr_query("add_cluster_data", 3, argv + 1, NULL, NULL)))
1945     com_err(program_name, stat, " in AddClusterData.");
1946   return DM_NORMAL;
1947 }
1948
1949 /*      Function Name: RealRemoveClusterData
1950  *      Description: actually removes the cluster data.
1951  *      Arguments: info - all info necessary to remove the cluster, in an array
1952  *                        of strings.
1953  *                 one_item - if true then the queue has only one elem and we
1954  *                            should confirm.
1955  *      Returns: none.
1956  */
1957
1958 static void RealRemoveClusterData(char **info, Bool one_item)
1959 {
1960   int stat;
1961   char *temp_ptr;
1962
1963   Put_message(" ");
1964   temp_ptr = "Are you sure that you want to remove this cluster data (y/n) ?";
1965   PrintClusterData(info);
1966   if (!one_item || Confirm(temp_ptr))
1967     {
1968       if ((stat = do_mr_query("delete_cluster_data", 3, info, NULL, NULL)))
1969         {
1970           com_err(program_name, stat, " in DeleteClusterData.");
1971           Put_message("Data not removed.");
1972         }
1973       else
1974         Put_message("Removal successful.");
1975     }
1976 }
1977
1978 /*      Function Name: RemoveClusterData
1979  *      Description: This function removes data on a given cluster.
1980  *      Arguments: argv, argc:   argv[1] - the name of the cluster.
1981  *                               argv[2] - the label of the data.
1982  *                               argv[3] - the data.
1983  *      Returns: DM_NORMAL.
1984  */
1985
1986 int RemoveClusterData(int argc, char **argv)
1987 {
1988   struct mqelem *top;
1989
1990   top = GetMCInfo(DATA, argv[1], argv[2]);
1991   QueryLoop(top, PrintClusterData, RealRemoveClusterData,
1992             "Remove data from cluster");
1993
1994   FreeQueue(top);
1995   return DM_NORMAL;
1996 }
1997
1998 /*      Function Name: MachineToClusterMap
1999  *      Description: This Retrieves the mapping between machine and cluster
2000  *      Arguments: argc, argv - argv[1] -> machine name or wildcard.
2001  *                              argv[2] -> cluster name or wildcard.
2002  *      Returns: none.
2003  */
2004
2005 int MachineToClusterMap(int argc, char **argv)
2006 {
2007   struct mqelem *elem, *top;
2008   char *tmpname, temp_buf[256];
2009
2010   tmpname = canonicalize_hostname(strdup(argv[1]));
2011   if (strcasecmp(tmpname, argv[1]) && *argv[1] != '"')
2012     {
2013       sprintf(temp_buf, "Warning: '%s' canonicalized to '%s'.",
2014               argv[1], tmpname);
2015       Put_message(temp_buf);
2016     }
2017   top = elem = GetMCInfo(MAP, tmpname, argv[2]);
2018
2019   Put_message("");              /* blank line on screen */
2020   while (elem)
2021     {
2022       char **info = elem->q_data;
2023       PrintMCMap(info);
2024       elem = elem->q_forw;
2025     }
2026
2027   FreeQueue(top);
2028   free(tmpname);
2029   return DM_NORMAL;
2030 }
2031
2032 /*        Function Name: MachineByOwner
2033  *        Description: This function prints all machines which are owned by 
2034  *                     a given user or group.
2035  *        Arguments: none.
2036  *        Returns: DM_NORMAL.
2037  */
2038
2039 int MachineByOwner(int argc, char **argv)
2040 {
2041   char buf[BUFSIZ], temp_buf[BUFSIZ], *type, *name;
2042   struct mqelem *top;
2043
2044   type = strdup("USER");
2045   if (GetTypeFromUser("Type of owner", "ace_type", &type) == SUB_ERROR)
2046     return DM_NORMAL;
2047
2048   sprintf(buf, "Name of %s", type);
2049   name = strdup(user);
2050   if (GetValueFromUser(buf, &name) == SUB_ERROR)
2051     return DM_NORMAL;
2052
2053   switch (YesNoQuestion("Do you want a recursive search (y/n)", FALSE))
2054     {
2055     case TRUE:
2056       sprintf(temp_buf, "R%s", type);     /* "USER to "RUSER", etc. */
2057       free(type);
2058       type = strdup(temp_buf);
2059       break;
2060     case FALSE:
2061       break;
2062     default:
2063       return DM_NORMAL;
2064     }
2065
2066   top = GetMachineByOwner(type, name);
2067   Loop(top, PrintMachine);
2068
2069   FreeQueue(top);
2070   return DM_NORMAL;
2071 }
2072
2073 /*         Function Name: GetMachineByOwner
2074  *         Description: This function stores information retrieved by 
2075  *                      the get_host_by_owner query
2076  *         Arguments: type - an ace_type, argv[0] for the query
2077  *                    name - name of machine, argv[1] for the query
2078  *         Returns: the top element of a queue returning the data, or NULL.
2079  */
2080
2081 struct mqelem *GetMachineByOwner(char *type, char *name)
2082 {
2083   char *args[2];
2084   struct mqelem *elem = NULL;
2085   int status;
2086
2087   args[0] = type;
2088   args[1] = name;
2089   if ((status = do_mr_query("get_host_by_owner", 2, args, StoreInfo, &elem)))
2090     {
2091       com_err(program_name, status, " in get_host_by_owner");
2092       return NULL;
2093     }
2094   return QueueTop(elem);
2095 }
2096
2097 int MachineByAcctNumber(int argc, char **argv)
2098 {
2099   char *args[1], *account_number;
2100   int status;
2101   struct mqelem *elem = NULL;
2102
2103   account_number = strdup("");
2104   if (GetValueFromUser("Account Number", &account_number) == SUB_ERROR)
2105     return DM_NORMAL;
2106
2107   args[0] = account_number;
2108   if (status = do_mr_query("get_host_by_account_number", 1, args, StoreInfo, 
2109                            &elem))
2110     {
2111       com_err(program_name, status, " in get_host_by_account_number");
2112       return DM_NORMAL;
2113     }
2114   Loop(QueueTop(elem), (void (*)(char **)) PrintMachInfo);
2115   FreeQueue(elem);
2116
2117   return DM_NORMAL;
2118 }
2119
2120 int ShowContainerInfo(int argc, char **argv)
2121 {
2122   struct mqelem *top;
2123
2124   top = GetMCInfo(CONTAINER, argv[1], NULL);
2125   Loop(top, (void (*)(char **)) PrintContainerInfo);
2126   FreeQueue(top);
2127   return DM_NORMAL;
2128 }
2129
2130 static char *PrintContainerInfo(char **info)
2131 {
2132   char buf[BUFSIZ], tbuf[256];
2133   char *args[2];
2134   struct mqelem *elem = NULL;
2135   int stat;
2136
2137   Put_message("");
2138   sprintf(buf, "Container:      %-16s    Public:      %s", info[CON_NAME],
2139           atoi(info[CON_PUBLIC]) ? "Yes" : "No");
2140   Put_message(buf);
2141   /* Do a get_container_list to see what the associated list is. */
2142   args[0] = info[CON_NAME];
2143   if (stat = do_mr_query("get_container_list", 1, args, StoreInfo, &elem))
2144     {
2145       if (stat != MR_NO_MATCH)
2146         com_err(program_name, stat, " looking up container list");
2147     }
2148   else 
2149     {
2150       sprintf(buf, "Container's associated list is: LIST %s",
2151               ((char **)elem->q_data)[1]);
2152       Put_message(buf);
2153       FreeQueue(elem);
2154     }
2155   sprintf(buf, "Description:    %-16s", info[CON_DESCRIPT]);
2156   Put_message(buf);
2157   sprintf(buf, "Location:       %-16s    Contact:     %s", info[CON_LOCATION],
2158           info[CON_CONTACT]);
2159   Put_message(buf);
2160   sprintf(tbuf, "%s %s", info[CON_OWNER_TYPE],
2161           strcmp(info[CON_OWNER_TYPE], "NONE") ? info[CON_OWNER_NAME] : "");
2162   sprintf(buf, "Owner:          %-16s", tbuf);
2163   Put_message(buf);
2164   sprintf(tbuf, "%s %s", info[CON_MEMACE_TYPE],
2165           strcmp(info[CON_MEMACE_TYPE], "NONE") ? info[CON_MEMACE_NAME] : "");
2166   sprintf(buf, "Membership ACL: %-16s", tbuf);
2167   Put_message(buf);
2168   Put_message("");
2169   sprintf(buf, MOD_FORMAT, info[CON_MODBY], info[CON_MODTIME], 
2170           info[CON_MODWITH]);
2171   Put_message(buf);
2172   return(info[CON_NAME]);
2173 }
2174
2175 static char *PrintContainer(char **info)
2176 {
2177   char buf[BUFSIZ];
2178
2179   sprintf(buf, "Container: %s", info[CON_NAME]);
2180   Put_message(buf);
2181   return "";
2182 }
2183
2184 static char *PrintMContMap(char **info)
2185 {
2186   char buf[BUFSIZ];
2187   sprintf(buf, "Container: %-30s Machine: %-20s",
2188           info[1], info[0]);
2189   Put_message(buf);
2190   return "";
2191 }
2192
2193 int AddContainer(int argc, char **argv)
2194 {
2195   char **args, *info[MAX_ARGS_SIZE], *name = argv[1];
2196   int stat;
2197
2198   /* Can't use ValidName() because spaces are allowed in container names */
2199   if (IS_EMPTY(name))
2200     {
2201       Put_message("Please use a non-empty name.");
2202       return DM_NORMAL;
2203     }
2204
2205   if (strchr(name, '*') || strchr(name, '?'))
2206     {
2207       Put_message("Wildcards not accepted here.");
2208       return DM_NORMAL;
2209     }
2210
2211   /* Check if this cluster already exists. */
2212   if ((stat = do_mr_query("get_container", 1, &name, NULL, NULL))
2213       == MR_SUCCESS)
2214     {
2215       Put_message("This container already exists.");
2216       return DM_NORMAL;
2217     }
2218   else if (stat != MR_NO_MATCH)
2219     {
2220       com_err(program_name, stat, " in AddContainer.");
2221       return DM_NORMAL;
2222     }
2223   if (!(args = AskMCDInfo(SetContainerDefaults(info, name), CONTAINER, FALSE)))
2224     {
2225       Put_message("Aborted.");
2226       FreeInfo(info);
2227       return DM_NORMAL;
2228     }
2229
2230   /* Create the new container. */
2231   if ((stat = do_mr_query("add_container", CountArgs(args), args, NULL, NULL)))
2232     com_err(program_name, stat, " in AddContainer.");
2233
2234   FreeInfo(info);
2235   return DM_NORMAL;
2236 }
2237
2238 int UpdateContainer(int argc, char **argv)
2239 {
2240   struct mqelem *top;
2241   top = GetMCInfo(CONTAINER, argv[1], NULL);
2242   QueryLoop(top, NullPrint, RealUpdateContainer, "Update the container");
2243
2244   FreeQueue(top);
2245   return DM_NORMAL;
2246 }
2247
2248 static void RealUpdateContainer(char **info, Bool junk)
2249 {
2250   int stat;
2251   char **args = AskMCDInfo(info, CONTAINER, TRUE);
2252
2253   if (!args)
2254     {
2255       Put_message("Aborted.");
2256       return;
2257     }
2258   if ((stat = do_mr_query("update_container", CountArgs(args), args,
2259                           NULL, NULL)))
2260     com_err(program_name, stat, " in UpdateContainer.");
2261   else
2262     Put_message("Container successfully updated.");
2263 }
2264
2265 int DeleteContainer(int argc, char **argv)
2266 {
2267   struct mqelem *top;
2268
2269   top = GetMCInfo(CONTAINER, argv[1], NULL);
2270   QueryLoop(top, PrintClusterInfo, RealDeleteContainer, 
2271             "Delete the container");
2272
2273   FreeQueue(top);
2274   return DM_NORMAL;
2275 }
2276
2277 static void RealDeleteContainer(char **info, Bool one_container)
2278 {
2279   int stat;
2280   char temp_buf[BUFSIZ];
2281
2282   sprintf(temp_buf,
2283           "Are you sure you want to delete the container %s (y/n) ?",
2284           info[CON_NAME]);
2285   if (!one_container || Confirm(temp_buf))
2286     {
2287       if (CheckAndRemoveMachinesFromContainer(info[CON_NAME], TRUE) 
2288           != SUB_ERROR)
2289         {
2290           if ((stat = do_mr_query("delete_container", 1, &info[CON_NAME],
2291                                   NULL, NULL)))
2292             {
2293               com_err(program_name, stat, " in delete_container.");
2294               sprintf(temp_buf, "Container %s ** NOT ** deleted.", 
2295                       info[CON_NAME]);
2296               Put_message(temp_buf);
2297             }
2298           else
2299             {
2300               sprintf(temp_buf, "Container %s successfully deleted.",
2301                       info[CON_NAME]);
2302               Put_message(temp_buf);
2303             }
2304         }
2305     }
2306 }
2307
2308 int CheckAndRemoveMachinesFromContainer(char *name, Bool ask_first)
2309 {
2310   int stat, ret_value;
2311   Bool delete_it;
2312   char *args[10], temp_buf[BUFSIZ], *ptr;
2313   struct mqelem *top, *elem = NULL;
2314   
2315   ret_value = SUB_NORMAL;
2316   args[0] = name;
2317   args[1] = "0";
2318   stat = do_mr_query("get_machines_of_container", 2, args, StoreInfo, 
2319                      &elem);
2320   if (stat && stat != MR_NO_MATCH)
2321     {
2322       com_err(program_name, stat, " in get_machines_of_container");
2323       return DM_NORMAL;
2324     }
2325   if (stat == MR_SUCCESS)
2326     {
2327       elem = top = QueueTop(elem);
2328       if (ask_first)
2329         {
2330           sprintf(temp_buf,
2331                   "The container %s has the following machines in it:", name);
2332           Put_message(temp_buf);
2333           while (elem)
2334             {
2335               char **info = elem->q_data;
2336               Print(1, &info[0], (char *) NULL);
2337               elem = elem->q_forw;
2338             }
2339           ptr = "Remove ** ALL ** these machines from this container?";
2340
2341           if (YesNoQuestion(ptr, FALSE) == TRUE) /* may return -1. */
2342             delete_it = TRUE;
2343           else
2344             {
2345               Put_message("Aborting...");
2346               FreeQueue(top);
2347               return SUB_ERROR;
2348             }
2349         }
2350       else
2351         delete_it = TRUE;
2352
2353       if (delete_it)
2354         {
2355           elem = top;
2356           while (elem)
2357             {
2358               char **info = elem->q_data;
2359               if ((stat = do_mr_query("delete_machine_from_container",
2360                                       2, info, NULL, NULL)))
2361                 {
2362                   ret_value = SUB_ERROR;
2363                   com_err(program_name, stat, 
2364                           " in delete_machine_from_container.");
2365                   sprintf(temp_buf, 
2366                           "Machine %s ** NOT ** removed from container %s.",
2367                           info[0], info[1]);
2368                   Put_message(temp_buf);
2369                 }
2370               elem = elem->q_forw;
2371             }
2372         }
2373     }
2374   return ret_value;
2375 }
2376
2377 int GetSubContainers(int argc, char **argv)
2378 {
2379   char *args[2];
2380   struct mqelem *elem = NULL, *top = NULL;
2381   int stat;
2382
2383   args[0] = argv[1];
2384
2385   if (YesNoQuestion("Do you want a recursive search?", TRUE) == TRUE)
2386     args[1] = "1";
2387   else
2388     args[1] = "0";
2389
2390   if (stat = do_mr_query("get_subcontainers_of_container", 2, args,
2391                          StoreInfo, &elem))
2392     com_err(program_name, stat, " in get_subcontainers_of_container");
2393
2394   top = QueueTop(elem);
2395   Loop(top, ((void (*)(char **)) PrintContainer));
2396   FreeQueue(top);
2397   return DM_NORMAL;
2398 }
2399
2400 int MachineToContainerMap(int argc, char **argv)
2401 {
2402   struct mqelem *elem, *top;
2403   char *tmpname, temp_buf[256];
2404
2405   tmpname = canonicalize_hostname(strdup(argv[1]));
2406   if (strcasecmp(tmpname, argv[1]) && *argv[1] != '"')
2407     {
2408       sprintf(temp_buf, "Warning: '%s' canonicalized to '%s'.",
2409               argv[1], tmpname);
2410       Put_message(temp_buf);
2411     }
2412   top = elem = GetMCInfo(CONTMAP, tmpname, NULL);
2413
2414   Put_message("");
2415   while (elem)
2416     {
2417       char **info = elem->q_data;
2418       PrintMContMap(info);
2419       elem = elem->q_forw;
2420     }
2421
2422   FreeQueue(top);
2423   free(tmpname);
2424   return DM_NORMAL;
2425 }
2426
2427 int AddMachineToContainer(int argc, char **argv)
2428 {
2429   int stat;
2430   char *machine, *container, temp_buf[BUFSIZ], *args[10];
2431   Bool add_it, one_machine, one_container;
2432   struct mqelem *melem, *mtop, *celem, *ctop;
2433
2434   machine = canonicalize_hostname(strdup(argv[1]));
2435   if (strcasecmp(machine, argv[1]) && *argv[1] != '"')
2436     {
2437       sprintf(temp_buf, "Warning: '%s' canonicalized to '%s'.",
2438               argv[1], machine);
2439       Put_message(temp_buf);
2440     }
2441   container = argv[2];
2442
2443   celem = ctop = GetMCInfo(CONTAINER, container, NULL);
2444   melem = mtop = GetMCInfo(MACHINE, machine, NULL);
2445   free(machine);
2446
2447   one_machine = (QueueCount(mtop) == 1);
2448   one_container = (QueueCount(ctop) == 1);
2449
2450   while (melem)
2451     {
2452       char **minfo = melem->q_data;
2453       while (celem)
2454         {
2455           char **cinfo = celem->q_data;
2456           if (one_machine && one_container)
2457             add_it = TRUE;
2458           else
2459             {
2460               sprintf(temp_buf, "Add machine %s to container %s (y/n/q) ?",
2461                       minfo[M_NAME], cinfo[CON_NAME]);
2462               switch (YesNoQuestion(temp_buf, FALSE))
2463                 {
2464                 case TRUE:
2465                   add_it = TRUE;
2466                   break;
2467                 case FALSE:
2468                   add_it = FALSE;
2469                   break;
2470             default:
2471               Put_message("Aborting...");
2472               FreeQueue(ctop);
2473               FreeQueue(mtop);
2474               return DM_NORMAL;
2475                 }
2476             }
2477           if (add_it)
2478             {
2479               args[0] = minfo[M_NAME];
2480               args[1] = cinfo[CON_NAME];
2481               stat = do_mr_query("add_machine_to_container", 2, args, NULL, 
2482                                  NULL);
2483               switch (stat)
2484                 {
2485                 case MR_SUCCESS:
2486                   break;
2487                 case MR_EXISTS:
2488                   sprintf(temp_buf, "%s is already in container %s",
2489                           minfo[M_NAME], cinfo[CON_NAME]);
2490                   Put_message(temp_buf);
2491                   break;
2492                 default:
2493                   com_err(program_name, stat, " in AddMachineToContainer.");
2494                   break;
2495                 }
2496             }
2497           celem = celem->q_forw;
2498         }
2499       celem = ctop;
2500       melem = melem->q_forw;
2501     }
2502   FreeQueue(ctop);
2503   FreeQueue(mtop);
2504   return DM_NORMAL;
2505 }
2506
2507 int RemoveMachineFromContainer(int argc, char **argv)
2508 {
2509   struct mqelem *elem = NULL;
2510   char buf[BUFSIZ], *args[10];
2511   int stat;
2512
2513   args[0] = canonicalize_hostname(strdup(argv[1]));
2514   if (strcasecmp(args[0], argv[1]) && *argv[1] != '"')
2515     {
2516       sprintf(buf, "Warning: '%s' canonicalized to '%s'.",
2517               argv[1], args[0]);
2518       Put_message(buf);
2519     }
2520   args[1] = argv[2];
2521   args[2] = NULL;
2522
2523   stat = do_mr_query("get_machine_to_container_map", 1, args, StoreInfo, 
2524                      &elem);
2525   if (stat == MR_NO_MATCH)
2526     {
2527       sprintf(buf, "The machine %s is not in the container %s.",
2528               args[0], args[1]);
2529       Put_message(buf);
2530       free(args[0]);
2531       return DM_NORMAL;
2532     }
2533   if (stat != MR_SUCCESS)
2534     com_err(program_name, stat, " in deleter_machine_from_container");
2535
2536   elem = QueueTop(elem);
2537   QueryLoop(elem, PrintMContMap, RealRemoveMachineFromContainer, 
2538             "Remove this machine from this container");
2539   
2540   FreeQueue(elem);
2541   free(args[0]);
2542   return DM_NORMAL;
2543 }
2544
2545 static void RealRemoveMachineFromContainer(char **info, Bool one_contmap)
2546 {
2547   char temp_buf[BUFSIZ];
2548   int stat;
2549
2550   sprintf(temp_buf, "Remove %s from the container %s",
2551           info[0], info[1]);
2552   if (!one_contmap || Confirm(temp_buf))
2553     {
2554       if ((stat = do_mr_query("delete_machine_from_container", 2,
2555                               info, NULL, NULL)))
2556         com_err(program_name, stat, " in delete_machine_from_container");
2557       else
2558         {
2559           sprintf(temp_buf, "%s has been removed from the container %s.",
2560                   info[0], info[1]);
2561           Put_message(temp_buf);
2562         }
2563     }
2564   else
2565     Put_message("Machine not removed.");
2566 }
2567
2568 int GetMachinesOfContainer(int argc, char **argv)
2569 {
2570   char *args[2];
2571   struct mqelem *elem = NULL, *top = NULL;
2572   int stat;
2573
2574   args[0] = argv[1];
2575
2576   if (YesNoQuestion("Do you want a recursive search?", TRUE) == TRUE)
2577     args[1] = "1";
2578   else
2579     args[1] = "0";
2580
2581   if (stat = do_mr_query("get_machines_of_container", 2, args,
2582                          StoreInfo, &elem))
2583     com_err(program_name, stat, " in get_machines_of_container");
2584
2585   top = QueueTop(elem);
2586   Loop(top, ((void (*)(char **)) PrintMContMap));
2587   FreeQueue(top);
2588   return DM_NORMAL;
2589 }
2590
2591 int GetTopLevelCont(int argc, char **argv)
2592 {
2593   int status;
2594   struct mqelem *elem = NULL;
2595   if (status = do_mr_query("get_toplevel_containers", 0, NULL, StoreInfo, 
2596                            &elem))
2597     {
2598       com_err(program_name, status, " in get_toplevel_containers");
2599       return DM_NORMAL;
2600     }
2601   Loop(QueueTop(elem), (void(*)(char **)) PrintContainer);
2602   FreeQueue(elem);
2603   return DM_NORMAL;
2604 }
This page took 0.413294 seconds and 3 git commands to generate.