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