]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - usr.sbin/sysinstall/dispatch.c
Copy stable/9 to releng/9.0 as part of the FreeBSD 9.0-RELEASE release
[FreeBSD/releng/9.0.git] / usr.sbin / sysinstall / dispatch.c
1 /*
2  * The new sysinstall program.
3  *
4  * This is probably the last program in the `sysinstall' line - the next
5  * generation being essentially a complete rewrite.
6  *
7  * $FreeBSD$
8  *
9  * Copyright (c) 1995
10  *      Jordan Hubbard.  All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer,
17  *    verbatim and that no modifications are made prior to this
18  *    point in the file.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  *
23  * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  */
36
37 #include "sysinstall.h"
38 #include <ctype.h>
39 #include <errno.h>
40 #include <sys/signal.h>
41 #include <sys/fcntl.h>
42
43 #include "list.h"
44
45 static int dispatch_shutdown(dialogMenuItem *unused);
46 static int dispatch_systemExecute(dialogMenuItem *unused);
47 static int dispatch_msgConfirm(dialogMenuItem *unused);
48 static int dispatch_mediaOpen(dialogMenuItem *unused);
49 static int dispatch_mediaClose(dialogMenuItem *unused);
50 static int cfgModuleFire(dialogMenuItem *self);
51
52 static struct _word {
53     char *name;
54     int (*handler)(dialogMenuItem *self);
55 } resWords[] = {
56     { "configAnonFTP",          configAnonFTP           },
57     { "configRouter",           configRouter            },
58     { "configInetd",            configInetd             },
59     { "configNFSServer",        configNFSServer         },
60     { "configNTP",              configNTP               },
61     { "configPCNFSD",           configPCNFSD            },
62     { "configPackages",         configPackages          },
63     { "configUsers",            configUsers             },
64 #ifdef WITH_SLICES
65     { "diskPartitionEditor",    diskPartitionEditor     },
66 #endif
67     { "diskPartitionWrite",     diskPartitionWrite      },
68     { "diskLabelEditor",        diskLabelEditor         },
69     { "diskLabelCommit",        diskLabelCommit         },
70     { "distReset",              distReset               },
71     { "distSetCustom",          distSetCustom           },
72     { "distUnsetCustom",        distUnsetCustom         },
73     { "distSetDeveloper",       distSetDeveloper        },
74     { "distSetKernDeveloper",   distSetKernDeveloper    },
75     { "distSetUser",            distSetUser             },
76     { "distSetMinimum",         distSetMinimum          },
77     { "distSetEverything",      distSetEverything       },
78     { "distSetSrc",             distSetSrc              },
79     { "distExtractAll",         distExtractAll          },
80     { "docBrowser",             docBrowser              },
81     { "docShowDocument",        docShowDocument         },
82     { "installCommit",          installCommit           },
83     { "installExpress",         installExpress          },
84     { "installStandard",        installStandard         },
85     { "installUpgrade",         installUpgrade          },
86     { "installFixupBase",       installFixupBase        },
87     { "installFixitHoloShell",  installFixitHoloShell   },
88     { "installFixitCDROM",      installFixitCDROM       },
89     { "installFixitUSB",        installFixitUSB         },
90     { "installFixitFloppy",     installFixitFloppy      },
91     { "installFilesystems",     installFilesystems      },
92     { "installVarDefaults",     installVarDefaults      },
93     { "loadConfig",             dispatch_load_file      },
94     { "loadFloppyConfig",       dispatch_load_floppy    },
95     { "loadCDROMConfig",        dispatch_load_cdrom     },
96     { "mediaOpen",              dispatch_mediaOpen      },
97     { "mediaClose",             dispatch_mediaClose     },
98     { "mediaSetCDROM",          mediaSetCDROM           },
99     { "mediaSetFloppy",         mediaSetFloppy          },
100     { "mediaSetUSB",            mediaSetUSB             },
101     { "mediaSetDOS",            mediaSetDOS             },
102     { "mediaSetFTP",            mediaSetFTP             },
103     { "mediaSetFTPActive",      mediaSetFTPActive       },
104     { "mediaSetFTPPassive",     mediaSetFTPPassive      },
105     { "mediaSetHTTP",           mediaSetHTTP            },
106     { "mediaSetUFS",            mediaSetUFS             },
107     { "mediaSetNFS",            mediaSetNFS             },
108     { "mediaSetFTPUserPass",    mediaSetFTPUserPass     },
109     { "mediaSetCPIOVerbosity",  mediaSetCPIOVerbosity   },
110     { "mediaGetType",           mediaGetType            },
111     { "msgConfirm",             dispatch_msgConfirm     },
112     { "optionsEditor",          optionsEditor           },
113     { "packageAdd",             packageAdd              },
114     { "addGroup",               userAddGroup            },
115     { "addUser",                userAddUser             },
116     { "shutdown",               dispatch_shutdown       },
117     { "system",                 dispatch_systemExecute  },
118     { "dumpVariables",          dump_variables          },
119     { "tcpMenuSelect",          tcpMenuSelect           },
120     { NULL, NULL },
121 };
122
123 /*
124  * Helper routines for buffering data.
125  *
126  * We read an entire configuration into memory before executing it
127  * so that we are truely standalone and can do things like nuke the
128  * file or disk we're working on.
129  */
130
131 typedef struct command_buffer_ {
132     qelement    queue;
133     char *      string;
134 } command_buffer;
135
136 static void
137 dispatch_free_command(command_buffer *item)
138 {
139     if (item != NULL) {
140         REMQUE(item);
141         free(item->string);
142         item->string = NULL;
143     }
144
145     free(item);
146 }
147
148 static void
149 dispatch_free_all(qelement *head)
150 {
151     command_buffer *item;
152
153     while (!EMPTYQUE(*head)) {
154         item = (command_buffer *) head->q_forw;
155         dispatch_free_command(item);
156     }
157 }
158
159 static command_buffer *
160 dispatch_add_command(qelement *head, char *string)
161 {
162     command_buffer *new = NULL;
163
164     new = malloc(sizeof(command_buffer));
165
166     if (new != NULL) {
167
168         new->string = strdup(string);
169
170         /*
171          * We failed to copy `string'; clean up the allocated
172          * resources.
173          */
174         if (new->string == NULL) {
175             free(new);
176             new = NULL;
177         } else {
178             INSQUEUE(new, head->q_back);
179         }
180     }
181
182     return new;
183 }
184
185 /*
186  * Command processing
187  */
188
189 /* Just convenience */
190 static int
191 dispatch_shutdown(dialogMenuItem *unused)
192 {
193     systemShutdown(0);
194     return DITEM_FAILURE;
195 }
196
197 static int
198 dispatch_systemExecute(dialogMenuItem *unused)
199 {
200     char *cmd = variable_get(VAR_COMMAND);
201
202     if (cmd)
203         return systemExecute(cmd) ? DITEM_FAILURE : DITEM_SUCCESS;
204     else
205         msgDebug("_systemExecute: No command passed in `command' variable.\n");
206     return DITEM_FAILURE;
207 }
208
209 static int
210 dispatch_msgConfirm(dialogMenuItem *unused)
211 {
212     char *msg = variable_get(VAR_COMMAND);
213
214     if (msg) {
215         msgConfirm("%s", msg);
216         return DITEM_SUCCESS;
217     }
218
219     msgDebug("_msgConfirm: No message passed in `command' variable.\n");
220     return DITEM_FAILURE;
221 }
222
223 static int
224 dispatch_mediaOpen(dialogMenuItem *unused)
225 {
226     return mediaOpen();
227 }
228
229 static int
230 dispatch_mediaClose(dialogMenuItem *unused)
231 {
232     mediaClose();
233     return DITEM_SUCCESS;
234 }
235
236 static int
237 call_possible_resword(char *name, dialogMenuItem *value, int *status)
238 {
239     int i, rval;
240
241     rval = 0;
242     for (i = 0; resWords[i].name; i++) {
243         if (!strcmp(name, resWords[i].name)) {
244             *status = resWords[i].handler(value);
245             rval = 1;
246             break;
247         }
248     }
249     return rval;
250 }
251
252 /* For a given string, call it or spit out an undefined command diagnostic */
253 int
254 dispatchCommand(char *str)
255 {
256     int i;
257     char *cp;
258
259     if (!str || !*str) {
260         msgConfirm("Null or zero-length string passed to dispatchCommand");
261         return DITEM_FAILURE;
262     }
263
264     /* Fixup DOS abuse */
265     if ((cp = index(str, '\r')) != NULL)
266         *cp = '\0';
267
268     /* If it's got a `=' sign in there, assume it's a variable setting */
269     if (index(str, '=')) {
270         if (isDebug())
271             msgDebug("dispatch: setting variable `%s'\n", str);
272         variable_set(str, 0);
273         i = DITEM_SUCCESS;
274     }
275     else {
276         /* A command might be a pathname if it's encoded in argv[0], which
277            we also support */
278         if ((cp = rindex(str, '/')) != NULL)
279             str = cp + 1;
280         if (isDebug())
281             msgDebug("dispatch: calling resword `%s'\n", str);
282         if (!call_possible_resword(str, NULL, &i)) {
283             msgNotify("Warning: No such command ``%s''", str);
284             i = DITEM_FAILURE;
285         }
286         /*
287          * Allow a user to prefix a command with "noError" to cause
288          * us to ignore any errors for that one command.
289          */
290         if (i != DITEM_SUCCESS && variable_get(VAR_NO_ERROR))
291             i = DITEM_SUCCESS;
292         variable_unset(VAR_NO_ERROR);
293     }
294     return i;
295 }
296
297
298 /*
299  * File processing
300  */
301
302 static qelement *
303 dispatch_load_fp(FILE *fp)
304 {
305     qelement *head;
306     char buf[BUFSIZ], *cp;
307
308     head = malloc(sizeof(qelement));
309
310     if (!head)
311         return NULL;
312
313     INITQUE(*head);
314
315     while (fgets(buf, sizeof buf, fp)) {
316         /* Fix up DOS abuse */
317         if ((cp = index(buf, '\r')) != NULL)
318             *cp = '\0';
319         /* If it's got a new line, trim it */
320        if ((cp = index(buf, '\n')) != NULL)
321             *cp = '\0';
322         if (*buf == '\0' || *buf == '#')
323             continue;
324
325         if (!dispatch_add_command(head, buf))
326             return NULL;
327     }
328
329     return head;
330 }
331
332 static int
333 dispatch_execute(qelement *head)
334 {
335     int result = DITEM_SUCCESS;
336     command_buffer *item;
337     char *old_interactive;
338
339     if (!head)
340         return result | DITEM_FAILURE;
341
342     old_interactive = variable_get(VAR_NONINTERACTIVE);
343     if (old_interactive)
344          old_interactive = strdup(old_interactive);     /* save copy */
345
346     /* Hint to others that we're running from a script, should they care */
347     variable_set2(VAR_NONINTERACTIVE, "yes", 0);
348
349     while (!EMPTYQUE(*head)) {
350         item = (command_buffer *) head->q_forw;
351
352         if (DITEM_STATUS(dispatchCommand(item->string)) != DITEM_SUCCESS) {
353             msgConfirm("Command `%s' failed - rest of script aborted.\n",
354                        item->string);
355             result |= DITEM_FAILURE;
356             break;
357         }
358         dispatch_free_command(item);
359     }
360
361     dispatch_free_all(head);
362
363     if (!old_interactive)
364         variable_unset(VAR_NONINTERACTIVE);
365     else {
366         variable_set2(VAR_NONINTERACTIVE, old_interactive, 0);
367         free(old_interactive);
368     }
369
370     return result;
371 }
372
373 int
374 dispatch_load_file_int(int quiet)
375 {
376     FILE *fp;
377     char *cp;
378     int  i;
379     qelement *list;
380
381     static const char *names[] = {
382         "install.cfg",
383         "/stand/install.cfg",
384         "/tmp/install.cfg",
385         NULL
386     };
387
388     fp = NULL;
389     cp = variable_get(VAR_CONFIG_FILE);
390     if (!cp) {
391         for (i = 0; names[i]; i++)
392             if ((fp = fopen(names[i], "r")) != NULL)
393                 break;
394     } else
395         fp = fopen(cp, "r");
396
397     if (!fp) {
398         if (!quiet)
399             msgConfirm("Unable to open %s: %s", cp, strerror(errno));
400         return DITEM_FAILURE;
401     }
402
403     list = dispatch_load_fp(fp);
404     fclose(fp);
405
406     return dispatch_execute(list);
407 }
408
409 int
410 dispatch_load_file(dialogMenuItem *self)
411 {
412     return dispatch_load_file_int(FALSE);
413 }
414
415 int
416 dispatch_load_floppy(dialogMenuItem *self)
417 {
418     int             what = DITEM_SUCCESS;
419     extern char    *distWanted;
420     char           *cp;
421     FILE           *fp;
422     qelement       *list;
423
424     mediaClose();
425     cp = variable_get_value(VAR_INSTALL_CFG,
426                             "Specify the name of a configuration file", 0);
427     if (!cp || !*cp) {
428         variable_unset(VAR_INSTALL_CFG);
429         what |= DITEM_FAILURE;
430         return what;
431     }
432
433     distWanted = cp;
434     /* Try to open the floppy drive */
435     if (DITEM_STATUS(mediaSetFloppy(NULL)) == DITEM_FAILURE) {
436         msgConfirm("Unable to set media device to floppy.");
437         what |= DITEM_FAILURE;
438         mediaClose();
439         return what;
440     }
441
442     if (!DEVICE_INIT(mediaDevice)) {
443         msgConfirm("Unable to mount floppy filesystem.");
444         what |= DITEM_FAILURE;
445         mediaClose();
446         return what;
447     }
448
449     fp = DEVICE_GET(mediaDevice, cp, TRUE);
450     if (fp) {
451         list = dispatch_load_fp(fp);
452         fclose(fp);
453         mediaClose();
454
455         what |= dispatch_execute(list);
456     }
457     else {
458         if (!variable_get(VAR_NO_ERROR))
459             msgConfirm("Configuration file '%s' not found.", cp);
460         variable_unset(VAR_INSTALL_CFG);
461         what |= DITEM_FAILURE;
462         mediaClose();
463     }
464     return what;
465 }
466
467 int
468 dispatch_load_cdrom(dialogMenuItem *self)
469 {
470     int             what = DITEM_SUCCESS;
471     extern char    *distWanted;
472     char           *cp;
473     FILE           *fp;
474     qelement       *list;
475
476     mediaClose();
477     cp = variable_get_value(VAR_INSTALL_CFG,
478                             "Specify the name of a configuration file\n"
479                             "residing on the CDROM.", 0);
480     if (!cp || !*cp) {
481         variable_unset(VAR_INSTALL_CFG);
482         what |= DITEM_FAILURE;
483         return what;
484     }
485
486     distWanted = cp;
487     /* Try to open the floppy drive */
488     if (DITEM_STATUS(mediaSetCDROM(NULL)) == DITEM_FAILURE) {
489         msgConfirm("Unable to set media device to CDROM.");
490         what |= DITEM_FAILURE;
491         mediaClose();
492         return what;
493     }
494
495     if (!DEVICE_INIT(mediaDevice)) {
496         msgConfirm("Unable to CDROM filesystem.");
497         what |= DITEM_FAILURE;
498         mediaClose();
499         return what;
500     }
501
502     fp = DEVICE_GET(mediaDevice, cp, TRUE);
503     if (fp) {
504         list = dispatch_load_fp(fp);
505         fclose(fp);
506         mediaClose();
507
508         what |= dispatch_execute(list);
509     }
510     else {
511         if (!variable_get(VAR_NO_ERROR))
512             msgConfirm("Configuration file '%s' not found.", cp);
513         variable_unset(VAR_INSTALL_CFG);
514         what |= DITEM_FAILURE;
515         mediaClose();
516     }
517     return what;
518 }
519
520 /*
521  * Create a menu based on available disk devices
522  */
523 int
524 dispatch_load_menu(dialogMenuItem *self)
525 {
526     DMenu       *menu;
527     Device      **devlist;
528     char        *err;
529     int         what, i, j, msize, count;
530     DeviceType  dtypes[] = {DEVICE_TYPE_FLOPPY, DEVICE_TYPE_CDROM,
531         DEVICE_TYPE_DOS, DEVICE_TYPE_UFS, DEVICE_TYPE_USB};
532
533     fprintf(stderr, "dispatch_load_menu called\n");
534
535     msize = sizeof(DMenu) + (sizeof(dialogMenuItem) * 2);
536     count = 0;
537     err = NULL;
538     what = DITEM_SUCCESS;
539
540     if ((menu = malloc(msize)) == NULL) {
541         err = "Failed to allocate memory for menu";
542         goto errout;
543     }
544
545     bcopy(&MenuConfig, menu, sizeof(DMenu));
546
547     bzero(&menu->items[count], sizeof(menu->items[0]));
548     menu->items[count].prompt = strdup("X Exit");
549     menu->items[count].title = strdup("Exit this menu (returning to previous)");
550     menu->items[count].fire = dmenuExit;
551     count++;
552
553     for (i = 0; i < sizeof(dtypes) / sizeof(dtypes[0]); i++) {
554         if ((devlist = deviceFind(NULL, dtypes[i])) == NULL) {
555             fprintf(stderr, "No devices found for type %d\n", dtypes[i]);
556             continue;
557         }
558
559         for (j = 0; devlist[j] != NULL; j++) {
560             fprintf(stderr, "device type %d device name %s\n", dtypes[i], devlist[j]->name);
561             msize += sizeof(dialogMenuItem);
562             if ((menu = realloc(menu, msize)) == NULL) {
563                 err = "Failed to allocate memory for menu item";
564                 goto errout;
565             }
566
567             bzero(&menu->items[count], sizeof(menu->items[0]));
568             menu->items[count].fire = cfgModuleFire;
569
570             menu->items[count].prompt = strdup(devlist[j]->name);
571             menu->items[count].title = strdup(devlist[j]->description);
572             /* XXX: dialog(3) sucks */
573             menu->items[count].aux = (long)devlist[j];
574             count++;
575         }
576     }
577
578     menu->items[count].prompt = NULL;
579     menu->items[count].title = NULL;
580
581     dmenuOpenSimple(menu, FALSE);
582
583   errout:
584     for (i = 0; i < count; i++) {
585         free(menu->items[i].prompt);
586         free(menu->items[i].title);
587     }
588
589     free(menu);
590
591     if (err != NULL) {
592         what |= DITEM_FAILURE;
593         if (!variable_get(VAR_NO_ERROR))
594             msgConfirm(err);
595     }
596
597     return (what);
598
599 }
600
601 static int
602 cfgModuleFire(dialogMenuItem *self) {
603     Device      *d;
604     FILE        *fp;
605     int         what = DITEM_SUCCESS;
606     extern char *distWanted;
607     qelement    *list;
608     char        *cp;
609
610     d = (Device *)self->aux;
611
612     msgDebug("cfgModuleFire: User selected %s (%s)\n", self->prompt, d->devname);
613
614     mediaClose();
615
616     cp = variable_get_value(VAR_INSTALL_CFG,
617                             "Specify the name of a configuration file", 0);
618     if (!cp || !*cp) {
619         variable_unset(VAR_INSTALL_CFG);
620         what |= DITEM_FAILURE;
621         return what;
622     }
623
624     distWanted = cp;
625
626     mediaDevice = d;
627     if (!DEVICE_INIT(mediaDevice)) {
628         msgConfirm("Unable to mount filesystem.");
629         what |= DITEM_FAILURE;
630         mediaClose();
631         return what;
632     }
633     msgDebug("getting fp for %s\n", cp);
634
635     fp = DEVICE_GET(mediaDevice, cp, TRUE);
636     if (fp) {
637         msgDebug("opened OK, processing..\n");
638
639         list = dispatch_load_fp(fp);
640         fclose(fp);
641         mediaClose();
642
643         what |= dispatch_execute(list);
644     } else {
645         if (!variable_get(VAR_NO_ERROR))
646             msgConfirm("Configuration file '%s' not found.", cp);
647         variable_unset(VAR_INSTALL_CFG);
648         what |= DITEM_FAILURE;
649         mediaClose();
650     }
651
652     return(what);
653  }