]> andersk Git - moira.git/blob - clients/moira/cluster.c
Support setting/displaying publicflg for containers, and displaying
[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
566   switch (type)
567     {
568     case MACHINE:
569       sprintf(temp_buf, "\nSetting the information for the Machine %s...",
570               info[M_NAME]);
571       break;
572     case SUBNET:
573       sprintf(temp_buf, "Setting the information for the Network %s...",
574               info[SN_NAME]);
575       break;
576     case CLUSTER:
577       sprintf(temp_buf, "Setting the information for the Cluster %s...",
578               info[C_NAME]);
579       break;
580     case DATA:
581       sprintf(temp_buf, "Setting the Data for the Cluster %s...",
582               info[CD_NAME]);
583       break;
584     case CONTAINER:
585       sprintf(temp_buf, "Setting the Data for the Container %s...",
586               info[CON_NAME]);
587       break;
588     }
589   Put_message(temp_buf);
590
591   if (name)
592     {
593       switch (type)
594         {
595         case MACHINE:
596           newname = strdup(info[M_NAME]);
597           if (GetValueFromUser("The new name for this machine? ", &newname) ==
598               SUB_ERROR)
599             return NULL;
600           oldnewname = strdup(newname);
601           newname = canonicalize_hostname(newname);
602           if (strcasecmp(newname, oldnewname) && *oldnewname != '"')
603             {
604               sprintf(temp_buf, "Warning: '%s' canonicalized to '%s'\n",
605                       oldnewname, newname);
606               Put_message(temp_buf);
607             }
608           free(oldnewname);
609           break;
610         case SUBNET:
611           newname = strdup(info[SN_NAME]);
612           if (GetValueFromUser("The new name for this network? ", &newname) ==
613               SUB_ERROR)
614             return NULL;
615           break;
616         case CLUSTER:
617           newname = strdup(info[C_NAME]);
618           if (GetValueFromUser("The new name for this cluster? ", &newname) ==
619               SUB_ERROR)
620             return NULL;
621           break;
622         case CONTAINER:
623           newname = strdup(info[CON_NAME]);
624           if (GetValueFromUser("The new name for this container? ", &newname)
625               == SUB_ERROR)
626             return NULL;
627           break;
628         default:
629           Put_message("Unknown type in AskMCDInfo, programmer botch");
630           return NULL;
631         }
632     }
633
634   switch (type)
635     {
636     case MACHINE:
637       if (GetValueFromUser("Machine's vendor", &info[M_VENDOR]) == SUB_ERROR)
638         return NULL;
639       if (GetValueFromUser("Machine's model", &info[M_MODEL]) == SUB_ERROR)
640         return NULL;
641       if (GetValueFromUser("Machine's operating system", &info[M_OS]) ==
642           SUB_ERROR)
643         return NULL;
644       if (GetValueFromUser("Machine's location", &info[M_LOC]) == SUB_ERROR)
645         return NULL;
646       if (GetValueFromUser("Machine's contact", &info[M_CONTACT]) ==
647           SUB_ERROR)
648         return NULL;
649       if (GetValueFromUser("Machine's billing contact", 
650                            &info[M_BILL_CONTACT]) == SUB_ERROR)
651         return NULL;
652       if (GetValueFromUser("Machine's billing account number",
653                            &info[M_ACCT_NUMBER]) == SUB_ERROR)
654         return NULL;
655       while (1)
656         {
657           int i;
658           if (GetValueFromUser("Machine's status (? for help)",
659                                  &info[M_STAT]) == SUB_ERROR)
660             return NULL;
661           if (isdigit(info[M_STAT][0]))
662             break;
663           Put_message("Valid status numbers:");
664           for (i = 0; i < 4; i++)
665             Put_message(states[i]);
666         }
667
668       /* there appears to be some argument mismatch between the client
669        * and the server.. so here is this argument shuffler.
670        * I have since modified this to always shuffle the arguments..
671        * not just do so when performing a modify all fields request.
672        * The SetMachinedefaults() has been changed to reflect this.
673        * pray for us and may we attain enlightenment through structures.
674        */
675
676       if (name)
677         {
678           /* info did not come from SetMachineDefaults(), which does not
679            * initialize entry 10 (M_STAT_CHNG), therefore we can
680            * free it.
681            */
682           /* This is an update of an existing machine and the structure
683            * was filled in thru a query to the db which does fill in this
684            * field.
685            */
686           free(info[10]);
687         }
688
689       info[10] = info[M_SUBNET];
690       info[11] = info[M_ADDR];
691       info[12] = info[M_OWNER_TYPE];
692       info[13] = info[M_OWNER_NAME];
693       info[14] = info[M_ACOMMENT];
694       info[15] = info[M_OCOMMENT];
695
696       if (name)
697         {
698           if (GetValueFromUser("Machine's network (or 'none')", &info[10])
699               == SUB_ERROR)
700             return NULL;
701         }
702       if (GetValueFromUser("Machine's address (or 'unassigned' or 'unique')",
703                            &info[11]) == SUB_ERROR)
704         return NULL;
705       if (GetTypeFromUser("Machine's owner type", "ace_type", &info[12]) ==
706           SUB_ERROR)
707         return NULL;
708       if (strcmp(info[12], "NONE") &&
709           GetValueFromUser("Owner's Name", &info[13]) == SUB_ERROR)
710         return NULL;
711       if (!strcmp(info[12], "KERBEROS"))
712           {
713             char *canon;
714
715             mrcl_validate_kerberos_member(info[13], &canon);
716             if (mrcl_get_message())
717               Put_message(mrcl_get_message());
718             free(info[13]);
719             info[13] = canon;
720           }
721       if (GetValueFromUser("Administrative comment", &info[14]) == SUB_ERROR)
722         return NULL;
723       if (GetValueFromUser("Operational comment", &info[15]) == SUB_ERROR)
724         return NULL;
725       info[16] = NULL;
726       FreeAndClear(&info[17], TRUE);
727       FreeAndClear(&info[18], TRUE);
728       break;
729     case SUBNET:
730       if (GetValueFromUser("Network description", &info[SN_DESC]) == SUB_ERROR)
731         return NULL;
732       while (1)
733         {
734           int i;
735           if (GetValueFromUser("Network's status (? for help)",
736                                &info[SN_STATUS]) == SUB_ERROR)
737             return NULL;
738           if (isdigit(info[SN_STATUS][0]))
739             break;
740           Put_message("Valid status numbers:");
741           for (i = 0; i < 9; i++)
742             Put_message(subnet_states[i]);
743         }
744       if (GetValueFromUser("Network's contact", &info[SN_CONTACT]) == SUB_ERROR)
745         return NULL;
746       if (GetValueFromUser("Network's billing account number", 
747                            &info[SN_ACCT_NUMBER]) == SUB_ERROR)
748         return NULL;
749       if (GetAddressFromUser("Network address", &info[SN_ADDRESS]) == SUB_ERROR)
750         return NULL;
751       if (GetAddressFromUser("Network mask", &info[SN_MASK]) == SUB_ERROR)
752         return NULL;
753       if (atoi(info[SN_LOW]) == (int)ntohl(inet_addr(S_DEFAULT_LOW)))
754         {
755           struct in_addr low;
756           unsigned long mask, addr;
757
758           addr = atoi(info[SN_ADDRESS]);
759           mask = atoi(info[SN_MASK]);
760           low.s_addr = atoi(info[SN_LOW]);
761           low.s_addr = (low.s_addr & ~mask) | (addr & mask);
762           free(info[SN_LOW]);
763           sprintf(temp_buf, "%ld", low.s_addr);
764           info[SN_LOW] = strdup(temp_buf);
765         }
766       if (GetAddressFromUser("Lowest assignable address", &info[SN_LOW]) ==
767           SUB_ERROR)
768         return NULL;
769       if (atoi(info[SN_HIGH]) == (int)ntohl(inet_addr(S_DEFAULT_HIGH)))
770         {
771           struct in_addr high;
772           unsigned long mask, addr;
773
774           addr = atoi(info[SN_ADDRESS]);
775           mask = atoi(info[SN_MASK]);
776           high.s_addr = atoi(info[SN_HIGH]);
777           high.s_addr = (high.s_addr & ~mask) | (addr & mask);
778           free(info[SN_HIGH]);
779           sprintf(temp_buf, "%ld", high.s_addr);
780           info[SN_HIGH] = strdup(temp_buf);
781         }
782       if (GetAddressFromUser("Highest assignable address", &info[SN_HIGH]) ==
783           SUB_ERROR)
784         return NULL;
785       if (GetValueFromUser("Hostname prefix", &info[SN_PREFIX]) == SUB_ERROR)
786         return NULL;
787       if (GetTypeFromUser("Owner type", "ace_type", &info[SN_ACE_TYPE]) ==
788           SUB_ERROR)
789         return NULL;
790       if (strcmp(info[SN_ACE_TYPE], "NONE") &&
791           GetValueFromUser("Owner name", &info[SN_ACE_NAME]) == SUB_ERROR)
792         return NULL;
793       if (!strcmp(info[SN_ACE_TYPE], "KERBEROS"))
794           {
795             char *canon;
796
797             mrcl_validate_kerberos_member(info[SN_ACE_NAME], &canon);
798             if (mrcl_get_message())
799               Put_message(mrcl_get_message());
800             free(info[SN_ACE_NAME]);
801             info[SN_ACE_NAME] = canon;
802           }
803       FreeAndClear(&info[SN_MODTIME], TRUE);
804       FreeAndClear(&info[SN_MODBY], TRUE);
805       FreeAndClear(&info[SN_MODWITH], TRUE);
806       break;
807     case CLUSTER:
808       if (GetValueFromUser("Cluster's Description:", &info[C_DESCRIPT]) ==
809           SUB_ERROR)
810         return NULL;
811       if (GetValueFromUser("Cluster's Location:", &info[C_LOCATION]) ==
812           SUB_ERROR)
813         return NULL;
814       FreeAndClear(&info[C_MODTIME], TRUE);
815       FreeAndClear(&info[C_MODBY], TRUE);
816       FreeAndClear(&info[C_MODWITH], TRUE);
817       break;
818     case DATA:
819       if (GetValueFromUser("Label defining this data?", &info[CD_LABEL]) ==
820           SUB_ERROR)
821         return NULL;
822       if (GetValueFromUser("The data itself ? ", &info[CD_DATA]) == SUB_ERROR)
823         return NULL;
824       break;
825     case CONTAINER:
826       if (GetYesNoValueFromUser("Is this a public container", 
827                                 &info[CON_PUBLIC]) == SUB_ERROR)
828         return NULL;
829       if (GetValueFromUser("Container's Description:", &info[CON_DESCRIPT]) ==
830           SUB_ERROR)
831         return NULL;
832       if (GetValueFromUser("Container's Location:", &info[CON_LOCATION]) ==
833           SUB_ERROR)
834         return NULL;
835       if (GetValueFromUser("Container's Contact:", &info[CON_CONTACT]) == 
836           SUB_ERROR)
837         return NULL;
838       if (GetTypeFromUser("Container's owner type", "ace_type", 
839                           &info[CON_OWNER_TYPE]) == SUB_ERROR)
840         return NULL;
841       if (strcmp(info[CON_OWNER_TYPE], "NONE") &&
842           GetValueFromUser("Owner's Name", &info[CON_OWNER_NAME]) == SUB_ERROR)
843         return NULL;
844       if (!strcmp(info[CON_OWNER_TYPE], "KERBEROS"))
845         {
846           char *canon;
847
848           mrcl_validate_kerberos_member(info[CON_OWNER_NAME], &canon);
849           if (mrcl_get_message())
850             Put_message(mrcl_get_message());
851           free(info[CON_OWNER_NAME]);
852           info[CON_OWNER_NAME] = canon;
853         }
854       if (GetTypeFromUser("Container's Membership ACL", "ace_type",
855                           &info[CON_MEMACE_TYPE]) == SUB_ERROR)
856         return NULL;
857       if (strcmp(info[CON_MEMACE_TYPE], "NONE") &&
858           GetValueFromUser("Membership ACL", &info[CON_MEMACE_NAME])
859           == SUB_ERROR)
860         return NULL;
861       if (!strcmp(info[CON_MEMACE_TYPE], "KERBEROS"))
862         {
863           char *canon;
864
865           mrcl_validate_kerberos_member(info[CON_MEMACE_NAME], &canon);
866           if (mrcl_get_message())
867             Put_message(mrcl_get_message());
868           free(info[CON_MEMACE_NAME]);
869           info[CON_MEMACE_NAME] = canon;
870         }
871       FreeAndClear(&info[CON_MODTIME], TRUE);
872       FreeAndClear(&info[CON_MODBY], TRUE);
873       FreeAndClear(&info[CON_MODWITH], TRUE);
874       break;
875     }
876
877   /*
878    * Slide the newname into the #2 slot, this screws up all future references
879    * to this list.
880    */
881   if (name)
882     SlipInNewName(info, newname);
883
884   return info;
885 }
886
887 /* -----------  Machine Menu ----------- */
888
889 /*      Function Name: ShowMachineInfo
890  *      Description: This function shows the information about a machine.
891  *      Arguments: argc, argv - the name of the machine in argv[1].
892  *      Returns: DM_NORMAL.
893  */
894
895 int ShowMachineInfo(int argc, char **argv)
896 {
897   struct mqelem *top;
898   char *tmpname;
899
900   tmpname = canonicalize_hostname(strdup(argv[1]));
901   top = GetMCInfo(MACHINE, tmpname, NULL);
902   Loop(top, ((void (*)(char **)) PrintMachInfo));
903   FreeQueue(top);
904   return DM_NORMAL;
905 }
906
907 /*      Function Name: ShowMachineQuery
908  *      Description: This function shows the information about a machine.
909  *              or group of machines, which may be selected through a
910  *              number of criteria.
911  *      Arguments: argc, argv - the name of the machine in argv[1],
912  *              the address of the machine in argv[2],
913  *              the location of the machine in argv[3],
914  *              and the contact name in argv[4].
915  *           any of these may be wildcards.
916  *      Returns: DM_NORMAL.
917  */
918
919 int ShowMachineQuery(int argc, char **argv)
920 {
921   int stat;
922   struct mqelem *top, *elem = NULL;
923   char *args[5];
924
925   if (!strcmp(argv[1], "") && !strcmp(argv[2], "") &&
926       !strcmp(argv[3], "") && !strcmp(argv[4], ""))
927     {
928       Put_message("You must specify at least one parameter of the query.");
929       return DM_NORMAL;
930     }
931
932   if (*argv[1])
933     args[0] = canonicalize_hostname(strdup(argv[1]));
934   else
935     args[0] = "*";
936   if (*argv[2])
937     args[1] = argv[2];
938   else
939     args[1] = "*";
940   if (*argv[3])
941     args[2] = argv[3];
942   else
943     args[2] = "*";
944   if (*argv[4])
945     args[3] = argv[4];
946   else
947     args[3] = "*";
948
949   if ((stat = do_mr_query("get_host", 4, args, StoreInfo, &elem)))
950     {
951       if (stat == MR_NO_MATCH)
952         Put_message("No machine(s) found matching query in the database.");
953       else
954         com_err(program_name, stat, " in get_machine.");
955       return DM_NORMAL;
956     }
957   top = QueueTop(elem);
958   Loop(top, ((void (*)(char **)) PrintMachInfo));
959   FreeQueue(top);
960   return DM_NORMAL;
961 }
962
963 /*      Function Name: AddMachine
964  *      Description: This function adds a new machine to the database.
965  *      Arguments: argc, argv - the name of the network in argv[1].
966  *      Returns: DM_NORMAL.
967  */
968
969 int AddMachine(int argc, char **argv)
970 {
971   char **args, *info[MAX_ARGS_SIZE], *name, buf[256], *xargs[5];
972   char **rinfo;
973   struct mqelem *elem = NULL;
974   int stat;
975
976   if (!ValidName(argv[1]))      /* Checks for wildcards. */
977     return DM_NORMAL;
978
979   /*
980    * get the network record
981    */
982
983   if (strcasecmp(argv[1], "none") &&
984       (stat = do_mr_query("get_subnet", 1, &argv[1], StoreInfo, &elem)))
985     {
986       if (stat == MR_NO_MATCH)
987         {
988           char buf[128];
989           sprintf(buf, "Network '%s' is not in the database.", argv[1]);
990           Put_message(buf);
991         } else
992           com_err(program_name, stat, " in get_subnet.");
993       return DM_NORMAL;
994     }
995
996   /*
997    * Check to see if this machine already exists.
998    */
999
1000   name = strdup(""); /* want to put prefix here */
1001   if (GetValueFromUser("Machine name", &name) == SUB_ERROR)
1002     return 0;
1003
1004   name = canonicalize_hostname(strdup(name));
1005
1006   xargs[0] = name;
1007   xargs[1] = xargs[2] = xargs[3] = "*";
1008   if (!(stat = do_mr_query("get_host", 4, xargs, NULL, NULL)))
1009     {
1010       sprintf(buf, "The machine '%s' already exists.", name);
1011       Put_message(buf);
1012       free(name);
1013       return DM_NORMAL;
1014     }
1015   else if (stat != MR_NO_MATCH)
1016     {
1017       com_err(program_name, stat,
1018               " while checking machine '%s' in AddMachine.", name);
1019       free(name);
1020       return DM_NORMAL;
1021     }
1022   rinfo = SetMachineDefaults(info, name);
1023   rinfo[M_SUBNET] = strdup(argv[1]);
1024   if (!(args = AskMCDInfo(rinfo, MACHINE, FALSE)))
1025     {
1026       Put_message("Aborted.");
1027       return DM_NORMAL;
1028     }
1029
1030   /*
1031    * Actually create the new Machine.
1032    */
1033
1034   if ((stat = do_mr_query("add_host", CountArgs(args), args, NULL, NULL)))
1035     com_err(program_name, stat, " in AddMachine.");
1036
1037   FreeInfo(info);
1038   free(name);
1039   return DM_NORMAL;
1040 }
1041
1042 /*      Function Name: RealUpdateMachine
1043  *      Description: Performs the actual update of the machine data.
1044  *      Arguments: info - the information on the machine to update.
1045  *                 junk - an UNUSED Boolean.
1046  *      Returns: none.
1047  */
1048
1049 static void RealUpdateMachine(char **info, Bool junk)
1050 {
1051   int stat;
1052   char **args = AskMCDInfo(info, MACHINE, TRUE);
1053   if (!args)
1054     {
1055       Put_message("Aborted.");
1056       return;
1057     }
1058   if ((stat = do_mr_query("update_host", CountArgs(args), args, NULL, NULL)))
1059     com_err(program_name, stat, " in UpdateMachine.");
1060   else
1061     Put_message("Machine successfully updated.");
1062 }
1063
1064 /*      Function Name: UpdateMachine
1065  *      Description: This function adds a new machine to the database.
1066  *      Arguments: argc, argv - the name of the machine in argv[1].
1067  *      Returns: DM_NORMAL.
1068  */
1069
1070 int UpdateMachine(int argc, char **argv)
1071 {
1072   struct mqelem *top;
1073   char *tmpname;
1074
1075   tmpname = canonicalize_hostname(strdup(argv[1]));
1076   top = GetMCInfo(MACHINE, tmpname, NULL);
1077   QueryLoop(top, NullPrint, RealUpdateMachine, "Update the machine");
1078
1079   FreeQueue(top);
1080   free(tmpname);
1081   return DM_NORMAL;
1082 }
1083
1084 /*      Function Name: CheckAndRemoveFromCluster
1085  *      Description: This func tests to see if a machine is in a cluster.
1086  *                   and if so then removes it
1087  *      Arguments: name - name of the machine (already Canonicalized).
1088  *                 ask_user- query the user before removing if from clusters?
1089  *      Returns: MR_ERROR if machine left in a cluster, or mr_error.
1090  */
1091
1092 int CheckAndRemoveFromCluster(char *name, Bool ask_user)
1093 {
1094   int stat, ret_value;
1095   Bool delete_it;
1096   char *args[10], temp_buf[BUFSIZ], *ptr;
1097   struct mqelem *top, *elem = NULL;
1098
1099   ret_value = SUB_NORMAL;       /* initialize ret_value. */
1100   args[0] = name;
1101   args[1] = "*";
1102   stat = do_mr_query("get_machine_to_cluster_map", 2, args, StoreInfo, &elem);
1103   if (stat && stat != MR_NO_MATCH)
1104     {
1105       com_err(program_name, stat, " in get_machine_to_cluster_map.");
1106       return DM_NORMAL;
1107     }
1108   if (stat == MR_SUCCESS)
1109     {
1110       elem = top = QueueTop(elem);
1111       if (ask_user)
1112         {
1113           sprintf(temp_buf, "%s is assigned to the following clusters.", name);
1114           Put_message(temp_buf);
1115           Loop(top, (void (*)(char **)) PrintMCMap);
1116           ptr = "Remove this machine from ** ALL ** these clusters?";
1117           if (YesNoQuestion(ptr, FALSE) == TRUE) /* may return -1. */
1118             delete_it = TRUE;
1119           else
1120             {
1121               Put_message("Aborting...");
1122               FreeQueue(top);
1123               return SUB_ERROR;
1124             }
1125         }
1126       else
1127         delete_it = TRUE;
1128
1129       if (delete_it)
1130         {
1131           while (elem)
1132             {
1133               char **info = elem->q_data;
1134               if ((stat = do_mr_query("delete_machine_from_cluster",
1135                                        2, info, NULL, NULL)))
1136                 {
1137                   ret_value = SUB_ERROR;
1138                   com_err(program_name, stat,
1139                           " in delete_machine_from_cluster.");
1140                   sprintf(temp_buf,
1141                           "Machine %s ** NOT ** removed from cluster %s.",
1142                           info[MAP_MACHINE], info[MAP_CLUSTER]);
1143                   Put_message(temp_buf);
1144                 }
1145               elem = elem->q_forw;
1146             }
1147         }
1148     }
1149   return ret_value;
1150 }
1151
1152 /*      Function Name: CheckAndRemoveCnames
1153  *      Description: This func tests to see if a machine has cnames, 
1154  *                   and if so then removes them.
1155  *      Arguments: name - name of the machine (already Canonicalized).
1156  *                 ask_user- query the user before removing cnames?
1157  *      Returns: MR_ERROR if machine left with a cname, or mr_error.
1158  */
1159
1160 int CheckAndRemoveCnames(char *name, Bool ask_user)
1161 {
1162   int stat, ret_value;
1163   Bool delete_it;
1164   char *args[10], temp_buf[BUFSIZ], *ptr;
1165   struct mqelem *top, *elem = NULL;
1166
1167   ret_value = SUB_NORMAL;
1168   args[0] = "*";
1169   args[1] = name;
1170   stat = do_mr_query("get_hostalias", 2, args, StoreInfo, &elem);
1171   if (stat && stat != MR_NO_MATCH)
1172     {
1173       com_err(program_name, stat, " in get_hostalias.");
1174       return DM_NORMAL;
1175     }
1176   if (stat == MR_SUCCESS)
1177     {
1178       elem = top = QueueTop(elem);
1179       if (ask_user)
1180         {
1181           sprintf(temp_buf, "%s has the following cnames.", name);
1182           Put_message(temp_buf);
1183           Loop(top, (void (*)(char **)) PrintCname);
1184           ptr = "Remove ** ALL ** these cnames?";
1185           if (YesNoQuestion(ptr, FALSE) == TRUE) /* may return -1. */
1186             delete_it = TRUE;
1187           else
1188             {
1189               Put_message("Aborting...");
1190               FreeQueue(top);
1191               return SUB_ERROR;
1192             }
1193         }
1194       else
1195         delete_it = TRUE;
1196
1197       if (delete_it)
1198         {
1199           while (elem)
1200             {
1201               char **info = elem->q_data;
1202               if ((stat = do_mr_query("delete_hostalias", 2, info, 
1203                                       NULL, NULL)))
1204                 {
1205                   ret_value = SUB_ERROR;
1206                   com_err(program_name, stat, " in delete_hostalias.");
1207                   sprintf(temp_buf, 
1208                           "Cname %s ** NOT ** removed from host %s.",
1209                           info[0], info[1]);
1210                   Put_message(temp_buf);
1211                 }
1212               elem = elem->q_forw;
1213             }
1214         }
1215     }
1216   return ret_value;
1217 }
1218
1219 /*      Function Name: RealDeleteMachine
1220  *      Description: Actually Deletes the Machine.
1221  *      Arguments: info - nescessary information stored as an array of char *'s
1222  *                 one_machine - a boolean, true if there is only one item in
1223  *                               the query.
1224  *      Returns: none.
1225  */
1226
1227 static void RealDeleteMachine(char **info, Bool one_machine)
1228 {
1229   int stat;
1230   char temp_buf[BUFSIZ];
1231
1232   sprintf(temp_buf, "Are you sure you want to delete the machine %s (y/n)? ",
1233           info[M_NAME]);
1234   if (!one_machine || Confirm(temp_buf))
1235     {
1236       if (CheckAndRemoveFromCluster(info[M_NAME], TRUE) != SUB_ERROR)
1237         {
1238           if (CheckAndRemoveCnames(info[M_NAME], TRUE) != SUB_ERROR)
1239             {
1240               if ((stat = do_mr_query("delete_host", 1,
1241                                       &info[M_NAME], NULL, NULL)))
1242                 {
1243                   com_err(program_name, stat, " in DeleteMachine.");
1244                   sprintf(temp_buf, "%s ** NOT ** deleted.",
1245                           info[M_NAME]);
1246                   Put_message(temp_buf);
1247                 }
1248               else
1249                 {
1250                   sprintf(temp_buf, "%s successfully Deleted.", info[M_NAME]);
1251                   Put_message(temp_buf);
1252                 }
1253             }
1254         }
1255     }
1256 }
1257
1258 /*      Function Name: DeleteMachine
1259  *      Description: This function removes a machine from the data base.
1260  *      Arguments: argc, argv - the machines name int argv[1].
1261  *      Returns: DM_NORMAL.
1262  */
1263
1264 /* Perhaps we should remove the cluster if it has no machine now. */
1265
1266 int DeleteMachine(int argc, char **argv)
1267 {
1268   struct mqelem *top;
1269   char *tmpname;
1270
1271   tmpname = canonicalize_hostname(strdup(argv[1]));
1272   top = GetMCInfo(MACHINE, tmpname, (char *) NULL);
1273   QueryLoop(top, PrintMachInfo, RealDeleteMachine, "Delete the machine");
1274   FreeQueue(top);
1275   free(tmpname);
1276   return DM_NORMAL;
1277 }
1278
1279
1280 /*      Function Name: ShowCname
1281  *      Description: This function shows machine aliases
1282  *      Arguments: argc, argv - the alias argv[1], the real name in argv[2]
1283  *      Returns: DM_NORMAL.
1284  */
1285
1286 int ShowCname(int argc, char **argv)
1287 {
1288   struct mqelem *top;
1289   char *tmpalias, *tmpname;
1290
1291   tmpalias = partial_canonicalize_hostname(strdup(argv[1]));
1292   tmpname = canonicalize_hostname(strdup(argv[2]));
1293   top = GetMCInfo(CNAME, tmpalias, tmpname);
1294   Put_message("");              /* blank line on screen */
1295   Loop(top, ((void (*)(char **)) PrintCname));
1296   FreeQueue(top);
1297   return DM_NORMAL;
1298 }
1299
1300
1301 int AddCname(int argc, char **argv)
1302 {
1303   int stat;
1304   char *args[10];
1305
1306   args[0] = partial_canonicalize_hostname(strdup(argv[1]));
1307   args[1] = canonicalize_hostname(strdup(argv[2]));
1308   stat = do_mr_query("add_hostalias", 2, args, NULL, NULL);
1309   switch (stat)
1310     {
1311     case MR_SUCCESS:
1312       break;
1313     case MR_EXISTS:
1314       Put_message("That alias name is already in use.");
1315       break;
1316     case MR_PERM:
1317       Put_message("Permission denied.  "
1318                   "(Regular users can only add two aliases to a host.");
1319       break;
1320     default:
1321       com_err(program_name, stat, " in add_hostalias");
1322     }
1323   return DM_NORMAL;
1324 }
1325
1326
1327 int DeleteCname(int argc, char **argv)
1328 {
1329   int stat;
1330   char *args[10];
1331
1332   args[0] = partial_canonicalize_hostname(strdup(argv[1]));
1333   args[1] = canonicalize_hostname(strdup(argv[2]));
1334   stat = do_mr_query("delete_hostalias", 2, args, NULL, NULL);
1335   if (stat)
1336     com_err(program_name, stat, " in delete_hostalias");
1337   return DM_NORMAL;
1338 }
1339
1340
1341 /*      Function Name: AddMachineToCluster
1342  *      Description: This function adds a machine to a cluster
1343  *      Arguments: argc, argv - The machine name is argv[1].
1344  *                              The cluster name in argv[2].
1345  *      Returns: DM_NORMAL.
1346  */
1347
1348 int AddMachineToCluster(int argc, char **argv)
1349 {
1350   int stat;
1351   char *machine, *cluster, temp_buf[BUFSIZ], *args[10];
1352   Bool add_it, one_machine, one_cluster;
1353   struct mqelem *melem, *mtop, *celem, *ctop;
1354
1355   machine = canonicalize_hostname(strdup(argv[1]));
1356   if (strcasecmp(machine, argv[1]) && *argv[1] != '"')
1357     {
1358       sprintf(temp_buf, "Warning: '%s' canonicalized to '%s'.",
1359               argv[1], machine);
1360       Put_message(temp_buf);
1361     }
1362   cluster = argv[2];
1363
1364   celem = ctop = GetMCInfo(CLUSTER, cluster, NULL);
1365   melem = mtop = GetMCInfo(MACHINE, machine, NULL);
1366   free(machine);
1367
1368   one_machine = (QueueCount(mtop) == 1);
1369   one_cluster = (QueueCount(ctop) == 1);
1370
1371   /* No good way to use QueryLoop() here, sigh */
1372
1373   while (melem)
1374     {
1375       char **minfo = melem->q_data;
1376       while (celem)
1377         {
1378           char **cinfo = celem->q_data;
1379           if (one_machine && one_cluster)
1380             add_it = TRUE;
1381           else
1382             {
1383               sprintf(temp_buf, "Add machine %s to cluster %s (y/n/q) ?",
1384                       minfo[M_NAME], cinfo[C_NAME]);
1385               switch (YesNoQuitQuestion(temp_buf, FALSE))
1386                 {
1387                 case TRUE:
1388                   add_it = TRUE;
1389                   break;
1390                 case FALSE:
1391                   add_it = FALSE;
1392                   break;
1393                 default:
1394                   Put_message("Aborting...");
1395                   FreeQueue(ctop);
1396                   FreeQueue(mtop);
1397                   return DM_NORMAL;
1398                 }
1399             }
1400           if (add_it)
1401             {
1402               args[0] = minfo[M_NAME];
1403               args[1] = cinfo[C_NAME];
1404               stat = do_mr_query("add_machine_to_cluster", 2, args,
1405                                  NULL, NULL);
1406               switch (stat)
1407                 {
1408                 case MR_SUCCESS:
1409                   break;
1410                 case MR_EXISTS:
1411                   sprintf(temp_buf, "%s is already in cluster %s",
1412                           minfo[M_NAME], cinfo[C_NAME]);
1413                   Put_message(temp_buf);
1414                   break;
1415                 default:
1416                   com_err(program_name, stat, " in AddMachineToCluster.");
1417                   break;
1418                 }
1419             }
1420           celem = celem->q_forw;
1421         }
1422       celem = ctop;             /* reset cluster element. */
1423       melem = melem->q_forw;
1424     }
1425   FreeQueue(ctop);
1426   FreeQueue(mtop);
1427   return DM_NORMAL;
1428 }
1429
1430 /*      Function Name: RealRemoveMachineFromCluster
1431  *      Description: This function actually removes the machine from its
1432  *                   cluster.
1433  *      Arguments: info - all information nescessary to perform the removal.
1434  *                 one_map - True if there is only one case, and we should
1435  *                           confirm.
1436  *      Returns: none.
1437  */
1438
1439 static void RealRemoveMachineFromCluster(char **info, Bool one_map)
1440 {
1441   char temp_buf[BUFSIZ];
1442   int stat;
1443
1444   sprintf(temp_buf, "Remove %s from the cluster %s",
1445           info[MAP_MACHINE], info[MAP_CLUSTER]);
1446   if (!one_map || Confirm(temp_buf))
1447     {
1448       if ((stat = do_mr_query("delete_machine_from_cluster", 2,
1449                               info, NULL, NULL)))
1450         com_err(program_name, stat, " in delete_machine_from_cluster");
1451       else
1452         {
1453           sprintf(temp_buf, "%s has been removed from the cluster %s.",
1454                   info[MAP_MACHINE], info[MAP_CLUSTER]);
1455           Put_message(temp_buf);
1456         }
1457     }
1458   else
1459     Put_message("Machine not removed.");
1460 }
1461
1462 /*      Function Name: RemoveMachineFromCluster
1463  *      Description: Removes this machine form a specific cluster.
1464  *      Arguments: argc, argv - Name of machine in argv[1].
1465  *                              Name of cluster in argv[2].
1466  *      Returns: none.
1467  */
1468
1469 int RemoveMachineFromCluster(int argc, char **argv)
1470 {
1471   struct mqelem *elem = NULL;
1472   char buf[BUFSIZ], *args[10];
1473   int stat;
1474
1475   args[MAP_MACHINE] = canonicalize_hostname(strdup(argv[1]));
1476   if (strcasecmp(args[MAP_MACHINE], argv[1]) && *argv[1] != '"')
1477     {
1478       sprintf(buf, "Warning: '%s' canonicalized to '%s'.",
1479               argv[1], args[MAP_MACHINE]);
1480       Put_message(buf);
1481     }
1482   args[MAP_CLUSTER] = argv[2];
1483   args[MAP_END] = NULL;
1484
1485   stat = do_mr_query("get_machine_to_cluster_map", CountArgs(args), args,
1486                      StoreInfo, &elem);
1487   if (stat == MR_NO_MATCH)
1488     {
1489       sprintf(buf, "The machine %s is not in the cluster %s.",
1490               args[MAP_MACHINE], args[MAP_CLUSTER]);
1491       Put_message(buf);
1492       free(args[MAP_MACHINE]);
1493       return DM_NORMAL;
1494     }
1495   if (stat != MR_SUCCESS)
1496     com_err(program_name, stat, " in delete_machine_from_cluster");
1497
1498   elem = QueueTop(elem);
1499   QueryLoop(elem, PrintMCMap, RealRemoveMachineFromCluster,
1500             "Remove this machine from this cluster");
1501
1502   FreeQueue(elem);
1503   free(args[MAP_MACHINE]);
1504   return DM_NORMAL;
1505 }
1506
1507 /* ---------- Subnet Menu -------- */
1508
1509 /*      Function Name: ShowSubnetInfo
1510  *      Description: Gets information about a subnet given its name.
1511  *      Arguments: argc, argc - the name of the subnet in in argv[1].
1512  *      Returns: DM_NORMAL.
1513  */
1514
1515 int ShowSubnetInfo(int argc, char **argv)
1516 {
1517   struct mqelem *top;
1518
1519   top = GetMCInfo(SUBNET, argv[1], (char *) NULL);
1520   Loop(top, (void (*)(char **)) PrintSubnetInfo);
1521   FreeQueue(top);
1522   return DM_NORMAL;
1523 }
1524
1525 /*      Function Name: AddSubnet
1526  *      Description: Creates a new subnet.
1527  *      Arguments: argc, argv - the name of the new subnet is argv[1].
1528  *      Returns: DM_NORMAL.
1529  */
1530
1531 int AddSubnet(int argc, char **argv)
1532 {
1533   char **args, *info[MAX_ARGS_SIZE], *name = argv[1];
1534   int stat;
1535
1536   /*
1537    * Check to see if this subnet already exists.
1538    */
1539   if (!ValidName(name))
1540     return DM_NORMAL;
1541
1542   if ((stat = do_mr_query("get_subnet", 1, &name, NULL, NULL)) == MR_SUCCESS)
1543     {
1544       Put_message("This subnet already exists.");
1545       return DM_NORMAL;
1546     }
1547   else if (stat != MR_NO_MATCH)
1548     {
1549       com_err(program_name, stat, " in AddSubnet.");
1550       return DM_NORMAL;
1551     }
1552   if (!(args = AskMCDInfo(SetSubnetDefaults(info, name), SUBNET, FALSE)))
1553     {
1554       Put_message("Aborted.");
1555       FreeInfo(info);
1556       return DM_NORMAL;
1557     }
1558
1559   /*
1560    * Actually create the new Subnet.
1561    */
1562   if ((stat = do_mr_query("add_subnet", CountArgs(args), args, NULL, NULL)))
1563     com_err(program_name, stat, " in AddSubnet.");
1564
1565   FreeInfo(info);
1566   return DM_NORMAL;
1567 }
1568
1569 /*      Function Name: RealUpdateSubnet
1570  *      Description: This function actually performs the subnet update.
1571  *      Arguments: info - all information nesc. for updating the subnet.
1572  *                 junk - an UNUSED boolean.
1573  *      Returns: none.
1574  */
1575
1576 static void RealUpdateSubnet(char **info, Bool junk)
1577 {
1578   int stat;
1579   char **args = AskMCDInfo(info, SUBNET, TRUE);
1580   if (!args)
1581     {
1582       Put_message("Aborted.");
1583       return;
1584     }
1585   if ((stat = do_mr_query("update_subnet", CountArgs(args), args, NULL, NULL)))
1586     com_err(program_name, stat, " in UpdateSubnet.");
1587   else
1588     Put_message("Subnet successfully updated.");
1589 }
1590
1591 /*      Function Name: UpdateSubnet
1592  *      Description: This Function Updates a subnet
1593  *      Arguments: name of the subnet in argv[1].
1594  *      Returns: DM_NORMAL.
1595  */
1596
1597 int UpdateSubnet(int argc, char **argv)
1598 {
1599   struct mqelem *top;
1600   top = GetMCInfo(SUBNET, argv[1], NULL);
1601   QueryLoop(top, NullPrint, RealUpdateSubnet, "Update the subnet");
1602
1603   FreeQueue(top);
1604   return DM_NORMAL;
1605 }
1606
1607 /*      Function Name: RealDeleteSubnet
1608  *      Description: Actually performs the subnet deletion.
1609  *      Arguments: info - all information about this subnet.
1610  *                 one_subnet - If true then there was only one subnet in
1611  *                               the queue, and we should confirm.
1612  *      Returns: none.
1613  */
1614
1615 static void RealDeleteSubnet(char **info, Bool one_subnet)
1616 {
1617   int stat;
1618   char temp_buf[BUFSIZ];
1619
1620   sprintf(temp_buf,
1621           "Are you sure the you want to delete the subnet %s (y/n) ?",
1622           info[C_NAME]);
1623   if (!one_subnet || Confirm(temp_buf))
1624     {
1625       if ((stat = do_mr_query("delete_subnet", 1, &info[C_NAME], NULL, NULL)))
1626         {
1627           com_err(program_name, stat, " in delete_subnet.");
1628           sprintf(temp_buf, "Subnet %s ** NOT ** deleted.", info[C_NAME]);
1629           Put_message(temp_buf);
1630         }
1631       else
1632         {
1633           sprintf(temp_buf, "subnet %s successfully deleted.",
1634                   info[C_NAME]);
1635           Put_message(temp_buf);
1636         }
1637     }
1638 }
1639
1640 /*      Function Name: DeleteSubnet
1641  *      Description: This function removes a subnet from the database.
1642  *      Arguments: argc, argv - the name of the subnet is stored in argv[1].
1643  *      Returns: DM_NORMAL.
1644  */
1645
1646 int DeleteSubnet(int argc, char **argv)
1647 {
1648   struct mqelem *top;
1649
1650   top = GetMCInfo(SUBNET, argv[1], NULL);
1651   QueryLoop(top, PrintSubnetInfo, RealDeleteSubnet, "Delete the subnet");
1652
1653   FreeQueue(top);
1654   return DM_NORMAL;
1655 }
1656
1657 /* ---------- Cluster Menu -------- */
1658
1659 /*      Function Name: ShowClusterInfo
1660  *      Description: Gets information about a cluser given its name.
1661  *      Arguments: argc, argc - the name of the cluster in in argv[1].
1662  *      Returns: DM_NORMAL.
1663  */
1664
1665 int ShowClusterInfo(int argc, char **argv)
1666 {
1667   struct mqelem *top;
1668
1669   top = GetMCInfo(CLUSTER, argv[1], NULL);
1670   Loop(top, (void (*)(char **)) PrintClusterInfo);
1671   FreeQueue(top);
1672   return DM_NORMAL;
1673 }
1674
1675 /*      Function Name: AddCluster
1676  *      Description: Creates a new cluster.
1677  *      Arguments: argc, argv - the name of the new cluster is argv[1].
1678  *      Returns: DM_NORMAL.
1679  */
1680
1681 int AddCluster(int argc, char **argv)
1682 {
1683   char **args, *info[MAX_ARGS_SIZE], *name = argv[1];
1684   int stat;
1685
1686   /*
1687    * Check to see if this cluster already exists.
1688    */
1689   if (!ValidName(name))
1690     return DM_NORMAL;
1691
1692   if ((stat = do_mr_query("get_cluster", 1, &name, NULL, NULL)) == MR_SUCCESS)
1693     {
1694       Put_message("This cluster already exists.");
1695       return DM_NORMAL;
1696     }
1697   else if (stat != MR_NO_MATCH)
1698     {
1699       com_err(program_name, stat, " in AddCluster.");
1700       return DM_NORMAL;
1701     }
1702   if (!(args = AskMCDInfo(SetClusterDefaults(info, name), CLUSTER, FALSE)))
1703     {
1704       Put_message("Aborted.");
1705       FreeInfo(info);
1706       return DM_NORMAL;
1707     }
1708
1709   /*
1710    * Actually create the new Cluster.
1711    */
1712   if ((stat = do_mr_query("add_cluster", CountArgs(args), args, NULL, NULL)))
1713     com_err(program_name, stat, " in AddCluster.");
1714
1715   FreeInfo(info);
1716   return DM_NORMAL;
1717 }
1718
1719 /*      Function Name: RealUpdateCluster
1720  *      Description: This function actually performs the cluster update.
1721  *      Arguments: info - all information nesc. for updating the cluster.
1722  *                 junk - an UNUSED boolean.
1723  *      Returns: none.
1724  */
1725
1726 static void RealUpdateCluster(char **info, Bool junk)
1727 {
1728   int stat;
1729   char **args = AskMCDInfo(info, CLUSTER, TRUE);
1730
1731   if (!args)
1732     {
1733       Put_message("Aborted.");
1734       return;
1735     }
1736   if ((stat = do_mr_query("update_cluster", CountArgs(args),
1737                           args, NULL, NULL)))
1738     com_err(program_name, stat, " in UpdateCluster.");
1739   else
1740     Put_message("Cluster successfully updated.");
1741 }
1742
1743 /*      Function Name: UpdateCluster
1744  *      Description: This Function Updates a cluster
1745  *      Arguments: name of the cluster in argv[1].
1746  *      Returns: DM_NORMAL.
1747  */
1748
1749 int UpdateCluster(int argc, char **argv)
1750 {
1751   struct mqelem *top;
1752   top = GetMCInfo(CLUSTER, argv[1], NULL);
1753   QueryLoop(top, NullPrint, RealUpdateCluster, "Update the cluster");
1754
1755   FreeQueue(top);
1756   return DM_NORMAL;
1757 }
1758
1759 /*      Function Name: CheckAndRemoveMachine
1760  *      Description: This function checks and removes all machines from a
1761  *                   cluster.
1762  *      Arguments: name - name of the cluster.
1763  *                 ask_first - if TRUE, then we will query the user, before
1764  *                             deletion.
1765  *      Returns: SUB_ERROR if all machines not removed.
1766  */
1767
1768 int CheckAndRemoveMachines(char *name, Bool ask_first)
1769 {
1770   int stat, ret_value;
1771   Bool delete_it;
1772   char *args[10], temp_buf[BUFSIZ], *ptr;
1773   struct mqelem *top, *elem = NULL;
1774
1775   ret_value = SUB_NORMAL;
1776   args[MAP_MACHINE] = "*";
1777   args[MAP_CLUSTER] = name;
1778   stat = do_mr_query("get_machine_to_cluster_map", 2, args, StoreInfo, &elem);
1779   if (stat && stat != MR_NO_MATCH)
1780     {
1781       com_err(program_name, stat, " in get_machine_to_cluster_map.");
1782       return DM_NORMAL;
1783     }
1784   if (stat == MR_SUCCESS)
1785     {
1786       elem = top = QueueTop(elem);
1787       if (ask_first)
1788         {
1789           sprintf(temp_buf, "The cluster %s has the following machines in it:",
1790                   name);
1791           Put_message(temp_buf);
1792           while (elem)
1793             {
1794               char **info = elem->q_data;
1795               Print(1, &info[MAP_MACHINE], (char *) NULL);
1796               elem = elem->q_forw;
1797             }
1798           ptr = "Remove ** ALL ** these machines from this cluster?";
1799
1800           if (YesNoQuestion(ptr, FALSE) == TRUE) /* may return -1. */
1801             delete_it = TRUE;
1802           else
1803             {
1804               Put_message("Aborting...");
1805               FreeQueue(top);
1806               return SUB_ERROR;
1807             }
1808         }
1809       else
1810         delete_it = TRUE;
1811
1812       if (delete_it)
1813         {
1814           elem = top;
1815           while (elem)
1816             {
1817               char **info = elem->q_data;
1818               if ((stat = do_mr_query("delete_machine_from_cluster",
1819                                       2, info, NULL, NULL)))
1820                 {
1821                   ret_value = SUB_ERROR;
1822                   com_err(program_name, stat,
1823                           " in delete_machine_from_cluster.");
1824                   sprintf(temp_buf,
1825                           "Machine %s ** NOT ** removed from cluster %s.",
1826                           info[MAP_MACHINE], info[MAP_CLUSTER]);
1827                   Put_message(temp_buf);
1828                 }
1829               elem = elem->q_forw;
1830             }
1831         }
1832     }
1833   return ret_value;
1834 }
1835
1836 /*      Function Name: RealDeleteCluster
1837  *      Description: Actually performs the cluster deletion.
1838  *      Arguments: info - all information about this cluster.
1839  *                 one_cluster - If true then there was only one cluster in
1840  *                               the queue, and we should confirm.
1841  *      Returns: none.
1842  */
1843
1844 static void RealDeleteCluster(char **info, Bool one_cluster)
1845 {
1846   int stat;
1847   char temp_buf[BUFSIZ];
1848
1849   sprintf(temp_buf,
1850           "Are you sure the you want to delete the cluster %s (y/n) ?",
1851           info[C_NAME]);
1852   if (!one_cluster || Confirm(temp_buf))
1853     {
1854       if (CheckAndRemoveMachines(info[C_NAME], TRUE) != SUB_ERROR)
1855         {
1856           if ((stat = do_mr_query("delete_cluster", 1,
1857                                   &info[C_NAME], NULL, NULL)))
1858             {
1859               com_err(program_name, stat, " in delete_cluster.");
1860               sprintf(temp_buf, "Cluster %s ** NOT ** deleted.", info[C_NAME]);
1861               Put_message(temp_buf);
1862             }
1863           else
1864             {
1865               sprintf(temp_buf, "cluster %s successfully deleted.",
1866                       info[C_NAME]);
1867               Put_message(temp_buf);
1868             }
1869         }
1870     }
1871 }
1872
1873 /*      Function Name: DeleteCluster
1874  *      Description: This function removes a cluster from the database.
1875  *      Arguments: argc, argv - the name of the cluster is stored in argv[1].
1876  *      Returns: DM_NORMAL.
1877  */
1878
1879 int DeleteCluster(int argc, char **argv)
1880 {
1881   struct mqelem *top;
1882
1883   top = GetMCInfo(CLUSTER, argv[1], NULL);
1884   QueryLoop(top, PrintClusterInfo, RealDeleteCluster, "Delete the cluster");
1885
1886   FreeQueue(top);
1887   return DM_NORMAL;
1888 }
1889
1890 /* ----------- Cluster Data Menu -------------- */
1891
1892 /*      Function Name: ShowClusterData
1893  *      Description: This function shows the services for one cluster.
1894  *      Arguments: argc, argv - The name of the cluster is argv[1].
1895  *                              The label of the data in argv[2].
1896  *      Returns: DM_NORMAL.
1897  */
1898
1899 int ShowClusterData(int argc, char **argv)
1900 {
1901   struct mqelem *elem, *top;
1902   char **info;
1903
1904   top = elem = GetMCInfo(DATA, argv[1], argv[2]);
1905   while (elem)
1906     {
1907       info = elem->q_data;
1908       PrintClusterData(info);
1909       elem = elem->q_forw;
1910     }
1911   FreeQueue(top);
1912   return DM_NORMAL;
1913 }
1914
1915 /*      Function Name: AddClusterData
1916  *      Description: This function adds some data to the cluster.
1917  *      Arguments: argv, argc:   argv[1] - the name of the cluster.
1918  *                               argv[2] - the label of the data.
1919  *                               argv[3] - the data.
1920  *      Returns: DM_NORMAL.
1921  */
1922
1923 int AddClusterData(int argc, char **argv)
1924 {
1925   int stat, i;
1926
1927   for (i = 1; i < 4; i++)
1928     {
1929       if (IS_EMPTY(argv[i]))
1930         {
1931           Put_message("Cluster data cannot be an empty string.");
1932           return DM_NORMAL;
1933         }
1934     }
1935   if ((stat = do_mr_query("add_cluster_data", 3, argv + 1, NULL, NULL)))
1936     com_err(program_name, stat, " in AddClusterData.");
1937   return DM_NORMAL;
1938 }
1939
1940 /*      Function Name: RealRemoveClusterData
1941  *      Description: actually removes the cluster data.
1942  *      Arguments: info - all info necessary to remove the cluster, in an array
1943  *                        of strings.
1944  *                 one_item - if true then the queue has only one elem and we
1945  *                            should confirm.
1946  *      Returns: none.
1947  */
1948
1949 static void RealRemoveClusterData(char **info, Bool one_item)
1950 {
1951   int stat;
1952   char *temp_ptr;
1953
1954   Put_message(" ");
1955   temp_ptr = "Are you sure that you want to remove this cluster data (y/n) ?";
1956   PrintClusterData(info);
1957   if (!one_item || Confirm(temp_ptr))
1958     {
1959       if ((stat = do_mr_query("delete_cluster_data", 3, info, NULL, NULL)))
1960         {
1961           com_err(program_name, stat, " in DeleteClusterData.");
1962           Put_message("Data not removed.");
1963         }
1964       else
1965         Put_message("Removal successful.");
1966     }
1967 }
1968
1969 /*      Function Name: RemoveClusterData
1970  *      Description: This function removes data on a given cluster.
1971  *      Arguments: argv, argc:   argv[1] - the name of the cluster.
1972  *                               argv[2] - the label of the data.
1973  *                               argv[3] - the data.
1974  *      Returns: DM_NORMAL.
1975  */
1976
1977 int RemoveClusterData(int argc, char **argv)
1978 {
1979   struct mqelem *top;
1980
1981   top = GetMCInfo(DATA, argv[1], argv[2]);
1982   QueryLoop(top, PrintClusterData, RealRemoveClusterData,
1983             "Remove data from cluster");
1984
1985   FreeQueue(top);
1986   return DM_NORMAL;
1987 }
1988
1989 /*      Function Name: MachineToClusterMap
1990  *      Description: This Retrieves the mapping between machine and cluster
1991  *      Arguments: argc, argv - argv[1] -> machine name or wildcard.
1992  *                              argv[2] -> cluster name or wildcard.
1993  *      Returns: none.
1994  */
1995
1996 int MachineToClusterMap(int argc, char **argv)
1997 {
1998   struct mqelem *elem, *top;
1999   char *tmpname, temp_buf[256];
2000
2001   tmpname = canonicalize_hostname(strdup(argv[1]));
2002   if (strcasecmp(tmpname, argv[1]) && *argv[1] != '"')
2003     {
2004       sprintf(temp_buf, "Warning: '%s' canonicalized to '%s'.",
2005               argv[1], tmpname);
2006       Put_message(temp_buf);
2007     }
2008   top = elem = GetMCInfo(MAP, tmpname, argv[2]);
2009
2010   Put_message("");              /* blank line on screen */
2011   while (elem)
2012     {
2013       char **info = elem->q_data;
2014       PrintMCMap(info);
2015       elem = elem->q_forw;
2016     }
2017
2018   FreeQueue(top);
2019   free(tmpname);
2020   return DM_NORMAL;
2021 }
2022
2023 /*        Function Name: MachineByOwner
2024  *        Description: This function prints all machines which are owned by 
2025  *                     a given user or group.
2026  *        Arguments: none.
2027  *        Returns: DM_NORMAL.
2028  */
2029
2030 int MachineByOwner(int argc, char **argv)
2031 {
2032   char buf[BUFSIZ], temp_buf[BUFSIZ], *type, *name;
2033   struct mqelem *top;
2034
2035   type = strdup("USER");
2036   if (GetTypeFromUser("Type of owner", "ace_type", &type) == SUB_ERROR)
2037     return DM_NORMAL;
2038
2039   sprintf(buf, "Name of %s", type);
2040   name = strdup(user);
2041   if (GetValueFromUser(buf, &name) == SUB_ERROR)
2042     return DM_NORMAL;
2043
2044   switch (YesNoQuestion("Do you want a recursive search (y/n)", FALSE))
2045     {
2046     case TRUE:
2047       sprintf(temp_buf, "R%s", type);     /* "USER to "RUSER", etc. */
2048       free(type);
2049       type = strdup(temp_buf);
2050       break;
2051     case FALSE:
2052       break;
2053     default:
2054       return DM_NORMAL;
2055     }
2056
2057   top = GetMachineByOwner(type, name);
2058   Loop(top, PrintMachine);
2059
2060   FreeQueue(top);
2061   return DM_NORMAL;
2062 }
2063
2064 /*         Function Name: GetMachineByOwner
2065  *         Description: This function stores information retrieved by 
2066  *                      the get_host_by_owner query
2067  *         Arguments: type - an ace_type, argv[0] for the query
2068  *                    name - name of machine, argv[1] for the query
2069  *         Returns: the top element of a queue returning the data, or NULL.
2070  */
2071
2072 struct mqelem *GetMachineByOwner(char *type, char *name)
2073 {
2074   char *args[2];
2075   struct mqelem *elem = NULL;
2076   int status;
2077
2078   args[0] = type;
2079   args[1] = name;
2080   if ((status = do_mr_query("get_host_by_owner", 2, args, StoreInfo, &elem)))
2081     {
2082       com_err(program_name, status, " in get_host_by_owner");
2083       return NULL;
2084     }
2085   return QueueTop(elem);
2086 }
2087
2088 int MachineByAcctNumber(int argc, char **argv)
2089 {
2090   char *args[1], *account_number;
2091   int status;
2092   struct mqelem *elem = NULL;
2093
2094   account_number = strdup("");
2095   if (GetValueFromUser("Account Number", &account_number) == SUB_ERROR)
2096     return DM_NORMAL;
2097
2098   args[0] = account_number;
2099   if (status = do_mr_query("get_host_by_account_number", 1, args, StoreInfo, 
2100                            &elem))
2101     {
2102       com_err(program_name, status, " in get_host_by_account_number");
2103       return DM_NORMAL;
2104     }
2105   Loop(QueueTop(elem), (void (*)(char **)) PrintMachInfo);
2106   FreeQueue(elem);
2107
2108   return DM_NORMAL;
2109 }
2110
2111 int ShowContainerInfo(int argc, char **argv)
2112 {
2113   struct mqelem *top;
2114
2115   top = GetMCInfo(CONTAINER, argv[1], NULL);
2116   Loop(top, (void (*)(char **)) PrintContainerInfo);
2117   FreeQueue(top);
2118   return DM_NORMAL;
2119 }
2120
2121 static char *PrintContainerInfo(char **info)
2122 {
2123   char buf[BUFSIZ], tbuf[256];
2124   char *args[2];
2125   struct mqelem *elem = NULL;
2126   int stat;
2127
2128   Put_message("");
2129   sprintf(buf, "Container:      %-16s    Public:      %s", info[CON_NAME],
2130           atoi(info[CON_PUBLIC]) ? "Yes" : "No");
2131   Put_message(buf);
2132   /* Do a get_container_list to see what the associated list is. */
2133   args[0] = info[CON_NAME];
2134   if (stat = do_mr_query("get_container_list", 1, args, StoreInfo, &elem))
2135     {
2136       if (stat != MR_NO_MATCH)
2137         com_err(program_name, stat, " looking up container list");
2138     }
2139   else 
2140     {
2141       sprintf(buf, "Container's associated list is: LIST %s",
2142               ((char **)elem->q_data)[1]);
2143       Put_message(buf);
2144       FreeQueue(elem);
2145     }
2146   sprintf(buf, "Description:    %-16s", info[CON_DESCRIPT]);
2147   Put_message(buf);
2148   sprintf(buf, "Location:       %-16s    Contact:     %s", info[CON_LOCATION],
2149           info[CON_CONTACT]);
2150   Put_message(buf);
2151   sprintf(tbuf, "%s %s", info[CON_OWNER_TYPE],
2152           strcmp(info[CON_OWNER_TYPE], "NONE") ? info[CON_OWNER_NAME] : "");
2153   sprintf(buf, "Owner:          %-16s", tbuf);
2154   Put_message(buf);
2155   sprintf(tbuf, "%s %s", info[CON_MEMACE_TYPE],
2156           strcmp(info[CON_MEMACE_TYPE], "NONE") ? info[CON_MEMACE_NAME] : "");
2157   sprintf(buf, "Membership ACL: %-16s", tbuf);
2158   Put_message(buf);
2159   Put_message("");
2160   sprintf(buf, MOD_FORMAT, info[CON_MODBY], info[CON_MODTIME], 
2161           info[CON_MODWITH]);
2162   Put_message(buf);
2163   return(info[CON_NAME]);
2164 }
2165
2166 static char *PrintContainer(char **info)
2167 {
2168   char buf[BUFSIZ];
2169
2170   sprintf(buf, "Container: %s", info[CON_NAME]);
2171   Put_message(buf);
2172   return "";
2173 }
2174
2175 static char *PrintMContMap(char **info)
2176 {
2177   char buf[BUFSIZ];
2178   sprintf(buf, "Container: %-30s Machine: %-20s",
2179           info[1], info[0]);
2180   Put_message(buf);
2181   return "";
2182 }
2183
2184 int AddContainer(int argc, char **argv)
2185 {
2186   char **args, *info[MAX_ARGS_SIZE], *name = argv[1];
2187   int stat;
2188
2189   /* Can't use ValidName() because spaces are allowed in container names */
2190   if (IS_EMPTY(name))
2191     {
2192       Put_message("Please use a non-empty name.");
2193       return DM_NORMAL;
2194     }
2195
2196   if (strchr(name, '*') || strchr(name, '?'))
2197     {
2198       Put_message("Wildcards not accepted here.");
2199       return DM_NORMAL;
2200     }
2201
2202   /* Check if this cluster already exists. */
2203   if ((stat = do_mr_query("get_container", 1, &name, NULL, NULL))
2204       == MR_SUCCESS)
2205     {
2206       Put_message("This container already exists.");
2207       return DM_NORMAL;
2208     }
2209   else if (stat != MR_NO_MATCH)
2210     {
2211       com_err(program_name, stat, " in AddContainer.");
2212       return DM_NORMAL;
2213     }
2214   if (!(args = AskMCDInfo(SetContainerDefaults(info, name), CONTAINER, FALSE)))
2215     {
2216       Put_message("Aborted.");
2217       FreeInfo(info);
2218       return DM_NORMAL;
2219     }
2220
2221   /* Create the new container. */
2222   if ((stat = do_mr_query("add_container", CountArgs(args), args, NULL, NULL)))
2223     com_err(program_name, stat, " in AddContainer.");
2224
2225   FreeInfo(info);
2226   return DM_NORMAL;
2227 }
2228
2229 int UpdateContainer(int argc, char **argv)
2230 {
2231   struct mqelem *top;
2232   top = GetMCInfo(CONTAINER, argv[1], NULL);
2233   QueryLoop(top, NullPrint, RealUpdateContainer, "Update the container");
2234
2235   FreeQueue(top);
2236   return DM_NORMAL;
2237 }
2238
2239 static void RealUpdateContainer(char **info, Bool junk)
2240 {
2241   int stat;
2242   char **args = AskMCDInfo(info, CONTAINER, TRUE);
2243
2244   if (!args)
2245     {
2246       Put_message("Aborted.");
2247       return;
2248     }
2249   if ((stat = do_mr_query("update_container", CountArgs(args), args,
2250                           NULL, NULL)))
2251     com_err(program_name, stat, " in UpdateContainer.");
2252   else
2253     Put_message("Container successfully updated.");
2254 }
2255
2256 int DeleteContainer(int argc, char **argv)
2257 {
2258   struct mqelem *top;
2259
2260   top = GetMCInfo(CONTAINER, argv[1], NULL);
2261   QueryLoop(top, PrintClusterInfo, RealDeleteContainer, 
2262             "Delete the container");
2263
2264   FreeQueue(top);
2265   return DM_NORMAL;
2266 }
2267
2268 static void RealDeleteContainer(char **info, Bool one_container)
2269 {
2270   int stat;
2271   char temp_buf[BUFSIZ];
2272
2273   sprintf(temp_buf,
2274           "Are you sure you want to delete the container %s (y/n) ?",
2275           info[CON_NAME]);
2276   if (!one_container || Confirm(temp_buf))
2277     {
2278       if (CheckAndRemoveMachinesFromContainer(info[CON_NAME], TRUE) 
2279           != SUB_ERROR)
2280         {
2281           if ((stat = do_mr_query("delete_container", 1, &info[CON_NAME],
2282                                   NULL, NULL)))
2283             {
2284               com_err(program_name, stat, " in delete_container.");
2285               sprintf(temp_buf, "Container %s ** NOT ** deleted.", 
2286                       info[CON_NAME]);
2287               Put_message(temp_buf);
2288             }
2289           else
2290             {
2291               sprintf(temp_buf, "Container %s successfully deleted.",
2292                       info[CON_NAME]);
2293               Put_message(temp_buf);
2294             }
2295         }
2296     }
2297 }
2298
2299 int CheckAndRemoveMachinesFromContainer(char *name, Bool ask_first)
2300 {
2301   int stat, ret_value;
2302   Bool delete_it;
2303   char *args[10], temp_buf[BUFSIZ], *ptr;
2304   struct mqelem *top, *elem = NULL;
2305   
2306   ret_value = SUB_NORMAL;
2307   args[0] = name;
2308   args[1] = "0";
2309   stat = do_mr_query("get_machines_of_container", 2, args, StoreInfo, 
2310                      &elem);
2311   if (stat && stat != MR_NO_MATCH)
2312     {
2313       com_err(program_name, stat, " in get_machines_of_container");
2314       return DM_NORMAL;
2315     }
2316   if (stat == MR_SUCCESS)
2317     {
2318       elem = top = QueueTop(elem);
2319       if (ask_first)
2320         {
2321           sprintf(temp_buf,
2322                   "The container %s has the following machines in it:", name);
2323           Put_message(temp_buf);
2324           while (elem)
2325             {
2326               char **info = elem->q_data;
2327               Print(1, &info[0], (char *) NULL);
2328               elem = elem->q_forw;
2329             }
2330           ptr = "Remove ** ALL ** these machines from this container?";
2331
2332           if (YesNoQuestion(ptr, FALSE) == TRUE) /* may return -1. */
2333             delete_it = TRUE;
2334           else
2335             {
2336               Put_message("Aborting...");
2337               FreeQueue(top);
2338               return SUB_ERROR;
2339             }
2340         }
2341       else
2342         delete_it = TRUE;
2343
2344       if (delete_it)
2345         {
2346           elem = top;
2347           while (elem)
2348             {
2349               char **info = elem->q_data;
2350               if ((stat = do_mr_query("delete_machine_from_container",
2351                                       2, info, NULL, NULL)))
2352                 {
2353                   ret_value = SUB_ERROR;
2354                   com_err(program_name, stat, 
2355                           " in delete_machine_from_container.");
2356                   sprintf(temp_buf, 
2357                           "Machine %s ** NOT ** removed from container %s.",
2358                           info[0], info[1]);
2359                   Put_message(temp_buf);
2360                 }
2361               elem = elem->q_forw;
2362             }
2363         }
2364     }
2365   return ret_value;
2366 }
2367
2368 int GetSubContainers(int argc, char **argv)
2369 {
2370   char *args[2];
2371   struct mqelem *elem = NULL, *top = NULL;
2372   int stat;
2373
2374   args[0] = argv[1];
2375
2376   if (YesNoQuestion("Do you want a recursive search?", TRUE) == TRUE)
2377     args[1] = "1";
2378   else
2379     args[1] = "0";
2380
2381   if (stat = do_mr_query("get_subcontainers_of_container", 2, args,
2382                          StoreInfo, &elem))
2383     com_err(program_name, stat, " in get_subcontainers_of_container");
2384
2385   top = QueueTop(elem);
2386   Loop(top, ((void (*)(char **)) PrintContainer));
2387   FreeQueue(top);
2388   return DM_NORMAL;
2389 }
2390
2391 int MachineToContainerMap(int argc, char **argv)
2392 {
2393   struct mqelem *elem, *top;
2394   char *tmpname, temp_buf[256];
2395
2396   tmpname = canonicalize_hostname(strdup(argv[1]));
2397   if (strcasecmp(tmpname, argv[1]) && *argv[1] != '"')
2398     {
2399       sprintf(temp_buf, "Warning: '%s' canonicalized to '%s'.",
2400               argv[1], tmpname);
2401       Put_message(temp_buf);
2402     }
2403   top = elem = GetMCInfo(CONTMAP, tmpname, NULL);
2404
2405   Put_message("");
2406   while (elem)
2407     {
2408       char **info = elem->q_data;
2409       PrintMContMap(info);
2410       elem = elem->q_forw;
2411     }
2412
2413   FreeQueue(top);
2414   free(tmpname);
2415   return DM_NORMAL;
2416 }
2417
2418 int AddMachineToContainer(int argc, char **argv)
2419 {
2420   int stat;
2421   char *machine, *container, temp_buf[BUFSIZ], *args[10];
2422   Bool add_it, one_machine, one_container;
2423   struct mqelem *melem, *mtop, *celem, *ctop;
2424
2425   machine = canonicalize_hostname(strdup(argv[1]));
2426   if (strcasecmp(machine, argv[1]) && *argv[1] != '"')
2427     {
2428       sprintf(temp_buf, "Warning: '%s' canonicalized to '%s'.",
2429               argv[1], machine);
2430       Put_message(temp_buf);
2431     }
2432   container = argv[2];
2433
2434   celem = ctop = GetMCInfo(CONTAINER, container, NULL);
2435   melem = mtop = GetMCInfo(MACHINE, machine, NULL);
2436   free(machine);
2437
2438   one_machine = (QueueCount(mtop) == 1);
2439   one_container = (QueueCount(ctop) == 1);
2440
2441   while (melem)
2442     {
2443       char **minfo = melem->q_data;
2444       while (celem)
2445         {
2446           char **cinfo = celem->q_data;
2447           if (one_machine && one_container)
2448             add_it = TRUE;
2449           else
2450             {
2451               sprintf(temp_buf, "Add machine %s to container %s (y/n/q) ?",
2452                       minfo[M_NAME], cinfo[CON_NAME]);
2453               switch (YesNoQuestion(temp_buf, FALSE))
2454                 {
2455                 case TRUE:
2456                   add_it = TRUE;
2457                   break;
2458                 case FALSE:
2459                   add_it = FALSE;
2460                   break;
2461             default:
2462               Put_message("Aborting...");
2463               FreeQueue(ctop);
2464               FreeQueue(mtop);
2465               return DM_NORMAL;
2466                 }
2467             }
2468           if (add_it)
2469             {
2470               args[0] = minfo[M_NAME];
2471               args[1] = cinfo[CON_NAME];
2472               stat = do_mr_query("add_machine_to_container", 2, args, NULL, 
2473                                  NULL);
2474               switch (stat)
2475                 {
2476                 case MR_SUCCESS:
2477                   break;
2478                 case MR_EXISTS:
2479                   sprintf(temp_buf, "%s is already in container %s",
2480                           minfo[M_NAME], cinfo[CON_NAME]);
2481                   Put_message(temp_buf);
2482                   break;
2483                 default:
2484                   com_err(program_name, stat, " in AddMachineToContainer.");
2485                   break;
2486                 }
2487             }
2488           celem = celem->q_forw;
2489         }
2490       celem = ctop;
2491       melem = melem->q_forw;
2492     }
2493   FreeQueue(ctop);
2494   FreeQueue(mtop);
2495   return DM_NORMAL;
2496 }
2497
2498 int RemoveMachineFromContainer(int argc, char **argv)
2499 {
2500   struct mqelem *elem = NULL;
2501   char buf[BUFSIZ], *args[10];
2502   int stat;
2503
2504   args[0] = canonicalize_hostname(strdup(argv[1]));
2505   if (strcasecmp(args[0], argv[1]) && *argv[1] != '"')
2506     {
2507       sprintf(buf, "Warning: '%s' canonicalized to '%s'.",
2508               argv[1], args[0]);
2509       Put_message(buf);
2510     }
2511   args[1] = argv[2];
2512   args[2] = NULL;
2513
2514   stat = do_mr_query("get_machine_to_container_map", 1, args, StoreInfo, 
2515                      &elem);
2516   if (stat == MR_NO_MATCH)
2517     {
2518       sprintf(buf, "The machine %s is not in the container %s.",
2519               args[0], args[1]);
2520       Put_message(buf);
2521       free(args[0]);
2522       return DM_NORMAL;
2523     }
2524   if (stat != MR_SUCCESS)
2525     com_err(program_name, stat, " in deleter_machine_from_container");
2526
2527   elem = QueueTop(elem);
2528   QueryLoop(elem, PrintMContMap, RealRemoveMachineFromContainer, 
2529             "Remove this machine from this container");
2530   
2531   FreeQueue(elem);
2532   free(args[0]);
2533   return DM_NORMAL;
2534 }
2535
2536 static void RealRemoveMachineFromContainer(char **info, Bool one_contmap)
2537 {
2538   char temp_buf[BUFSIZ];
2539   int stat;
2540
2541   sprintf(temp_buf, "Remove %s from the container %s",
2542           info[0], info[1]);
2543   if (!one_contmap || Confirm(temp_buf))
2544     {
2545       if ((stat = do_mr_query("delete_machine_from_container", 2,
2546                               info, NULL, NULL)))
2547         com_err(program_name, stat, " in delete_machine_from_container");
2548       else
2549         {
2550           sprintf(temp_buf, "%s has been removed from the container %s.",
2551                   info[0], info[1]);
2552           Put_message(temp_buf);
2553         }
2554     }
2555   else
2556     Put_message("Machine not removed.");
2557 }
2558
2559 int GetMachinesOfContainer(int argc, char **argv)
2560 {
2561   char *args[2];
2562   struct mqelem *elem = NULL, *top = NULL;
2563   int stat;
2564
2565   args[0] = argv[1];
2566
2567   if (YesNoQuestion("Do you want a recursive search?", TRUE) == TRUE)
2568     args[1] = "1";
2569   else
2570     args[1] = "0";
2571
2572   if (stat = do_mr_query("get_machines_of_container", 2, args,
2573                          StoreInfo, &elem))
2574     com_err(program_name, stat, " in get_machines_of_container");
2575
2576   top = QueueTop(elem);
2577   Loop(top, ((void (*)(char **)) PrintMContMap));
2578   FreeQueue(top);
2579   return DM_NORMAL;
2580 }
2581
2582 int GetTopLevelCont(int argc, char **argv)
2583 {
2584   int status;
2585   struct mqelem *elem = NULL;
2586   if (status = do_mr_query("get_toplevel_containers", 0, NULL, StoreInfo, 
2587                            &elem))
2588     {
2589       com_err(program_name, status, " in get_toplevel_containers");
2590       return DM_NORMAL;
2591     }
2592   Loop(QueueTop(elem), (void(*)(char **)) PrintContainer);
2593   FreeQueue(elem);
2594   return DM_NORMAL;
2595 }
This page took 0.238007 seconds and 5 git commands to generate.