Initial import from FreeBSD RELENG_4:
[dragonfly.git] / release / 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: src/release/sysinstall/dispatch.c,v 1.35.2.7 2002/07/02 21:37:49 jhb Exp $
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_mediaClose(dialogMenuItem *unused);
49
50 static struct _word {
51     char *name;
52     int (*handler)(dialogMenuItem *self);
53 } resWords[] = {
54     { "configAnonFTP",          configAnonFTP           },
55     { "configRouter",           configRouter            },
56     { "configInetd",            configInetd             },
57     { "configNFSServer",        configNFSServer         },
58     { "configNTP",              configNTP               },
59     { "configPCNFSD",           configPCNFSD            },
60     { "configPackages",         configPackages          },
61     { "configUsers",            configUsers             },
62     { "configXSetup",           configXSetup    },
63     { "configXDesktop",         configXDesktop  },
64     { "diskPartitionEditor",    diskPartitionEditor     },
65     { "diskPartitionWrite",     diskPartitionWrite      },
66     { "diskLabelEditor",        diskLabelEditor         },
67     { "diskLabelCommit",        diskLabelCommit         },
68     { "distReset",              distReset               },
69     { "distSetCustom",          distSetCustom           },
70     { "distUnsetCustom",        distUnsetCustom         },
71     { "distSetDeveloper",       distSetDeveloper        },
72     { "distSetXDeveloper",      distSetXDeveloper       },
73     { "distSetKernDeveloper",   distSetKernDeveloper    },
74     { "distSetUser",            distSetUser             },
75     { "distSetXUser",           distSetXUser            },
76     { "distSetMinimum",         distSetMinimum          },
77     { "distSetEverything",      distSetEverything       },
78     { "distSetSrc",             distSetSrc              },
79     { "distSetXF86",            distSetXF86             },
80     { "distExtractAll",         distExtractAll          },
81     { "docBrowser",             docBrowser              },
82     { "docShowDocument",        docShowDocument         },
83     { "installCommit",          installCommit           },
84     { "installExpress",         installExpress          },
85     { "installStandard",        installStandard         },
86     { "installUpgrade",         installUpgrade          },
87     { "installFixupBin",        installFixupBin         },
88 #ifndef X_AS_PKG
89     { "installFixupXFree",      installFixupXFree       },
90 #endif
91     { "installFixitHoloShell",  installFixitHoloShell   },
92     { "installFixitCDROM",      installFixitCDROM       },
93     { "installFixitFloppy",     installFixitFloppy      },
94     { "installFilesystems",     installFilesystems      },
95     { "installVarDefaults",     installVarDefaults      },
96     { "loadConfig",             dispatch_load_file      },
97     { "loadFloppyConfig",       dispatch_load_floppy    },
98     { "mediaClose",             dispatch_mediaClose     },
99     { "mediaSetCDROM",          mediaSetCDROM           },
100     { "mediaSetFloppy",         mediaSetFloppy          },
101     { "mediaSetDOS",            mediaSetDOS             },
102     { "mediaSetTape",           mediaSetTape            },
103     { "mediaSetFTP",            mediaSetFTP             },
104     { "mediaSetFTPActive",      mediaSetFTPActive       },
105     { "mediaSetFTPPassive",     mediaSetFTPPassive      },
106     { "mediaSetHTTP",           mediaSetHTTP            },
107     { "mediaSetUFS",            mediaSetUFS             },
108     { "mediaSetNFS",            mediaSetNFS             },
109     { "mediaSetFTPUserPass",    mediaSetFTPUserPass     },
110     { "mediaSetCPIOVerbosity",  mediaSetCPIOVerbosity   },
111     { "mediaGetType",           mediaGetType            },
112     { "msgConfirm",             dispatch_msgConfirm     },
113     { "optionsEditor",          optionsEditor           },
114     { "packageAdd",             packageAdd              },
115     { "addGroup",               userAddGroup            },
116     { "addUser",                userAddUser             },
117     { "shutdown",               dispatch_shutdown       },
118     { "system",                 dispatch_systemExecute  },
119     { "dumpVariables",          dump_variables          },
120     { "tcpMenuSelect",          tcpMenuSelect           },
121     { NULL, NULL },
122 };
123
124 /*
125  * Helper routines for buffering data.
126  *
127  * We read an entire configuration into memory before executing it
128  * so that we are truely standalone and can do things like nuke the
129  * file or disk we're working on.
130  */
131
132 typedef struct command_buffer_ {
133     qelement    queue;
134     char *      string;
135 } command_buffer;
136
137 static void
138 dispatch_free_command(command_buffer *item)
139 {
140     REMQUE(item);
141     free(item->string);
142     free(item);
143 }
144
145 static void
146 dispatch_free_all(qelement *head)
147 {
148     command_buffer *item;
149
150     while (!EMPTYQUE(*head)) {
151         item = (command_buffer *) head->q_forw;
152         dispatch_free_command(item);
153     }
154 }
155
156 static command_buffer *
157 dispatch_add_command(qelement *head, char *string)
158 {
159     command_buffer *new;
160
161     new = malloc(sizeof(command_buffer));
162
163     if (!new)
164         return NULL;
165
166     new->string = strdup(string);
167     INSQUEUE(new, head->q_back);
168
169     return new;
170 }
171 \f
172 /*
173  * Command processing
174  */
175
176 /* Just convenience */
177 static int
178 dispatch_shutdown(dialogMenuItem *unused)
179 {
180     systemShutdown(0);
181     return DITEM_FAILURE;
182 }
183
184 static int
185 dispatch_systemExecute(dialogMenuItem *unused)
186 {
187     char *cmd = variable_get(VAR_COMMAND);
188
189     if (cmd)
190         return systemExecute(cmd) ? DITEM_FAILURE : DITEM_SUCCESS;
191     else
192         msgDebug("_systemExecute: No command passed in `command' variable.\n");
193     return DITEM_FAILURE;
194 }
195
196 static int
197 dispatch_msgConfirm(dialogMenuItem *unused)
198 {
199     char *msg = variable_get(VAR_COMMAND);
200
201     if (msg) {
202         msgConfirm("%s", msg);
203         return DITEM_SUCCESS;
204     }
205
206     msgDebug("_msgConfirm: No message passed in `command' variable.\n");
207     return DITEM_FAILURE;
208 }
209
210 static int
211 dispatch_mediaClose(dialogMenuItem *unused)
212 {
213     mediaClose();
214     return DITEM_SUCCESS;
215 }
216
217 static int
218 call_possible_resword(char *name, dialogMenuItem *value, int *status)
219 {
220     int i, rval;
221
222     rval = 0;
223     for (i = 0; resWords[i].name; i++) {
224         if (!strcmp(name, resWords[i].name)) {
225             *status = resWords[i].handler(value);
226             rval = 1;
227             break;
228         }
229     }
230     return rval;
231 }
232
233 /* For a given string, call it or spit out an undefined command diagnostic */
234 int
235 dispatchCommand(char *str)
236 {
237     int i;
238     char *cp;
239
240     if (!str || !*str) {
241         msgConfirm("Null or zero-length string passed to dispatchCommand");
242         return DITEM_FAILURE;
243     }
244     /* If it's got a newline, trim it */
245     if ((cp = index(str, '\n')) != NULL)
246         *cp = '\0';
247
248     /* If it's got a `=' sign in there, assume it's a variable setting */
249     if (index(str, '=')) {
250         if (isDebug())
251             msgDebug("dispatch: setting variable `%s'\n", str);
252         variable_set(str, 0);
253         i = DITEM_SUCCESS;
254     }
255     else {
256         /* A command might be a pathname if it's encoded in argv[0], which
257            we also support */
258         if ((cp = rindex(str, '/')) != NULL)
259             str = cp + 1;
260         if (isDebug())
261             msgDebug("dispatch: calling resword `%s'\n", str);
262         if (!call_possible_resword(str, NULL, &i)) {
263             msgNotify("Warning: No such command ``%s''", str);
264             i = DITEM_FAILURE;
265         }
266         /*
267          * Allow a user to prefix a command with "noError" to cause
268          * us to ignore any errors for that one command.
269          */
270         if (i != DITEM_SUCCESS && variable_get(VAR_NO_ERROR))
271             i = DITEM_SUCCESS;
272         variable_unset(VAR_NO_ERROR);
273     }
274     return i;
275 }
276
277 \f
278 /*
279  * File processing
280  */
281
282 static qelement *
283 dispatch_load_fp(FILE *fp)
284 {
285     qelement *head;
286     char buf[BUFSIZ], *cp;
287
288     head = malloc(sizeof(qelement));
289
290     if (!head)
291         return NULL;
292
293     INITQUE(*head);
294
295     while (fgets(buf, sizeof buf, fp)) {
296
297         if ((cp = strchr(buf, '\n')) != NULL)
298             *cp = '\0';
299         if (*buf == '\0' || *buf == '#')
300             continue;
301
302         if (!dispatch_add_command(head, buf))
303             return NULL;
304     }
305
306     return head;
307 }
308
309 static int
310 dispatch_execute(qelement *head)
311 {
312     int result = DITEM_SUCCESS;
313     command_buffer *item;
314     char *old_interactive;
315
316     if (!head)
317         return result | DITEM_FAILURE;
318
319     old_interactive = variable_get(VAR_NONINTERACTIVE);
320     if (old_interactive)
321          old_interactive = strdup(old_interactive);     /* save copy */
322
323     /* Hint to others that we're running from a script, should they care */
324     variable_set2(VAR_NONINTERACTIVE, "yes", 0);
325
326     while (!EMPTYQUE(*head)) {
327         item = (command_buffer *) head->q_forw;
328         
329         if (DITEM_STATUS(dispatchCommand(item->string)) != DITEM_SUCCESS) {
330             msgConfirm("Command `%s' failed - rest of script aborted.\n",
331                        item->string);
332             result |= DITEM_FAILURE;
333             break;
334         }
335         dispatch_free_command(item);
336     }
337
338     dispatch_free_all(head);
339
340     if (!old_interactive)
341         variable_unset(VAR_NONINTERACTIVE);
342     else {
343         variable_set2(VAR_NONINTERACTIVE, old_interactive, 0);
344         free(old_interactive);
345     }
346
347     return result;
348 }
349
350 int
351 dispatch_load_file_int(int quiet)
352 {
353     FILE *fp;
354     char *cp;
355     int  i;
356     qelement *list;
357
358     static const char *names[] = {
359         "install.cfg",
360         "/stand/install.cfg",
361         "/tmp/install.cfg",
362         NULL
363     };
364
365     fp = NULL;
366     cp = variable_get(VAR_CONFIG_FILE);
367     if (!cp) {
368         for (i = 0; names[i]; i++)
369             if ((fp = fopen(names[i], "r")) != NULL)
370                 break;
371     } else
372         fp = fopen(cp, "r");
373
374     if (!fp) {
375         if (!quiet)
376             msgConfirm("Unable to open %s: %s", cp, strerror(errno));
377         return DITEM_FAILURE;
378     }
379
380     list = dispatch_load_fp(fp);
381     fclose(fp);
382
383     return dispatch_execute(list);
384 }
385
386 int
387 dispatch_load_file(dialogMenuItem *self)
388 {
389     return dispatch_load_file_int(FALSE);
390 }
391
392 int
393 dispatch_load_floppy(dialogMenuItem *self)
394 {
395     int             what = DITEM_SUCCESS;
396     extern char    *distWanted;
397     char           *cp;
398     FILE           *fp;
399     qelement       *list;
400
401     mediaClose();
402     cp = variable_get_value(VAR_INSTALL_CFG,
403                             "Specify the name of a configuration file\n"
404                             "residing on a MSDOS or UFS floppy.", 0);
405     if (!cp || !*cp) {
406         variable_unset(VAR_INSTALL_CFG);
407         what |= DITEM_FAILURE;
408         return what;
409     }
410
411     distWanted = cp;
412     /* Try to open the floppy drive */
413     if (DITEM_STATUS(mediaSetFloppy(NULL)) == DITEM_FAILURE) {
414         msgConfirm("Unable to set media device to floppy.");
415         what |= DITEM_FAILURE;
416         mediaClose();
417         return what;
418     }
419
420     if (!DEVICE_INIT(mediaDevice)) {
421         msgConfirm("Unable to mount floppy filesystem.");
422         what |= DITEM_FAILURE;
423         mediaClose();
424         return what;
425     }
426
427     fp = DEVICE_GET(mediaDevice, cp, TRUE);
428     if (fp) {
429         list = dispatch_load_fp(fp);
430         fclose(fp);
431         mediaClose();
432
433         what |= dispatch_execute(list);
434     }
435     else {
436         if (!variable_get(VAR_NO_ERROR))
437             msgConfirm("Configuration file '%s' not found.", cp);
438         variable_unset(VAR_INSTALL_CFG);
439         what |= DITEM_FAILURE;
440         mediaClose();
441     }
442     return what;
443 }
444