]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - release/sysinstall/config.c
Correct misfeature with quoting, better error reporting.
[FreeBSD/FreeBSD.git] / release / sysinstall / config.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  * $Id: config.c,v 1.107 1998/03/09 15:00:43 jkh 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 <sys/disklabel.h>
39 #include <sys/wait.h>
40 #include <sys/errno.h>
41 #include <sys/ioctl.h>
42 #include <sys/fcntl.h>
43 #include <sys/param.h>
44 #include <sys/stat.h>
45 #include <unistd.h>
46 #include <sys/mount.h>
47
48 static Chunk *chunk_list[MAX_CHUNKS];
49 static int nchunks;
50 static int rootdev_is_od;
51
52 /* arg to sort */
53 static int
54 chunk_compare(Chunk *c1, Chunk *c2)
55 {
56     if (!c1 && !c2)
57         return 0;
58     else if (!c1 && c2)
59         return 1;
60     else if (c1 && !c2)
61         return -1;
62     else if (!c1->private_data && !c2->private_data)
63         return 0;
64     else if (c1->private_data && !c2->private_data)
65         return 1;
66     else if (!c1->private_data && c2->private_data)
67         return -1;
68     else
69         return strcmp(((PartInfo *)(c1->private_data))->mountpoint, ((PartInfo *)(c2->private_data))->mountpoint);
70 }
71
72 static void
73 chunk_sort(void)
74 {
75     int i, j;
76
77     for (i = 0; i < nchunks; i++) {
78         for (j = 0; j < nchunks; j++) {
79             if (chunk_compare(chunk_list[j], chunk_list[j + 1]) > 0) {
80                 Chunk *tmp = chunk_list[j];
81
82                 chunk_list[j] = chunk_list[j + 1];
83                 chunk_list[j + 1] = tmp;
84             }
85         }
86     }
87 }
88
89 static void
90 check_rootdev(Chunk **list, int n)
91 {
92         int i;
93         Chunk *c;
94
95         rootdev_is_od = 0;
96         for (i = 0; i < n; i++) {
97                 c = *list++;
98                 if (c->type == part && (c->flags & CHUNK_IS_ROOT)
99                     && strncmp(c->disk->name, "od", 2) == 0)
100                         rootdev_is_od = 1;
101         }
102 }
103
104 static char *
105 name_of(Chunk *c1)
106 {
107     return c1->name;
108 }
109
110 static char *
111 mount_point(Chunk *c1)
112 {
113     if (c1->type == part && c1->subtype == FS_SWAP)
114         return "none";
115     else if (c1->type == part || c1->type == fat)
116         return ((PartInfo *)c1->private_data)->mountpoint;
117     return "/bogus";
118 }
119
120 static char *
121 fstype(Chunk *c1)
122 {
123     if (c1->type == fat)
124         return "msdos";
125     else if (c1->type == part) {
126         if (c1->subtype != FS_SWAP)
127             return "ufs";
128         else
129             return "swap";
130     }
131     return "bogus";
132 }
133
134 static char *
135 fstype_short(Chunk *c1)
136 {
137     if (c1->type == part) {
138         if (c1->subtype != FS_SWAP) {
139             if (rootdev_is_od == 0 && strncmp(c1->name, "od", 2) == 0)
140                 return "rw,noauto";
141             else
142                 return "rw";
143         }
144         else
145             return "sw";
146     }
147     else if (c1->type == fat) {
148         if (strncmp(c1->name, "od", 2) == 0)
149             return "ro,noauto";
150         else
151             return "ro";
152     }
153     return "bog";
154 }
155
156 static int
157 seq_num(Chunk *c1)
158 {
159     if (c1->type == part && c1->subtype != FS_SWAP) {
160         if (rootdev_is_od == 0 && strncmp(c1->name, "od", 2) == 0)
161             return 0;
162         else if (c1->flags & CHUNK_IS_ROOT)
163             return 1;
164         else
165             return 2;
166     }
167     return 0;
168 }
169
170 int
171 configFstab(void)
172 {
173     Device **devs;
174     Disk *disk;
175     FILE *fstab;
176     int i, cnt;
177     Chunk *c1, *c2;
178
179     if (!RunningAsInit) {
180         if (file_readable("/etc/fstab"))
181             return DITEM_SUCCESS;
182         else {
183             msgConfirm("Attempting to rebuild your /etc/fstab file.  Warning: If you had\n"
184                        "any CD devices in use before running sysinstall then they may NOT\n"
185                        "be found by this run!");
186         }
187     }
188
189     devs = deviceFind(NULL, DEVICE_TYPE_DISK);
190     if (!devs) {
191         msgConfirm("No disks found!");
192         return DITEM_FAILURE;
193     }
194
195     /* Record all the chunks */
196     nchunks = 0;
197     for (i = 0; devs[i]; i++) {
198         if (!devs[i]->enabled)
199             continue;
200         disk = (Disk *)devs[i]->private;
201         if (!disk->chunks)
202             msgFatal("No chunk list found for %s!", disk->name);
203         for (c1 = disk->chunks->part; c1; c1 = c1->next) {
204             if (c1->type == freebsd) {
205                 for (c2 = c1->part; c2; c2 = c2->next) {
206                     if (c2->type == part && (c2->subtype == FS_SWAP || c2->private_data))
207                         chunk_list[nchunks++] = c2;
208                 }
209             }
210             else if (c1->type == fat && c1->private_data)
211                 chunk_list[nchunks++] = c1;
212         }
213     }
214     chunk_list[nchunks] = 0;
215     chunk_sort();
216
217     fstab = fopen("/etc/fstab", "w");
218     if (!fstab) {
219         msgConfirm("Unable to create a new /etc/fstab file!  Manual intervention\n"
220                    "will be required.");
221         return DITEM_FAILURE;
222     }
223
224     check_rootdev(chunk_list, nchunks);
225
226     /* Go for the burn */
227     msgDebug("Generating /etc/fstab file\n");
228     fprintf(fstab, "# Device\t\tMountpoint\tFStype\tOptions\t\tDump\tPass#\n");
229     for (i = 0; i < nchunks; i++)
230         fprintf(fstab, "/dev/%s\t\t%s\t\t%s\t%s\t\t%d\t%d\n", name_of(chunk_list[i]), mount_point(chunk_list[i]),
231                 fstype(chunk_list[i]), fstype_short(chunk_list[i]), seq_num(chunk_list[i]), seq_num(chunk_list[i]));
232     Mkdir("/proc");
233     fprintf(fstab, "proc\t\t\t/proc\t\tprocfs\trw\t\t0\t0\n");
234
235     /* Now look for the CDROMs */
236     devs = deviceFind(NULL, DEVICE_TYPE_CDROM);
237     cnt = deviceCount(devs);
238
239     /* Write the first one out as /cdrom */
240     if (cnt) {
241         if (Mkdir("/cdrom")) {
242             msgConfirm("Unable to make mount point for: /cdrom");
243         }
244         else
245             fprintf(fstab, "/dev/%s\t\t/cdrom\t\tcd9660\tro,noauto\t0\t0\n", devs[0]->name);
246     }
247
248     /* Write the others out as /cdrom<n> */
249     for (i = 1; i < cnt; i++) {
250         char cdname[10];
251
252         sprintf(cdname, "/cdrom%d", i);
253         if (Mkdir(cdname)) {
254             msgConfirm("Unable to make mount point for: %s", cdname);
255         }
256         else
257             fprintf(fstab, "/dev/%s\t\t%s\tcd9660\tro,noauto\t0\t0\n", devs[i]->name, cdname);
258     }
259     fclose(fstab);
260     if (isDebug())
261         msgDebug("Wrote out /etc/fstab file\n");
262     return DITEM_SUCCESS;
263 }
264
265 /* Do the work of sucking in a config file.
266  * config is the filename to read in.
267  * lines is a fixed (max) sized array of char *.
268  * returns number of lines read.  line contents
269  * are malloc'd and must be freed by the caller.
270  */
271 int
272 readConfig(char *config, char **lines, int max)
273 {
274     FILE *fp;
275     char line[256];
276     int i, nlines;
277
278     fp = fopen(config, "r");
279     if (!fp)
280         return -1;
281
282     nlines = 0;
283     /* Read in the entire file */
284     for (i = 0; i < max; i++) {
285         if (!fgets(line, sizeof line, fp))
286             break;
287         lines[nlines++] = strdup(line);
288     }
289     fclose(fp);
290     if (isDebug())
291         msgDebug("readConfig: Read %d lines from %s.\n", nlines, config);
292     return nlines;
293 }
294
295 #define MAX_LINES  2000 /* Some big number we're not likely to ever reach - I'm being really lazy here, I know */
296
297 /* Load the environment from an rc.conf file */
298 void
299 configEnvironmentRC_conf(char *config)
300 {
301     char *lines[MAX_LINES], *cp, *cp2;
302     int i, nlines;
303
304     nlines = readConfig(config, lines, MAX_LINES);
305     if (nlines == -1)
306         return;
307
308     for (i = 0; i < nlines; i++) {
309         /* Skip the comments & non-variable settings */
310         if (lines[i][0] == '#' || !(cp = index(lines[i], '='))) {
311             free(lines[i]);
312             continue;
313         }
314         *cp++ = '\0';
315         /* Find quotes */
316         if ((cp2 = index(cp, '"')) || (cp2 = index(cp, '\047'))) {
317             cp = cp2 + 1;
318             cp2 = index(cp, *cp2);
319         }
320         /* If valid quotes, use it */
321         if (cp2) {
322             *cp2 = '\0';
323             /* If we have a legit value and it's not already set, set it */
324             if (strlen(cp) && !variable_get(lines[i]))
325                 variable_set2(lines[i], cp);
326         }
327         free(lines[i]);
328     }
329 }
330
331 /* Load the environment from a resolv.conf file */
332 void
333 configEnvironmentResolv(char *config)
334 {
335     char *lines[MAX_LINES];
336     int i, nlines;
337
338     nlines = readConfig(config, lines, MAX_LINES);
339     if (nlines == -1)
340         return;
341     for (i = 0; i < nlines; i++) {
342         Boolean name_set = (Boolean)variable_get(VAR_NAMESERVER);
343
344         if (!strncmp(lines[i], "domain", 6) && !variable_get(VAR_DOMAINNAME))
345             variable_set2(VAR_DOMAINNAME, string_skipwhite(string_prune(lines[i] + 6)));
346         else if (!strncmp(lines[i], "nameserver", 10) && !name_set) {
347             /* Only take the first nameserver setting - we're lame */
348             variable_set2(VAR_NAMESERVER, string_skipwhite(string_prune(lines[i] + 10)));
349             name_set = TRUE;
350         }
351         free(lines[i]);
352     }
353 }
354
355 /* Version of below for dispatch routines */
356 int
357 configRC(dialogMenuItem *unused)
358 {
359     configRC_conf("/etc/rc.conf");
360     return DITEM_SUCCESS;
361 }
362
363 /*
364  * This sucks in /etc/rc.conf, substitutes anything needing substitution, then
365  * writes it all back out.  It's pretty gross and needs re-writing at some point.
366  */
367 void
368 configRC_conf(char *config)
369 {
370     FILE *fp;
371     char *lines[MAX_LINES], *cp;
372     Variable *v;
373     int i, nlines, len;
374
375     nlines = readConfig(config, lines, MAX_LINES);
376     if (nlines == -1)
377         return;
378
379     /* Now do variable substitutions */
380     for (v = VarHead; v; v = v->next) {
381         for (i = 0; i < nlines; i++) {
382             /* Skip the comments & non-variable settings */
383             if (lines[i][0] == '#' || !(cp = index(lines[i], '=')))
384                 continue;
385             len = strlen(v->name);
386             if (!strncmp(lines[i], v->name, cp - lines[i]) && (cp - lines[i]) == len) {
387                 char *cp2, *comment = NULL;
388
389                 /* If trailing comment, try and preserve it */
390                 if ((index(lines[i], '#')) != NULL) {
391                     /* Find quotes */
392                     if ((cp2 = index(cp, '"')) || (cp2 = index(cp, '\047')))
393                         cp2 = index(cp2 + 1, *cp2);
394                     if (cp2 && strlen(cp2 + 1)) {
395                         comment = alloca(strlen(cp2));
396                         strcpy(comment, cp2 + 1);
397                     }
398                 }
399                 free(lines[i]);
400                 lines[i] = (char *)malloc(strlen(v->name) + strlen(v->value) + (comment ? strlen(comment) : 0) + 10);
401                 if (comment)
402                     sprintf(lines[i], "%s=\"%s\"%s", v->name, v->value, comment);
403                 else
404                     sprintf(lines[i], "%s=\"%s\"\n", v->name, v->value);
405             }
406         }
407     }
408
409     /* Now write it all back out again */
410     if (isDebug())
411         msgDebug("Writing configuration changes to %s file..", config);
412     if (Fake)
413         fp = fdopen(DebugFD, "w");
414     else {
415         (void)vsystem("cp %s %s.previous", config, config);
416         fp = fopen(config, "w");
417     }
418     for (i = 0; i < nlines; i++) {
419         fprintf(fp, lines[i]);
420         /* Stand by for bogus special case handling - we try to dump the interface specs here */
421         if (!strncmp(lines[i], VAR_INTERFACES, strlen(VAR_INTERFACES))) {
422             Device **devp;
423             int j, cnt;
424
425             devp = deviceFind(NULL, DEVICE_TYPE_NETWORK);
426             cnt = deviceCount(devp);
427             for (j = 0; j < cnt; j++) {
428                 char iname[255], toadd[512];
429                 int k, addit = TRUE;
430
431                 if (!strncmp(devp[j]->name, "ppp", 3) || !strncmp(devp[j]->name, "tun", 3))
432                     continue;
433
434                 snprintf(iname, 255, "%s%s", VAR_IFCONFIG, devp[j]->name);
435                 if ((cp = variable_get(iname))) {
436                     snprintf(toadd, sizeof toadd, "%s=\"%s\"\n", iname, cp);
437                     for (k = 0; k < nlines; k++) {
438                         if (!strcmp(lines[k], toadd)) {
439                             addit = FALSE;
440                             break;
441                         }
442                     }
443                     if (addit)
444                         fprintf(fp, toadd);
445                 }
446             }
447         }
448         free(lines[i]);
449     }
450     fclose(fp);
451 }
452
453 int
454 configSaver(dialogMenuItem *self)
455 {
456     variable_set((char *)self->data);
457     if (!variable_get(VAR_BLANKTIME))
458         variable_set2(VAR_BLANKTIME, "300");
459     return DITEM_SUCCESS;
460 }
461
462 int
463 configSaverTimeout(dialogMenuItem *self)
464 {
465     return (variable_get_value(VAR_BLANKTIME,
466             "Enter time-out period in seconds for screen saver") ?
467             DITEM_SUCCESS : DITEM_FAILURE) | DITEM_RESTORE;
468 }
469
470 int
471 configRegister(dialogMenuItem *self)
472 {
473     return DITEM_STATUS(registerOpenDialog()) | DITEM_RESTORE;
474 }
475
476 int
477 configNTP(dialogMenuItem *self)
478 {
479     int status;
480
481     status = variable_get_value(VAR_NTPDATE_FLAGS,
482                                 "Enter the name of an NTP server")
483              ? DITEM_SUCCESS : DITEM_FAILURE;
484     if (status == DITEM_SUCCESS) {
485         static char tmp[255];
486
487         snprintf(tmp, sizeof(tmp), "ntpdate_enable=YES,ntpdate_flags=%s",
488                  variable_get(VAR_NTPDATE_FLAGS));
489         self->data = tmp;
490         dmenuSetVariables(self);
491     }
492     return status | DITEM_RESTORE;
493 }
494
495 int
496 configUsers(dialogMenuItem *self)
497 {
498     dialog_clear_norefresh();
499     dmenuOpenSimple(&MenuUsermgmt, FALSE); 
500     dialog_clear();
501     return DITEM_SUCCESS | DITEM_RESTORE;
502 }
503
504 int
505 configXEnvironment(dialogMenuItem *self)
506 {
507     char *config, *execfile;
508     char *moused;
509
510     dialog_clear_norefresh();
511     if (!dmenuOpenSimple(&MenuXF86Config, FALSE))
512         return DITEM_FAILURE | DITEM_RESTORE;
513     systemExecute("/sbin/ldconfig /usr/lib /usr/X11R6/lib /usr/local/lib /usr/lib/compat");
514     config = variable_get(VAR_XF86_CONFIG);
515     if (!config)
516         return DITEM_FAILURE | DITEM_RESTORE;
517     execfile = string_concat("/usr/X11R6/bin/", config);
518     if (file_executable(execfile)) {
519         dialog_clear_norefresh();
520         moused = variable_get(VAR_MOUSED);
521         while (!moused || strcmp(moused, "YES")) {
522             if (msgYesNo("The X server may access the mouse in two ways: direct access\n"
523                          "or indirect access via the mouse daemon.  You have not\n"
524                          "configured the mouse daemon.  Would you like to configure it\n"
525                          "now?  If you intend to let the X server access the mouse\n"
526                          "directly, choose \"No\" at this time."))
527                 break;
528             dmenuOpenSimple(&MenuMouse, FALSE); 
529             dialog_clear();
530             moused = variable_get(VAR_MOUSED);
531         }
532         if (moused && !strcmp(moused, "YES"))
533             msgConfirm("You have configured and been running the mouse daemon.\n"
534                        "Choose \"/dev/sysmouse\" as the mouse port and \"SysMouse\" or\n"
535                        "\"MouseSystems\" as the mouse protocol in the X configuration\n"
536                        "utility.");
537         dialog_clear();
538         systemExecute(execfile);
539         return DITEM_SUCCESS | DITEM_RESTORE;
540     }
541     else {
542         dialog_clear_norefresh();
543         msgConfirm("XFree86 does not appear to be installed!  Please install\n"
544                    "The XFree86 distribution before attempting to configure it.");
545         return DITEM_FAILURE | DITEM_RESTORE;
546     }
547 }
548
549 void
550 configResolv(void)
551 {
552     FILE *fp;
553     char *cp, *dp, *hp;
554
555     cp = variable_get(VAR_NAMESERVER);
556     if (!cp || !*cp)
557         goto skip;
558     Mkdir("/etc");
559     fp = fopen("/etc/resolv.conf", "w");
560     if (!fp)
561         return;
562     if (variable_get(VAR_DOMAINNAME))
563         fprintf(fp, "domain\t%s\n", variable_get(VAR_DOMAINNAME));
564     fprintf(fp, "nameserver\t%s\n", cp);
565     fclose(fp);
566     if (isDebug())
567         msgDebug("Wrote out /etc/resolv.conf\n");
568
569 skip:
570     dp = variable_get(VAR_DOMAINNAME);
571     cp = variable_get(VAR_IPADDR);
572     hp = variable_get(VAR_HOSTNAME);
573     /* Tack ourselves into /etc/hosts */
574     fp = fopen("/etc/hosts", "w");
575     if (!fp)
576         return;
577     /* Add an entry for localhost */
578     fprintf(fp, "127.0.0.1\t\tlocalhost.%s localhost\n", dp ? dp : "my.domain");
579     /* Now the host entries, if applicable */
580     if (cp && cp[0] != '0' && hp) {
581         char cp2[255];
582
583         if (!index(hp, '.'))
584             cp2[0] = '\0';
585         else {
586             SAFE_STRCPY(cp2, hp);
587             *(index(cp2, '.')) = '\0';
588         }
589         fprintf(fp, "%s\t\t%s %s\n", cp, hp, cp2);
590         fprintf(fp, "%s\t\t%s.\n", cp, hp);
591     }
592     fclose(fp);
593     if (isDebug())
594         msgDebug("Wrote out /etc/hosts\n");
595 }
596
597 int
598 configRouter(dialogMenuItem *self)
599 {
600     int ret;
601
602     ret = variable_get_value(VAR_ROUTER,
603                              "Please specify the router you wish to use.  Routed is\n"
604                              "provided with the stock system and gated is provided\n"
605                              "as an optional package which this installation system\n"
606                              "will attempt to load if you select gated.  Any other\n"
607                              "choice of routing daemon will be assumed to be something\n"
608                              "the user intends to install themselves before rebooting\n"
609                              "the system.  If you don't want any routing daemon, choose NO")
610       ? DITEM_SUCCESS : DITEM_FAILURE;
611   
612     if (ret == DITEM_SUCCESS) {
613         char *cp = variable_get(VAR_ROUTER);
614     
615         if (cp && strcmp(cp, "NO")) {
616             variable_set2(VAR_ROUTER_ENABLE, "YES");
617             if (!strcmp(cp, "gated")) {
618                 if (package_add(variable_get(VAR_GATED_PKG)) != DITEM_SUCCESS) {
619                     msgConfirm("Unable to load gated package.  Falling back to no router.");
620                     variable_unset(VAR_ROUTER);
621                     variable_unset(VAR_ROUTERFLAGS);
622                     variable_set2(VAR_ROUTER_ENABLE, "NO");
623                     cp = NULL;
624                 }
625             }
626             if (cp) {
627                 /* Now get the flags, if they chose a router */
628                 ret = variable_get_value(VAR_ROUTERFLAGS, 
629                                          "Please Specify the routing daemon flags; if you're running routed\n"
630                                          "then -q is the right choice for nodes and -s for gateway hosts.\n")
631                   ? DITEM_SUCCESS : DITEM_FAILURE;
632                 if (ret != DITEM_SUCCESS)
633                     variable_unset(VAR_ROUTERFLAGS);
634             }
635         }
636         else {
637             /* No router case */
638             variable_set2(VAR_ROUTER_ENABLE, "NO");
639             variable_unset(VAR_ROUTERFLAGS);
640             variable_unset(VAR_ROUTER);
641         }
642     }
643     return ret | DITEM_RESTORE;
644 }
645
646 int
647 configPackages(dialogMenuItem *self)
648 {
649     static PkgNode top, plist;
650     static Boolean index_initted = FALSE;
651     PkgNodePtr tmp;
652     FILE *fp;
653
654     if (!mediaVerify())
655         return DITEM_FAILURE;
656
657     if (!mediaDevice->init(mediaDevice))
658         return DITEM_FAILURE;
659
660     if (!index_initted) {
661         msgNotify("Attempting to fetch packages/INDEX file from selected media.");
662         fp = mediaDevice->get(mediaDevice, "packages/INDEX", TRUE);
663         if (!fp) {
664             dialog_clear_norefresh();
665             msgConfirm("Unable to get packages/INDEX file from selected media.\n"
666                        "This may be because the packages collection is not available at\n"
667                        "on the distribution media you've chosen (most likely an FTP site\n"
668                        "without the packages collection mirrored).  Please verify media\n"
669                        "(or path to media) and try again.  If your local site does not\n"
670                        "carry the packages collection, then we recommend either a CD\n"
671                        "distribution or the master distribution on ftp.freebsd.org.");
672             mediaDevice->shutdown(mediaDevice);
673             return DITEM_FAILURE | DITEM_RESTORE;
674         }
675         msgNotify("Located INDEX, now reading package data from it...");
676         index_init(&top, &plist);
677         if (index_read(fp, &top)) {
678             msgConfirm("I/O or format error on packages/INDEX file.\n"
679                        "Please verify media (or path to media) and try again.");
680             fclose(fp);
681             return DITEM_FAILURE | DITEM_RESTORE;
682         }
683         fclose(fp);
684         index_sort(&top);
685         index_initted = TRUE;
686     }
687     while (1) {
688         int ret, pos, scroll;
689
690         /* Bring up the packages menu */
691         pos = scroll = 0;
692         index_menu(&top, &plist, &pos, &scroll);
693
694         if (plist.kids && plist.kids->name) {
695             /* Now show the packing list menu */
696             pos = scroll = 0;
697             ret = index_menu(&plist, NULL, &pos, &scroll);
698             if (ret & DITEM_LEAVE_MENU)
699                 break;
700             else if (DITEM_STATUS(ret) != DITEM_FAILURE) {
701                 index_extract(mediaDevice, &top, &plist);
702                 break;
703             }
704         }
705         else {
706             dialog_clear_norefresh();
707             msgConfirm("No packages were selected for extraction.");
708             break;
709         }
710     }
711     tmp = plist.kids;
712     while (tmp) {
713         PkgNodePtr tmp2 = tmp->next;
714            
715         safe_free(tmp);
716         tmp = tmp2;
717     }
718     index_init(NULL, &plist);
719     return DITEM_SUCCESS | DITEM_RESTORE;
720 }
721
722 #ifdef NETCON_EXTENTIONS
723 /* Load novell client/server package */
724 int
725 configNovell(dialogMenuItem *self)
726 {
727     int ret = DITEM_SUCCESS;
728
729     if (!RunningAsInit) {
730         msgConfirm("This package can only be installed in multi-user mode.");
731         return ret;
732     }
733     if (variable_get(VAR_NOVELL))
734         variable_unset(VAR_NOVELL);
735     else {
736         ret = package_add(PACKAGE_NETCON);
737         if (DITEM_STATUS(ret) == DITEM_SUCCESS)
738             variable_set2(VAR_NOVELL, "YES");
739     }
740     return ret | DITEM_RESTORE;
741 }
742 #endif
743
744 /* Load pcnfsd package */
745 int
746 configPCNFSD(dialogMenuItem *self)
747 {
748     int ret = DITEM_SUCCESS;
749
750     if (variable_get(VAR_PCNFSD))
751         variable_unset(VAR_PCNFSD);
752     else {
753         ret = package_add(variable_get(VAR_PCNFSD_PKG));
754         if (DITEM_STATUS(ret) == DITEM_SUCCESS) {
755             variable_set2(VAR_PCNFSD, "YES");
756             variable_set2("mountd_flags", "-n");
757         }
758     }
759     return ret;
760 }
761
762 int
763 configNFSServer(dialogMenuItem *self)
764 {
765     char cmd[256];
766
767     /* If we're an NFS server, we need an exports file */
768     if (!file_readable("/etc/exports")) {
769         WINDOW *w = savescr();
770
771         if (file_readable("/etc/exports.disabled"))
772             vsystem("mv /etc/exports.disabled /etc/exports");
773         else {
774             dialog_clear_norefresh();
775             msgConfirm("Operating as an NFS server means that you must first configure\n"
776                        "an /etc/exports file to indicate which hosts are allowed certain\n"
777                        "kinds of access to your local file systems.\n"
778                        "Press [ENTER] now to invoke an editor on /etc/exports\n");
779             vsystem("echo '#The following examples export /usr to 3 machines named after ducks,' > /etc/exports");
780             vsystem("echo '#/home and all directories under it to machines named after dead rock stars' >> /etc/exports");
781             vsystem("echo '#and, finally, /a to 2 privileged machines allowed to write on it as root.' >> /etc/exports");
782             vsystem("echo '#/usr                huey louie dewie' >> /etc/exports");
783             vsystem("echo '#/home   -alldirs    janice jimmy frank' >> /etc/exports");
784             vsystem("echo '#/a      -maproot=0  bill albert' >> /etc/exports");
785             vsystem("echo '#' >> /etc/exports");
786             vsystem("echo '# You should replace these lines with your actual exported filesystems.' >> /etc/exports");
787             vsystem("echo >> /etc/exports");
788             sprintf(cmd, "%s /etc/exports", variable_get(VAR_EDITOR));
789             dialog_clear();
790             systemExecute(cmd);
791             restorescr(w);
792         }
793         variable_set2(VAR_NFS_SERVER, "YES");
794     }
795     else if (variable_get(VAR_NFS_SERVER)) { /* We want to turn it off again? */
796         vsystem("mv -f /etc/exports /etc/exports.disabled");
797         variable_unset(VAR_NFS_SERVER);
798     }
799     return DITEM_SUCCESS;
800 }