2 * The new sysinstall program.
4 * This is probably the last program in the `sysinstall' line - the next
5 * generation being essentially a complete rewrite.
10 * Jordan Hubbard. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
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
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.
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
37 #include "sysinstall.h"
38 #include <sys/disklabel.h>
40 #include <sys/errno.h>
41 #include <sys/ioctl.h>
42 #include <sys/fcntl.h>
43 #include <sys/param.h>
46 #include <sys/mount.h>
48 static Chunk *chunk_list[MAX_CHUNKS];
50 static int rootdev_is_od;
54 chunk_compare(Chunk *c1, Chunk *c2)
62 else if (!c1->private_data && !c2->private_data)
64 else if (c1->private_data && !c2->private_data)
66 else if (!c1->private_data && c2->private_data)
69 return strcmp(((PartInfo *)(c1->private_data))->mountpoint, ((PartInfo *)(c2->private_data))->mountpoint);
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];
82 chunk_list[j] = chunk_list[j + 1];
83 chunk_list[j + 1] = tmp;
90 check_rootdev(Chunk **list, int n)
96 for (i = 0; i < n; i++) {
98 if (c->type == part && (c->flags & CHUNK_IS_ROOT)
99 && strncmp(c->disk->name, "od", 2) == 0)
111 mount_point(Chunk *c1)
113 if (c1->type == part && c1->subtype == FS_SWAP)
115 else if (c1->type == part || c1->type == fat)
116 return ((PartInfo *)c1->private_data)->mountpoint;
125 else if (c1->type == part) {
126 if (c1->subtype != FS_SWAP)
135 fstype_short(Chunk *c1)
137 if (c1->type == part) {
138 if (c1->subtype != FS_SWAP) {
139 if (rootdev_is_od == 0 && strncmp(c1->name, "od", 2) == 0)
147 else if (c1->type == fat) {
148 if (strncmp(c1->name, "od", 2) == 0)
159 if (c1->type == part && c1->subtype != FS_SWAP) {
160 if (rootdev_is_od == 0 && strncmp(c1->name, "od", 2) == 0)
162 else if (c1->flags & CHUNK_IS_ROOT)
171 configFstab(dialogMenuItem *self)
179 if (!RunningAsInit) {
180 if (file_readable("/etc/fstab"))
181 return DITEM_SUCCESS;
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!");
189 devs = deviceFind(NULL, DEVICE_TYPE_DISK);
191 msgConfirm("No disks found!");
192 return DITEM_FAILURE;
195 /* Record all the chunks */
197 for (i = 0; devs[i]; i++) {
198 if (!devs[i]->enabled)
200 disk = (Disk *)devs[i]->private;
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;
210 else if (c1->type == fat && c1->private_data)
211 chunk_list[nchunks++] = c1;
214 chunk_list[nchunks] = 0;
217 fstab = fopen("/etc/fstab", "w");
219 msgConfirm("Unable to create a new /etc/fstab file! Manual intervention\n"
220 "will be required.");
221 return DITEM_FAILURE;
224 check_rootdev(chunk_list, nchunks);
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]));
233 /* Now look for the CDROMs */
234 devs = deviceFind(NULL, DEVICE_TYPE_CDROM);
235 cnt = deviceCount(devs);
237 /* Write out the CDROM entries */
238 for (i = 0; i < cnt; i++) {
241 sprintf(cdname, "/cdrom%s", i ? itoa(i) : "");
243 msgConfirm("Unable to make mount point for: %s", cdname);
245 fprintf(fstab, "/dev/%s\t\t%s\tcd9660\tro,noauto\t0\t0\n", devs[i]->name, cdname);
248 /* And finally, a /proc. */
249 fprintf(fstab, "proc\t\t\t/proc\t\tprocfs\trw\t\t0\t0\n");
254 msgDebug("Wrote out /etc/fstab file\n");
255 return DITEM_SUCCESS;
258 /* Do the work of sucking in a config file.
259 * config is the filename to read in.
260 * lines is a fixed (max) sized array of char*
261 * returns number of lines read. line contents
262 * are malloc'd and must be freed by the caller.
265 readConfig(char *config, char **lines, int max)
271 fp = fopen(config, "r");
276 /* Read in the entire file */
277 for (i = 0; i < max; i++) {
278 if (!fgets(line, sizeof line, fp))
280 lines[nlines++] = strdup(line);
284 msgDebug("readConfig: Read %d lines from %s.\n", nlines, config);
288 #define MAX_LINES 2000 /* Some big number we're not likely to ever reach - I'm being really lazy here, I know */
291 readConfigFile(char *config, int marked)
293 char *lines[MAX_LINES], *cp, *cp2;
296 nlines = readConfig(config, lines, MAX_LINES);
300 for (i = 0; i < nlines; i++) {
301 /* Skip the comments & non-variable settings */
302 if (lines[i][0] == '#' || !(cp = index(lines[i], '='))) {
308 if ((cp2 = index(cp, '"')) || (cp2 = index(cp, '\047'))) {
310 cp2 = index(cp, *cp2);
312 /* If valid quotes, use it */
315 /* If we have a legit value, set it */
317 variable_set2(lines[i], cp, marked);
323 /* Load the environment from rc.conf file(s) */
325 configEnvironmentRC_conf(void)
331 { "/etc/defaults/rc.conf", 0 },
332 { "/etc/rc.conf", 0 },
333 { "/etc/rc.conf.local", 0 },
338 for (i = 0; configs[i].fname; i++) {
339 if (file_readable(configs[i].fname))
340 readConfigFile(configs[i].fname, configs[i].marked);
344 /* Load the environment from a resolv.conf file */
346 configEnvironmentResolv(char *config)
348 char *lines[MAX_LINES];
351 nlines = readConfig(config, lines, MAX_LINES);
354 for (i = 0; i < nlines; i++) {
355 Boolean name_set = (Boolean)variable_get(VAR_NAMESERVER);
357 if (!strncmp(lines[i], "domain", 6) && !variable_get(VAR_DOMAINNAME))
358 variable_set2(VAR_DOMAINNAME, string_skipwhite(string_prune(lines[i] + 6)), 0);
359 else if (!name_set && !strncmp(lines[i], "nameserver", 10)) {
360 /* Only take the first nameserver setting - we're lame */
361 variable_set2(VAR_NAMESERVER, string_skipwhite(string_prune(lines[i] + 10)), 0);
367 /* Version of below for dispatch routines */
369 configRC(dialogMenuItem *unused)
372 return DITEM_SUCCESS;
381 static int did_marker = 0;
383 write_header = !file_readable("/etc/rc.conf");
384 rcSite = fopen("/etc/rc.conf", "a");
388 fprintf(rcSite, "# This file now contains just the overrides from /etc/defaults/rc.conf\n");
389 fprintf(rcSite, "# please make all changes to this file.\n\n");
392 /* Now do variable substitutions */
393 for (v = VarHead; v; v = v->next) {
396 fprintf(rcSite, "# -- sysinstall generated deltas -- #\n");
399 fprintf(rcSite, "%s=\"%s\"\n", v->name, v->value);
407 configSaver(dialogMenuItem *self)
409 variable_set((char *)self->data, 1);
410 if (!variable_get(VAR_BLANKTIME))
411 variable_set2(VAR_BLANKTIME, "300", 1);
412 return DITEM_SUCCESS;
416 configSaverTimeout(dialogMenuItem *self)
418 return (variable_get_value(VAR_BLANKTIME,
419 "Enter time-out period in seconds for screen saver", 1) ?
420 DITEM_SUCCESS : DITEM_FAILURE);
424 configNTP(dialogMenuItem *self)
428 status = variable_get_value(VAR_NTPDATE_FLAGS,
429 "Enter the name of an NTP server", 1)
430 ? DITEM_SUCCESS : DITEM_FAILURE;
431 if (status == DITEM_SUCCESS) {
432 static char tmp[255];
434 snprintf(tmp, sizeof(tmp), "ntpdate_enable=YES,ntpdate_flags=%s",
435 variable_get(VAR_NTPDATE_FLAGS));
437 dmenuSetVariables(self);
443 configUsers(dialogMenuItem *self)
445 WINDOW *w = savescr();
447 dialog_clear_norefresh();
448 dmenuOpenSimple(&MenuUsermgmt, FALSE);
450 return DITEM_SUCCESS;
454 configLinux(dialogMenuItem *self)
456 WINDOW *w = savescr();
459 dialog_clear_norefresh();
460 variable_set2(VAR_LINUX_ENABLE, "YES", 1);
461 msgNotify("Installing Linux compatibility library...");
462 i = package_add("linux_base");
468 write_root_xprofile(char *str)
473 static char *flist[] = { /* take care of both xdm and startx */
476 "/usr/share/skel/dot.xinitrc",
477 "/usr/share/skel/dot.xsession",
482 for (cp = flist; *cp; cp++) {
483 fp = fopen(*cp, "w");
485 fwrite(str, 1, len, fp);
486 fchmod(fileno(fp), 0755);
495 char tmp[FILENAME_MAX];
497 snprintf(tmp, sizeof tmp, "/usr/X11R6/bin/%s", fname);
498 if (file_executable(tmp))
500 snprintf(tmp, sizeof tmp, "/usr/local/bin/%s", fname);
501 return file_executable(tmp);
505 configXDesktop(dialogMenuItem *self)
508 int ret = DITEM_SUCCESS;
509 WINDOW *w = savescr();
511 dialog_clear_norefresh();
512 if (!dmenuOpenSimple(&MenuXDesktops, FALSE) || !(desk = variable_get(VAR_DESKSTYLE))) {
514 return DITEM_FAILURE;
516 if (!strcmp(desk, "kde")) {
517 ret = package_add("kde");
518 if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("startkde"))
519 write_root_xprofile("exec startkde\n");
521 else if (!strcmp(desk, "gnome")) {
522 ret = package_add("gnomecore");
523 if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("gnome-session")) {
524 ret = package_add("afterstep");
525 if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("afterstep"))
526 write_root_xprofile("gnome-session &\nexec afterstep");
529 else if (!strcmp(desk, "enlightenment")) {
530 ret = package_add("gnomecore");
531 if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("gnome-session")) {
532 ret = package_add("enlightenment");
533 if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("enlightenment"))
534 write_root_xprofile("exec gnome-session\n");
537 else if (!strcmp(desk, "afterstep")) {
538 ret = package_add("afterstep");
539 if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("afterstep"))
540 write_root_xprofile("xterm &\nexec afterstep\n");
542 else if (!strcmp(desk, "windowmaker")) {
543 ret = package_add("windowmaker");
544 if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("wmaker.inst")) {
545 write_root_xprofile("xterm &\n[ ! -d $HOME/GNUstep/Library/WindowMaker ] && /usr/X11R6/bin/wmaker.inst\nexec /usr/X11R6/bin/wmaker\n");
548 else if (!strcmp(desk, "fvwm2")) {
549 ret = package_add("fvwm");
550 if (DITEM_STATUS(ret) != DITEM_FAILURE && gotit("fvwm2"))
551 write_root_xprofile("xterm &\nexec fvwm2\n");
553 if (DITEM_STATUS(ret) == DITEM_FAILURE)
554 msgConfirm("An error occurred while adding the package(s) required\n"
555 "by this desktop type. Please change installation media\n"
556 "and/or select a different, perhaps simpler, desktop\n"
557 "environment and try again.");
563 configXSetup(dialogMenuItem *self)
565 char *config, *execfile, *style;
569 setenv("XWINHOME", "/usr/X11R6", 1);
572 variable_unset(VAR_DESKSTYLE);
573 variable_unset(VAR_XF86_CONFIG);
574 dialog_clear_norefresh();
575 if (!dmenuOpenSimple(&MenuXF86Config, FALSE)) {
577 return DITEM_FAILURE;
579 config = variable_get(VAR_XF86_CONFIG);
580 style = variable_get(VAR_DESKSTYLE);
586 return DITEM_FAILURE;
590 if (file_readable("/var/run/ld.so.hints"))
591 vsystem("/sbin/ldconfig -m /usr/lib /usr/X11R6/lib /usr/local/lib /usr/lib/compat");
593 vsystem("/sbin/ldconfig /usr/lib /usr/X11R6/lib /usr/local/lib /usr/lib/compat");
594 vsystem("/sbin/ldconfig -aout /usr/lib/compat/aout /usr/lib/aout /usr/X11R6/lib/aout /usr/local/lib/aout");
595 vsystem("/sbin/ifconfig lo0 127.0.0.1");
596 execfile = string_concat("/usr/X11R6/bin/", config);
597 if (file_executable(execfile)) {
598 moused = variable_get(VAR_MOUSED);
599 while (!moused || strcmp(moused, "YES")) {
600 if (msgYesNo("The X server may access the mouse in two ways: direct access\n"
601 "or indirect access via the mouse daemon. You have not\n"
602 "configured the mouse daemon. Would you like to configure it\n"
603 "now? If you intend to let the X server access the mouse\n"
604 "directly, choose \"No\" at this time."))
606 dialog_clear_norefresh();
607 dmenuOpenSimple(&MenuMouse, FALSE);
608 moused = variable_get(VAR_MOUSED);
610 if (moused && !strcmp(moused, "YES"))
611 msgConfirm("You have configured and are now running the mouse daemon.\n"
612 "Choose \"/dev/sysmouse\" as the mouse port and \"SysMouse\" or\n"
613 "\"MouseSystems\" as the mouse protocol in the X configuration\n"
615 systemExecute(execfile);
616 if (!file_readable("/etc/XF86Config")) {
617 if (!msgYesNo("The XFree86 configuration process seems to have\nfailed. Would you like to try again?"))
621 return DITEM_FAILURE;
625 configXDesktop(self);
627 return DITEM_SUCCESS;
630 msgConfirm("The XFree86 setup utility you chose does not appear to be installed!\n"
631 "Please install this before attempting to configure XFree86.");
633 return DITEM_FAILURE;
638 configResolv(dialogMenuItem *ditem)
643 cp = variable_get(VAR_NAMESERVER);
647 fp = fopen("/etc/resolv.conf", "w");
649 return DITEM_FAILURE;
650 if (variable_get(VAR_DOMAINNAME))
651 fprintf(fp, "domain\t%s\n", variable_get(VAR_DOMAINNAME));
652 fprintf(fp, "nameserver\t%s\n", cp);
655 msgDebug("Wrote out /etc/resolv.conf\n");
658 dp = variable_get(VAR_DOMAINNAME);
659 cp = variable_get(VAR_IPADDR);
660 hp = variable_get(VAR_HOSTNAME);
661 /* Tack ourselves into /etc/hosts */
662 fp = fopen("/etc/hosts", "w");
664 return DITEM_FAILURE;
665 /* Add an entry for localhost */
667 fprintf(fp, "127.0.0.1\t\tlocalhost.%s localhost\n", dp);
669 fprintf(fp, "127.0.0.1\t\tlocalhost\n");
670 /* Now the host entries, if applicable */
671 if (cp && cp[0] != '0' && hp) {
677 SAFE_STRCPY(cp2, hp);
678 *(index(cp2, '.')) = '\0';
680 fprintf(fp, "%s\t\t%s %s\n", cp, hp, cp2);
681 fprintf(fp, "%s\t\t%s.\n", cp, hp);
685 msgDebug("Wrote out /etc/hosts\n");
686 return DITEM_SUCCESS;
690 configRouter(dialogMenuItem *self)
694 ret = variable_get_value(VAR_ROUTER,
695 "Please specify the router you wish to use. Routed is\n"
696 "provided with the stock system and gated is provided\n"
697 "as an optional package which this installation system\n"
698 "will attempt to load if you select gated. Any other\n"
699 "choice of routing daemon will be assumed to be something\n"
700 "the user intends to install themselves before rebooting\n"
701 "the system. If you don't want any routing daemon, choose NO", 1)
702 ? DITEM_SUCCESS : DITEM_FAILURE;
704 if (ret == DITEM_SUCCESS) {
705 char *cp = variable_get(VAR_ROUTER);
707 if (cp && strcmp(cp, "NO")) {
708 variable_set2(VAR_ROUTER_ENABLE, "YES", 1);
709 if (!strcmp(cp, "gated")) {
710 if (package_add("gated") != DITEM_SUCCESS) {
711 msgConfirm("Unable to load gated package. Falling back to no router.");
712 variable_unset(VAR_ROUTER);
713 variable_unset(VAR_ROUTERFLAGS);
714 variable_set2(VAR_ROUTER_ENABLE, "NO", 1);
719 /* Now get the flags, if they chose a router */
720 ret = variable_get_value(VAR_ROUTERFLAGS,
721 "Please Specify the routing daemon flags; if you're running routed\n"
722 "then -q is the right choice for nodes and -s for gateway hosts.\n", 1)
723 ? DITEM_SUCCESS : DITEM_FAILURE;
724 if (ret != DITEM_SUCCESS)
725 variable_unset(VAR_ROUTERFLAGS);
730 variable_set2(VAR_ROUTER_ENABLE, "NO", 1);
731 variable_unset(VAR_ROUTERFLAGS);
732 variable_unset(VAR_ROUTER);
738 /* Shared between us and index_initialize() */
739 extern PkgNode Top, Plist;
742 configPackages(dialogMenuItem *self)
747 /* Did we get an INDEX? */
748 i = index_initialize("packages/INDEX");
749 if (DITEM_STATUS(i) == DITEM_FAILURE)
753 int ret, pos, scroll;
755 /* Bring up the packages menu */
757 index_menu(&Top, &Top, &Plist, &pos, &scroll);
759 if (Plist.kids && Plist.kids->name) {
760 /* Now show the packing list menu */
762 ret = index_menu(&Plist, &Plist, NULL, &pos, &scroll);
763 if (ret & DITEM_LEAVE_MENU)
765 else if (DITEM_STATUS(ret) != DITEM_FAILURE) {
766 for (tmp = Plist.kids; tmp && tmp->name; tmp = tmp->next)
767 (void)index_extract(mediaDevice, &Top, tmp, FALSE);
772 msgConfirm("No packages were selected for extraction.");
778 PkgNodePtr tmp2 = tmp->next;
783 index_init(NULL, &Plist);
784 return DITEM_SUCCESS;
787 /* Load pcnfsd package */
789 configPCNFSD(dialogMenuItem *self)
793 ret = package_add("pcnfsd");
794 if (DITEM_STATUS(ret) == DITEM_SUCCESS) {
795 variable_set2(VAR_PCNFSD, "YES", 0);
796 variable_set2("mountd_flags", "-n", 1);
802 configNFSServer(dialogMenuItem *self)
806 /* If we're an NFS server, we need an exports file */
807 if (!file_readable("/etc/exports")) {
808 WINDOW *w = savescr();
810 if (file_readable("/etc/exports.disabled"))
811 vsystem("mv /etc/exports.disabled /etc/exports");
813 dialog_clear_norefresh();
814 msgConfirm("Operating as an NFS server means that you must first configure\n"
815 "an /etc/exports file to indicate which hosts are allowed certain\n"
816 "kinds of access to your local file systems.\n"
817 "Press [ENTER] now to invoke an editor on /etc/exports\n");
818 vsystem("echo '#The following examples export /usr to 3 machines named after ducks,' > /etc/exports");
819 vsystem("echo '#/home and all directories under it to machines named after dead rock stars' >> /etc/exports");
820 vsystem("echo '#and, finally, /a to 2 privileged machines allowed to write on it as root.' >> /etc/exports");
821 vsystem("echo '#/usr huey louie dewie' >> /etc/exports");
822 vsystem("echo '#/home -alldirs janice jimmy frank' >> /etc/exports");
823 vsystem("echo '#/a -maproot=0 bill albert' >> /etc/exports");
824 vsystem("echo '#' >> /etc/exports");
825 vsystem("echo '# You should replace these lines with your actual exported filesystems.' >> /etc/exports");
826 vsystem("echo >> /etc/exports");
827 sprintf(cmd, "%s /etc/exports", variable_get(VAR_EDITOR));
831 variable_set2(VAR_NFS_SERVER, "YES", 1);
834 else if (variable_get(VAR_NFS_SERVER)) { /* We want to turn it off again? */
835 vsystem("mv -f /etc/exports /etc/exports.disabled");
836 variable_unset(VAR_NFS_SERVER);
838 return DITEM_SUCCESS;