install.c revision 114979
1110827Sphantom/*
2110827Sphantom * The new sysinstall program.
3110827Sphantom *
4110827Sphantom * This is probably the last program in the `sysinstall' line - the next
5110827Sphantom * generation being essentially a complete rewrite.
6110827Sphantom *
7110827Sphantom * $FreeBSD: head/usr.sbin/sade/install.c 114979 2003-05-13 19:16:00Z jhb $
8110827Sphantom *
9110827Sphantom * Copyright (c) 1995
10110827Sphantom *	Jordan Hubbard.  All rights reserved.
11110827Sphantom *
12110827Sphantom * Redistribution and use in source and binary forms, with or without
13110827Sphantom * modification, are permitted provided that the following conditions
14110827Sphantom * are met:
15110827Sphantom * 1. Redistributions of source code must retain the above copyright
16110827Sphantom *    notice, this list of conditions and the following disclaimer,
17110827Sphantom *    verbatim and that no modifications are made prior to this
18110827Sphantom *    point in the file.
19110827Sphantom * 2. Redistributions in binary form must reproduce the above copyright
20110827Sphantom *    notice, this list of conditions and the following disclaimer in the
21110827Sphantom *    documentation and/or other materials provided with the distribution.
22110827Sphantom *
23110827Sphantom * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
24110827Sphantom * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25110827Sphantom * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26110827Sphantom * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
27110827Sphantom * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28110827Sphantom * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29110827Sphantom * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
30110827Sphantom * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31110827Sphantom * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32110827Sphantom * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33110827Sphantom * SUCH DAMAGE.
34124535Sru *
35110827Sphantom */
36110827Sphantom
37110827Sphantom#include "sysinstall.h"
38111447Sru#include <ctype.h>
39110827Sphantom#include <sys/disklabel.h>
40110827Sphantom#include <sys/errno.h>
41110827Sphantom#include <sys/ioctl.h>
42111447Sru#include <sys/fcntl.h>
43111447Sru#include <sys/wait.h>
44111447Sru#include <sys/uio.h>
45110827Sphantom#include <sys/param.h>
46110827Sphantom#define MSDOSFS
47110827Sphantom#include <sys/mount.h>
48110827Sphantom#include <ufs/ufs/ufsmount.h>
49110827Sphantom#include <fs/msdosfs/msdosfsmount.h>
50111447Sru#undef MSDOSFS
51111447Sru#include <sys/stat.h>
52111447Sru#include <sys/sysctl.h>
53111447Sru#include <limits.h>
54110827Sphantom#include <unistd.h>
55110827Sphantom#include <termios.h>
56110827Sphantom
57110827Sphantom/* Hack for rsaref package add, which displays interactive license.
58110827Sphantom * Used by package.c
59111447Sru */
60111447Sruint _interactiveHack;
61int FixItMode = 0;
62
63static void	create_termcap(void);
64static void	fixit_common(void);
65
66#define TERMCAP_FILE	"/usr/share/misc/termcap"
67
68static void	installConfigure(void);
69
70Boolean
71checkLabels(Boolean whinge, Chunk **rdev, Chunk **sdev, Chunk **udev, Chunk **vdev, Chunk **tdev, Chunk **hdev)
72{
73    Device **devs;
74    Boolean status;
75    Disk *disk;
76    Chunk *c1, *c2, *rootdev, *swapdev, *usrdev, *vardev, *tmpdev, *homedev;
77    int i;
78
79    /* Don't allow whinging if noWarn is set */
80    if (variable_get(VAR_NO_WARN))
81	whinge = FALSE;
82
83    status = TRUE;
84    if (rdev)
85	*rdev = NULL;
86    if (sdev)
87	*sdev = NULL;
88    if (udev)
89	*udev = NULL;
90    if (vdev)
91	*vdev = NULL;
92    if (tdev)
93	*tdev = NULL;
94    if (hdev)
95	*hdev = NULL;
96    rootdev = swapdev = usrdev = vardev = tmpdev = homedev = NULL;
97
98    /* We don't need to worry about root/usr/swap if we're already multiuser */
99    if (!RunningAsInit)
100	return status;
101
102    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
103    /* First verify that we have a root device */
104    for (i = 0; devs[i]; i++) {
105	if (!devs[i]->enabled)
106	    continue;
107	disk = (Disk *)devs[i]->private;
108	msgDebug("Scanning disk %s for root filesystem\n", disk->name);
109	if (!disk->chunks)
110	    msgFatal("No chunk list found for %s!", disk->name);
111	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
112	    if (c1->type == freebsd) {
113		for (c2 = c1->part; c2; c2 = c2->next) {
114		    if (c2->type == part && c2->subtype != FS_SWAP && c2->private_data) {
115			if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/")) {
116			    if (rootdev) {
117				if (whinge)
118				    msgConfirm("WARNING:  You have more than one root device set?!\n"
119					       "Using the first one found.");
120				continue;
121			    }
122			    else {
123				rootdev = c2;
124				if (isDebug())
125				    msgDebug("Found rootdev at %s!\n", rootdev->name);
126			    }
127			}
128			else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/usr")) {
129			    if (usrdev) {
130				if (whinge)
131				    msgConfirm("WARNING:  You have more than one /usr filesystem.\n"
132					       "Using the first one found.");
133				continue;
134			    }
135			    else {
136				usrdev = c2;
137				if (isDebug())
138				    msgDebug("Found usrdev at %s!\n", usrdev->name);
139			    }
140			}
141			else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/var")) {
142			    if (vardev) {
143				if (whinge)
144				    msgConfirm("WARNING:  You have more than one /var filesystem.\n"
145					       "Using the first one found.");
146				continue;
147			    }
148			    else {
149				vardev = c2;
150				if (isDebug())
151				    msgDebug("Found vardev at %s!\n", vardev->name);
152			    }
153			} else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/tmp")) {
154			    if (tmpdev) {
155				if (whinge)
156				    msgConfirm("WARNING:  You have more than one /tmp filesystem.\n"
157					       "Using the first one found.");
158				continue;
159			    }
160			    else {
161				tmpdev = c2;
162				if (isDebug())
163				    msgDebug("Found tmpdev at %s!\n", tmpdev->name);
164			    }
165			} else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/home")) {
166			    if (homedev) {
167				if (whinge)
168				    msgConfirm("WARNING:  You have more than one /home filesystem.\n"
169					       "Using the first one found.");
170				continue;
171			    }
172			    else {
173				homedev = c2;
174				if (isDebug())
175				    msgDebug("Found homedev at %s!\n", homedev->name);
176			    }
177			}
178		    }
179		}
180	    }
181	}
182    }
183
184    /* Now check for swap devices */
185    for (i = 0; devs[i]; i++) {
186	if (!devs[i]->enabled)
187	    continue;
188	disk = (Disk *)devs[i]->private;
189	msgDebug("Scanning disk %s for swap partitions\n", disk->name);
190	if (!disk->chunks)
191	    msgFatal("No chunk list found for %s!", disk->name);
192	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
193	    if (c1->type == freebsd) {
194		for (c2 = c1->part; c2; c2 = c2->next) {
195		    if (c2->type == part && c2->subtype == FS_SWAP && !swapdev) {
196			swapdev = c2;
197			if (isDebug())
198			    msgDebug("Found swapdev at %s!\n", swapdev->name);
199			break;
200		    }
201		}
202	    }
203	}
204    }
205
206    /* Copy our values over */
207    if (rdev)
208	*rdev = rootdev;
209    if (sdev)
210	*sdev = swapdev;
211    if (udev)
212	*udev = usrdev;
213    if (vdev)
214	*vdev = vardev;
215    if (tdev)
216	*tdev = tmpdev;
217    if (hdev)
218	*hdev = homedev;
219
220    if (!rootdev && whinge) {
221	msgConfirm("No root device found - you must label a partition as /\n"
222		   "in the label editor.");
223	status = FALSE;
224    }
225    if (!swapdev && whinge) {
226	msgConfirm("No swap devices found - you must create at least one\n"
227		   "swap partition.");
228	status = FALSE;
229    }
230    return status;
231}
232
233static int
234installInitial(void)
235{
236    static Boolean alreadyDone = FALSE;
237    int status = DITEM_SUCCESS;
238
239    if (alreadyDone)
240	return DITEM_SUCCESS;
241
242    if (!variable_get(DISK_LABELLED)) {
243	msgConfirm("You need to assign disk labels before you can proceed with\n"
244		   "the installation.");
245	return DITEM_FAILURE;
246    }
247    /* If it's labelled, assume it's also partitioned */
248    if (!variable_get(DISK_PARTITIONED))
249	variable_set2(DISK_PARTITIONED, "yes", 0);
250
251    /* If we refuse to proceed, bail. */
252    dialog_clear_norefresh();
253    if (!variable_get(VAR_NO_WARN)) {
254	if (msgYesNo(
255	    "Last Chance!  Are you SURE you want continue the installation?\n\n"
256	    "If you're running this on a disk with data you wish to save\n"
257	    "then WE STRONGLY ENCOURAGE YOU TO MAKE PROPER BACKUPS before\n"
258	    "proceeding!\n\n"
259	    "We can take no responsibility for lost disk contents!") != 0)
260	return DITEM_FAILURE;
261    }
262
263    if (DITEM_STATUS(diskLabelCommit(NULL)) != DITEM_SUCCESS) {
264	msgConfirm("Couldn't make filesystems properly.  Aborting.");
265	return DITEM_FAILURE;
266    }
267
268    if (!copySelf()) {
269	msgConfirm("installInitial: Couldn't clone the boot floppy onto the\n"
270		   "root file system.  Aborting!");
271	return DITEM_FAILURE;
272    }
273
274    if (!Restarting && chroot("/mnt") == -1) {
275	msgConfirm("installInitial: Unable to chroot to %s - this is bad!",
276		   "/mnt");
277	return DITEM_FAILURE;
278    }
279
280    chdir("/");
281    variable_set2(RUNNING_ON_ROOT, "yes", 0);
282
283    /* Configure various files in /etc */
284    if (DITEM_STATUS(configResolv(NULL)) == DITEM_FAILURE)
285	status = DITEM_FAILURE;
286    if (DITEM_STATUS(configFstab(NULL)) == DITEM_FAILURE)
287	status = DITEM_FAILURE;
288
289    /* stick a helpful shell over on the 4th VTY */
290    if (!variable_get(VAR_NO_HOLOSHELL))
291	systemCreateHoloshell();
292
293    alreadyDone = TRUE;
294    return status;
295}
296
297int
298installFixitHoloShell(dialogMenuItem *self)
299{
300    FixItMode = 1;
301    systemCreateHoloshell();
302    return DITEM_SUCCESS;
303    FixItMode = 0;
304}
305
306int
307installFixitCDROM(dialogMenuItem *self)
308{
309    struct stat sb;
310
311    if (!RunningAsInit)
312	return DITEM_SUCCESS;
313
314    variable_set2(SYSTEM_STATE, "fixit", 0);
315    (void)unlink("/mnt2");
316    (void)rmdir("/mnt2");
317
318    while (1) {
319	msgConfirm("Please insert a FreeBSD live filesystem CD/DVD and press return");
320	if (DITEM_STATUS(mediaSetCDROM(NULL)) != DITEM_SUCCESS
321	    || !DEVICE_INIT(mediaDevice)) {
322	    /* If we can't initialize it, it's probably not a FreeBSD CDROM so punt on it */
323	    mediaClose();
324	    if (msgYesNo("Unable to mount the disc - do you want to try again?") != 0)
325		return DITEM_FAILURE;
326	}
327	else
328	    break;
329    }
330
331    /* Since the fixit code expects everything to be in /mnt2, and the CDROM mounting stuff /dist, do
332     * a little kludge dance here..
333     */
334    if (symlink("/dist", "/mnt2")) {
335	msgConfirm("Unable to symlink /mnt2 to the disc mount point.  Please report this\n"
336		   "unexpected failure to freebsd-bugs@FreeBSD.org.");
337	return DITEM_FAILURE;
338    }
339
340    /*
341     * If /tmp points to /mnt2/tmp from a previous fixit floppy session, it's
342     * not very good for us if we point it to the CDROM now.  Rather make it
343     * a directory in the root MFS then.  Experienced admins will still be
344     * able to mount their disk's /tmp over this if they need.
345     */
346    if (lstat("/tmp", &sb) == 0 && (sb.st_mode & S_IFMT) == S_IFLNK)
347	(void)unlink("/tmp");
348    Mkdir("/tmp");
349
350    /*
351     * Since setuid binaries ignore LD_LIBRARY_PATH, we indeed need the
352     * ld.so.hints file.  Fortunately, it's fairly small (~ 3 KB).
353     */
354    if (!file_readable("/var/run/ld.so.hints")) {
355	Mkdir("/var/run");
356	if (vsystem("/mnt2/sbin/ldconfig -s /mnt2/usr/lib")) {
357	    msgConfirm("Warning: ldconfig could not create the ld.so hints file.\n"
358		       "Dynamic executables from the disc likely won't work.");
359	}
360    }
361
362    /* Yet more iggly hardcoded pathnames. */
363    Mkdir("/usr/libexec");
364    if (!file_readable("/usr/libexec/ld.so") && file_readable("/mnt2/usr/libexec/ld.so")) {
365	if (symlink("/mnt2/usr/libexec/ld.so", "/usr/libexec/ld.so"))
366	    msgDebug("Couldn't link to ld.so - not necessarily a problem for ELF\n");
367    }
368    if (!file_readable("/usr/libexec/ld-elf.so.1")) {
369	if (symlink("/mnt2/usr/libexec/ld-elf.so.1", "/usr/libexec/ld-elf.so.1")) {
370	    msgConfirm("Warning: could not create the symlink for ld-elf.so.1\n"
371		       "Dynamic executables from the disc likely won't work.");
372	}
373    }
374    /* optional nicety */
375    if (!file_readable("/usr/bin/vi"))
376	symlink("/mnt2/usr/bin/vi", "/usr/bin/vi");
377    fixit_common();
378    mediaClose();
379    msgConfirm("Please remove the FreeBSD fixit CDROM/DVD now.");
380    return DITEM_SUCCESS;
381}
382
383int
384installFixitFloppy(dialogMenuItem *self)
385{
386    struct ufs_args args;
387    extern char *distWanted;
388
389    if (!RunningAsInit)
390	return DITEM_SUCCESS;
391
392    /* Try to open the floppy drive */
393    if (DITEM_STATUS(mediaSetFloppy(NULL)) == DITEM_FAILURE || !mediaDevice) {
394	msgConfirm("Unable to set media device to floppy.");
395	mediaClose();
396	return DITEM_FAILURE;
397    }
398
399    memset(&args, 0, sizeof(args));
400    args.fspec = mediaDevice->devname;
401    mediaDevice->private = "/mnt2";
402    distWanted = NULL;
403    Mkdir("/mnt2");
404
405    variable_set2(SYSTEM_STATE, "fixit", 0);
406
407    while (1) {
408	if (!DEVICE_INIT(mediaDevice)) {
409	    if (msgYesNo("The attempt to mount the fixit floppy failed, bad floppy\n"
410			 "or unclean filesystem.  Do you want to try again?"))
411		return DITEM_FAILURE;
412	}
413	else
414	    break;
415    }
416    if (!directory_exists("/tmp"))
417	(void)symlink("/mnt2/tmp", "/tmp");
418    fixit_common();
419    mediaClose();
420    msgConfirm("Please remove the fixit floppy now.");
421    return DITEM_SUCCESS;
422}
423
424/*
425 * The common code for both fixit variants.
426 */
427static void
428fixit_common(void)
429{
430    pid_t child;
431    int waitstatus;
432
433    if (!directory_exists("/var/tmp/vi.recover")) {
434	if (DITEM_STATUS(Mkdir("/var/tmp/vi.recover")) != DITEM_SUCCESS) {
435	    msgConfirm("Warning:  Was unable to create a /var/tmp/vi.recover directory.\n"
436		       "vi will kvetch and moan about it as a result but should still\n"
437		       "be essentially usable.");
438	}
439    }
440    if (!directory_exists("/bin"))
441	(void)Mkdir("/bin");
442    (void)symlink("/stand/sh", "/bin/sh");
443    /* Link the /etc/ files */
444    if (DITEM_STATUS(Mkdir("/etc")) != DITEM_SUCCESS)
445	msgConfirm("Unable to create an /etc directory!  Things are weird on this floppy..");
446    else if ((symlink("/mnt2/etc/spwd.db", "/etc/spwd.db") == -1 && errno != EEXIST) ||
447	     (symlink("/mnt2/etc/protocols", "/etc/protocols") == -1 && errno != EEXIST) ||
448	     (symlink("/mnt2/etc/group", "/etc/group") == -1 && errno != EEXIST) ||
449	     (symlink("/mnt2/etc/services", "/etc/services") == -1 && errno != EEXIST))
450	msgConfirm("Couldn't symlink the /etc/ files!  I'm not sure I like this..");
451    if (!file_readable(TERMCAP_FILE))
452	create_termcap();
453    if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0)
454	systemSuspendDialog();	/* must be before the fork() */
455    if (!(child = fork())) {
456	int i, fd;
457	struct termios foo;
458	extern int login_tty(int);
459
460	ioctl(0, TIOCNOTTY, NULL);
461	for (i = getdtablesize(); i >= 0; --i)
462	    close(i);
463
464	if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0)
465	    fd = open("/dev/console", O_RDWR);
466	else
467	    fd = open("/dev/ttyv3", O_RDWR);
468	ioctl(0, TIOCSCTTY, &fd);
469	dup2(0, 1);
470	dup2(0, 2);
471	DebugFD = 2;
472	if (login_tty(fd) == -1)
473	    msgDebug("fixit: I can't set the controlling terminal.\n");
474
475	signal(SIGTTOU, SIG_IGN);
476	if (tcgetattr(0, &foo) != -1) {
477	    foo.c_cc[VERASE] = '\010';
478	    if (tcsetattr(0, TCSANOW, &foo) == -1)
479		msgDebug("fixit shell: Unable to set erase character.\n");
480	}
481	else
482	    msgDebug("fixit shell: Unable to get terminal attributes!\n");
483	setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin:/stand:"
484	       "/mnt2/stand:/mnt2/bin:/mnt2/sbin:/mnt2/usr/bin:/mnt2/usr/sbin", 1);
485	if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0) {
486	    printf("Waiting for fixit shell to exit.\n"
487		"When you are done, type ``exit'' to exit\n"
488		"the fixit shell and be returned here.\n\n");
489	    fflush(stdout);
490	}
491
492	/* use the .profile from the fixit medium */
493	setenv("HOME", "/mnt2", 1);
494	chdir("/mnt2");
495	execlp("sh", "-sh", (char *)0);
496	msgDebug("fixit shell: Failed to execute shell!\n");
497	_exit(1);;
498    }
499    else {
500	if (strcmp(variable_get(VAR_FIXIT_TTY), "standard") == 0) {
501	    dialog_clear_norefresh();
502	    msgNotify("Waiting for fixit shell to exit.  Go to VTY4 now by\n"
503		"typing ALT-F4.  When you are done, type ``exit'' to exit\n"
504		"the fixit shell and be returned here\n.");
505	}
506	(void)waitpid(child, &waitstatus, 0);
507	if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0)
508	    systemResumeDialog();
509    }
510    dialog_clear();
511}
512
513
514int
515installExpress(dialogMenuItem *self)
516{
517    int i;
518
519    dialog_clear_norefresh();
520    variable_set2(SYSTEM_STATE, "express", 0);
521#ifdef WITH_SLICES
522    if (DITEM_STATUS((i = diskPartitionEditor(self))) == DITEM_FAILURE)
523	return i;
524#endif
525
526    if (DITEM_STATUS((i = diskLabelEditor(self))) == DITEM_FAILURE)
527	return i;
528
529    if (DITEM_STATUS((i = installCommit(self))) == DITEM_SUCCESS) {
530	i |= DITEM_LEAVE_MENU;
531	/* Set default security level */
532	configSecurityModerate(NULL);
533
534	/* Give user the option of one last configuration spree */
535	installConfigure();
536    }
537    return i;
538}
539
540/* Standard mode installation */
541int
542installStandard(dialogMenuItem *self)
543{
544    int i, tries = 0;
545    Device **devs;
546
547    variable_set2(SYSTEM_STATE, "standard", 0);
548    dialog_clear_norefresh();
549#ifdef WITH_SLICES
550    msgConfirm("In the next menu, you will need to set up a DOS-style (\"fdisk\") partitioning\n"
551	       "scheme for your hard disk.  If you simply wish to devote all disk space\n"
552	       "to FreeBSD (overwriting anything else that might be on the disk(s) selected)\n"
553	       "then use the (A)ll command to select the default partitioning scheme followed\n"
554	       "by a (Q)uit.  If you wish to allocate only free space to FreeBSD, move to a\n"
555	       "partition marked \"unused\" and use the (C)reate command.");
556
557nodisks:
558    if (DITEM_STATUS(diskPartitionEditor(self)) == DITEM_FAILURE)
559	return DITEM_FAILURE;
560
561    if (diskGetSelectCount(&devs) <= 0 && tries < 3) {
562	msgConfirm("You need to select some disks to operate on!  Be sure to use SPACE\n"
563		   "instead of RETURN in the disk selection menu when selecting a disk.");
564	++tries;
565	goto nodisks;
566    }
567
568    msgConfirm("Now you need to create BSD partitions inside of the fdisk partition(s)\n"
569	       "just created.  If you have a reasonable amount of disk space (200MB or more)\n"
570	       "and don't have any special requirements, simply use the (A)uto command to\n"
571	       "allocate space automatically.  If you have more specific needs or just don't\n"
572	       "care for the layout chosen by (A)uto, press F1 for more information on\n"
573	       "manual layout.");
574#else
575    msgConfirm("First you need to create BSD partitions on the disk which you are\n"
576	       "installing to.  If you have a reasonable amount of disk space (200MB or more)\n"
577	       "and don't have any special requirements, simply use the (A)uto command to\n"
578	       "allocate space automatically.  If you have more specific needs or just don't\n"
579	       "care for the layout chosen by (A)uto, press F1 for more information on\n"
580	       "manual layout.");
581#endif
582
583    if (DITEM_STATUS(diskLabelEditor(self)) == DITEM_FAILURE)
584	return DITEM_FAILURE;
585
586    if (DITEM_STATUS((i = installCommit(self))) == DITEM_FAILURE) {
587	dialog_clear();
588	msgConfirm("Installation completed with some errors.  You may wish to\n"
589		   "scroll through the debugging messages on VTY1 with the\n"
590		   "scroll-lock feature.  You can also choose \"No\" at the next\n"
591		   "prompt and go back into the installation menus to retry\n"
592		   "whichever operations have failed.");
593	return i;
594
595    }
596    else {
597	dialog_clear();
598	msgConfirm("Congratulations!  You now have FreeBSD installed on your system.\n\n"
599		   "We will now move on to the final configuration questions.\n"
600		   "For any option you do not wish to configure, simply select\n"
601		   "No.\n\n"
602		   "If you wish to re-enter this utility after the system is up, you\n"
603		   "may do so by typing: /usr/sbin/sysinstall.");
604    }
605    if (mediaDevice->type != DEVICE_TYPE_FTP && mediaDevice->type != DEVICE_TYPE_NFS) {
606	if (!msgYesNo("Would you like to configure any Ethernet or SLIP/PPP network devices?")) {
607	    Device *tmp = tcpDeviceSelect();
608
609	    if (tmp && !((DevInfo *)tmp->private)->use_dhcp && !msgYesNo("Would you like to bring the %s interface up right now?", tmp->name))
610		if (!DEVICE_INIT(tmp))
611		    msgConfirm("Initialization of %s device failed.", tmp->name);
612	}
613	dialog_clear_norefresh();
614    }
615
616    if (!msgNoYes("Do you want this machine to function as a network gateway?"))
617	variable_set2("gateway_enable", "YES", 1);
618
619    dialog_clear_norefresh();
620    if (!msgNoYes("Do you want to configure inetd and the network services that it provides?"))
621        configInetd(self);
622
623    dialog_clear_norefresh();
624    if (!msgNoYes("Do you want to have anonymous FTP access to this machine?"))
625	configAnonFTP(self);
626
627    dialog_clear_norefresh();
628    if (!msgNoYes("Do you want to configure this machine as an NFS server?"))
629	configNFSServer(self);
630
631    dialog_clear_norefresh();
632    if (!msgNoYes("Do you want to configure this machine as an NFS client?"))
633	variable_set2("nfs_client_enable", "YES", 1);
634
635    if (!msgNoYes("Do you want to select a default security profile for\n"
636	         "this host (select No for \"moderate\" security)?"))
637	configSecurityProfile(self);
638    else
639	configSecurityModerate(self);
640
641#ifdef WITH_SYSCONS
642    dialog_clear_norefresh();
643    if (!msgNoYes("Would you like to customize your system console settings?"))
644	dmenuOpenSimple(&MenuSyscons, FALSE);
645#endif
646
647    dialog_clear_norefresh();
648    if (!msgYesNo("Would you like to set this machine's time zone now?"))
649	systemExecute("tzsetup");
650
651#ifdef WITH_LINUX
652    dialog_clear_norefresh();
653    if (!msgYesNo("Would you like to enable Linux binary compatibility?"))
654	(void)configLinux(self);
655#endif
656
657#ifdef __alpha__
658    dialog_clear_norefresh();
659    if (!msgYesNo("Would you like to enable OSF/1 binary compatibility?"))
660	(void)configOSF1(self);
661#endif
662
663#ifdef WITH_MICE
664    dialog_clear_norefresh();
665    if (!msgNoYes("Does this system have a PS/2, serial, or bus mouse?"))
666	dmenuOpenSimple(&MenuMouse, FALSE);
667#endif
668
669    /* Now would be a good time to checkpoint the configuration data */
670    configRC_conf();
671    sync();
672
673    if (directory_exists("/usr/X11R6")) {
674	dialog_clear_norefresh();
675	if (!msgYesNo("Would you like to configure your X server at this time?"))
676	    (void)configXSetup(self);
677    }
678
679    dialog_clear_norefresh();
680    if (!msgYesNo("The FreeBSD package collection is a collection of thousands of ready-to-run\n"
681		  "applications, from text editors to games to WEB servers and more.  Would you\n"
682		  "like to browse the collection now?")) {
683	(void)configPackages(self);
684    }
685
686    if (!msgYesNo("Would you like to add any initial user accounts to the system?\n"
687		  "Adding at least one account for yourself at this stage is suggested\n"
688		  "since working as the \"root\" user is dangerous (it is easy to do\n"
689		  "things which adversely affect the entire system)."))
690	(void)configUsers(self);
691
692    msgConfirm("Now you must set the system manager's password.\n"
693	       "This is the password you'll use to log in as \"root\".");
694    if (!systemExecute("passwd root"))
695	variable_set2("root_password", "YES", 0);
696
697    /* XXX Put whatever other nice configuration questions you'd like to ask the user here XXX */
698
699    /* Give user the option of one last configuration spree */
700    dialog_clear_norefresh();
701    installConfigure();
702    return DITEM_LEAVE_MENU;
703}
704
705/* The version of commit we call from the Install Custom menu */
706int
707installCustomCommit(dialogMenuItem *self)
708{
709    int i;
710
711    i = installCommit(self);
712    if (DITEM_STATUS(i) == DITEM_SUCCESS) {
713	/* Set default security level */
714	configSecurityModerate(NULL);
715
716	/* Give user the option of one last configuration spree */
717	installConfigure();
718	return i;
719    }
720    else
721	msgConfirm("The commit operation completed with errors.  Not\n"
722		   "updating /etc files.");
723    return i;
724}
725
726/*
727 * What happens when we finally decide to going ahead with the installation.
728 *
729 * This is broken into multiple stages so that the user can do a full
730 * installation but come back here again to load more distributions,
731 * perhaps from a different media type.  This would allow, for
732 * example, the user to load the majority of the system from CDROM and
733 * then use ftp to load just the CRYPTO dist.
734 */
735int
736installCommit(dialogMenuItem *self)
737{
738    int i;
739    char *str;
740
741    dialog_clear_norefresh();
742    if (!Dists)
743	distConfig(NULL);
744
745    if (!Dists) {
746	(void)dmenuOpenSimple(&MenuDistributions, FALSE);
747	/* select reasonable defaults if necessary */
748	if (!Dists)
749	    Dists = _DIST_USER;
750    }
751
752    if (!mediaVerify())
753	return DITEM_FAILURE;
754
755    str = variable_get(SYSTEM_STATE);
756    if (isDebug())
757	msgDebug("installCommit: System state is `%s'\n", str);
758
759    /* Installation stuff we wouldn't do to a running system */
760    if (RunningAsInit && DITEM_STATUS((i = installInitial())) == DITEM_FAILURE)
761	return i;
762
763try_media:
764    if (!DEVICE_INIT(mediaDevice)) {
765	if (!msgYesNo("Unable to initialize selected media. Would you like to\n"
766		      "adjust your media configuration and try again?")) {
767	    mediaDevice = NULL;
768	    if (!mediaVerify())
769		return DITEM_FAILURE;
770	    else
771		goto try_media;
772	}
773	else
774	    return DITEM_FAILURE;
775    }
776
777    /* Now go get it all */
778    i = distExtractAll(self);
779
780    /* When running as init, *now* it's safe to grab the rc.foo vars */
781    installEnvironment();
782
783    variable_set2(SYSTEM_STATE, DITEM_STATUS(i) == DITEM_FAILURE ? "error-install" : "full-install", 0);
784
785    return i;
786}
787
788static void
789installConfigure(void)
790{
791    /* Final menu of last resort */
792    if (!msgNoYes("Visit the general configuration menu for a chance to set\n"
793		  "any last options?"))
794	dmenuOpenSimple(&MenuConfigure, FALSE);
795    configRC_conf();
796    sync();
797}
798
799int
800installFixupBase(dialogMenuItem *self)
801{
802    Device **devs;
803    char *cp;
804    int i;
805    FILE *fp;
806    int kstat = 1;
807
808    /* All of this is done only as init, just to be safe */
809    if (RunningAsInit) {
810#ifdef __i386__
811	if ((fp = fopen("/boot/loader.conf", "a")) != NULL) {
812	    if (!kstat || !OnVTY)
813		fprintf(fp, "# -- sysinstall generated deltas -- #\n");
814	    if (!kstat)
815		fprintf(fp, "userconfig_script_load=\"YES\"\n");
816	    if (!OnVTY)
817		fprintf(fp, "console=\"comconsole\"\n");
818	    fclose(fp);
819	}
820#endif
821
822	/* BOGON #2: We leave /etc in a bad state */
823	chmod("/etc", 0755);
824
825	/* BOGON #3: No /var/db/mountdtab complains */
826	Mkdir("/var/db");
827	creat("/var/db/mountdtab", 0644);
828
829	/* BOGON #4: /compat created by default in root fs */
830	Mkdir("/usr/compat");
831	vsystem("ln -s usr/compat /compat");
832
833	/* BOGON #5: aliases database not build for bin */
834	vsystem("newaliases");
835
836	/* Now run all the mtree stuff to fix things up */
837        vsystem("mtree -deU -f /etc/mtree/BSD.root.dist -p /");
838        vsystem("mtree -deU -f /etc/mtree/BSD.var.dist -p /var");
839        vsystem("mtree -deU -f /etc/mtree/BSD.usr.dist -p /usr");
840
841	/* Do all the last ugly work-arounds here */
842    }
843    return DITEM_SUCCESS | DITEM_RESTORE;
844}
845
846#ifndef X_AS_PKG
847/* Fix side-effects from the the XFree86 installation */
848int
849installFixupXFree(dialogMenuItem *self)
850{
851    /* BOGON #1:  XFree86 requires various specialized fixups */
852    if (directory_exists("/usr/X11R6")) {
853	dialog_clear_norefresh();
854	msgNotify("Fixing permissions in XFree86 tree..");
855	vsystem("chmod -R a+r /usr/X11R6");
856	vsystem("find /usr/X11R6 -type d | xargs chmod a+x");
857
858	/* Also do bogus minimal package registration so ports don't whine */
859	if (file_readable("/usr/X11R6/lib/X11/pkgreg.tar.gz")) {
860	    dialog_clear_norefresh();
861	    msgNotify("Installing package metainfo..");
862	    vsystem("tar xpzf /usr/X11R6/lib/X11/pkgreg.tar.gz -C / && rm /usr/X11R6/lib/X11/pkgreg.tar.gz");
863	}
864    }
865    return DITEM_SUCCESS | DITEM_RESTORE;
866}
867#endif
868
869#define	QUEUE_YES	1
870#define	QUEUE_NO	0
871static int
872performNewfs(PartInfo *pi, char *dname, int queue)
873{
874	char buffer[LINE_MAX];
875
876	if (pi->do_newfs) {
877		switch(pi->newfs_type) {
878		case NEWFS_UFS:
879			snprintf(buffer, LINE_MAX, "%s %s %s %s %s",
880			    NEWFS_UFS_CMD,
881			    pi->newfs_data.newfs_ufs.softupdates ?  "-U" : "",
882			    pi->newfs_data.newfs_ufs.ufs1 ? "-O1" : "-O2",
883			    pi->newfs_data.newfs_ufs.user_options,
884			    dname);
885			break;
886
887		case NEWFS_MSDOS:
888			snprintf(buffer, LINE_MAX, "%s %s", NEWFS_MSDOS_CMD,
889			    dname);
890			break;
891
892		case NEWFS_CUSTOM:
893			snprintf(buffer, LINE_MAX, "%s %s",
894			    pi->newfs_data.newfs_custom.command, dname);
895			break;
896		}
897
898		if (queue == QUEUE_YES) {
899			command_shell_add(pi->mountpoint, buffer);
900			return (0);
901		} else
902			return (vsystem(buffer));
903	}
904	return (0);
905}
906
907/* Go newfs and/or mount all the filesystems we've been asked to */
908int
909installFilesystems(dialogMenuItem *self)
910{
911    int i;
912    Disk *disk;
913    Chunk *c1, *c2, *rootdev, *swapdev;
914    Device **devs;
915    PartInfo *root;
916    char dname[80];
917    Boolean upgrade = FALSE;
918#if defined(__ia64__)
919    char efi_bootdir[FILENAME_MAX];
920#endif
921
922    /* If we've already done this, bail out */
923    if (!variable_cmp(DISK_LABELLED, "written"))
924	return DITEM_SUCCESS;
925
926    upgrade = !variable_cmp(SYSTEM_STATE, "upgrade");
927    if (!checkLabels(TRUE, &rootdev, &swapdev, NULL, NULL, NULL, NULL))
928	return DITEM_FAILURE;
929
930    if (rootdev)
931	root = (PartInfo *)rootdev->private_data;
932    else
933	root = NULL;
934
935    command_clear();
936    if (swapdev && RunningAsInit) {
937	/* As the very first thing, try to get ourselves some swap space */
938	sprintf(dname, "/dev/%s", swapdev->name);
939	if (!Fake && !file_readable(dname)) {
940	    msgConfirm("Unable to find device node for %s in /dev!\n"
941		       "The creation of filesystems will be aborted.", dname);
942	    return DITEM_FAILURE;
943	}
944
945	if (!Fake) {
946	    if (!swapon(dname)) {
947		dialog_clear_norefresh();
948		msgNotify("Added %s as initial swap device", dname);
949	    }
950	    else {
951		msgConfirm("WARNING!  Unable to swap to %s: %s\n"
952			   "This may cause the installation to fail at some point\n"
953			   "if you don't have a lot of memory.", dname, strerror(errno));
954	    }
955	}
956    }
957
958    if (rootdev && RunningAsInit) {
959	/* Next, create and/or mount the root device */
960	sprintf(dname, "/dev/%s", rootdev->name);
961	if (!Fake && !file_readable(dname)) {
962	    msgConfirm("Unable to make device node for %s in /dev!\n"
963		       "The creation of filesystems will be aborted.", dname);
964	    return DITEM_FAILURE | DITEM_RESTORE;
965	}
966	if (strcmp(root->mountpoint, "/"))
967	    msgConfirm("Warning: %s is marked as a root partition but is mounted on %s", rootdev->name, root->mountpoint);
968
969	if (root->do_newfs && (!upgrade ||
970	    !msgNoYes("You are upgrading - are you SURE you want to newfs "
971	    "the root partition?"))) {
972	    int i;
973
974	    dialog_clear_norefresh();
975	    msgNotify("Making a new root filesystem on %s", dname);
976	    i = performNewfs(root, dname, QUEUE_NO);
977	    if (i) {
978		msgConfirm("Unable to make new root filesystem on %s!\n"
979			   "Command returned status %d", dname, i);
980		return DITEM_FAILURE | DITEM_RESTORE;
981	    }
982	}
983	else {
984	    if (!upgrade) {
985		msgConfirm("Warning:  Using existing root partition.  It will be assumed\n"
986			   "that you have the appropriate device entries already in /dev.");
987	    }
988	    dialog_clear_norefresh();
989	    msgNotify("Checking integrity of existing %s filesystem.", dname);
990	    i = vsystem("fsck_ffs -y %s", dname);
991	    if (i)
992		msgConfirm("Warning: fsck returned status of %d for %s.\n"
993			   "This partition may be unsafe to use.", i, dname);
994	}
995
996	/*
997	 * If soft updates was enabled in the editor but we didn't newfs,
998	 * use tunefs to update the soft updates flag on the file system.
999	 */
1000	if (!root->do_newfs && root->newfs_type == NEWFS_UFS &&
1001	    root->newfs_data.newfs_ufs.softupdates) {
1002		i = vsystem("tunefs -n enable %s", dname);
1003		if (i)
1004			msgConfirm("Warning: Unable to enable soft updates"
1005			    " for root file system on %s", dname);
1006	}
1007
1008	/* Switch to block device */
1009	sprintf(dname, "/dev/%s", rootdev->name);
1010	if (Mount("/mnt", dname)) {
1011	    msgConfirm("Unable to mount the root file system on %s!  Giving up.", dname);
1012	    return DITEM_FAILURE | DITEM_RESTORE;
1013	}
1014
1015	/* Mount devfs for other partitions to mount */
1016	Mkdir("/mnt/dev");
1017	if (!Fake) {
1018	    struct iovec iov[4];
1019
1020	    iov[0].iov_base = "fstype";
1021	    iov[0].iov_len = strlen(iov[0].iov_base) + 1;
1022	    iov[1].iov_base = "devfs";
1023	    iov[1].iov_len = strlen(iov[1].iov_base) + 1;
1024	    iov[2].iov_base = "fspath";
1025	    iov[2].iov_len = strlen(iov[2].iov_base) + 1;
1026	    iov[3].iov_base = "/mnt/dev";
1027	    iov[3].iov_len = strlen(iov[3].iov_base) + 1;
1028	    i = nmount(iov, 4, 0);
1029
1030	    if (i) {
1031		dialog_clear_norefresh();
1032		msgConfirm("Unable to mount DEVFS (error %d)", errno);
1033		return DITEM_FAILURE | DITEM_RESTORE;
1034	    }
1035	}
1036    }
1037
1038    /* Now buzz through the rest of the partitions and mount them too */
1039    devs = deviceFind(NULL, DEVICE_TYPE_DISK);
1040    for (i = 0; devs[i]; i++) {
1041	if (!devs[i]->enabled)
1042	    continue;
1043
1044	disk = (Disk *)devs[i]->private;
1045	if (!disk->chunks) {
1046	    msgConfirm("No chunk list found for %s!", disk->name);
1047	    return DITEM_FAILURE | DITEM_RESTORE;
1048	}
1049	for (c1 = disk->chunks->part; c1; c1 = c1->next) {
1050	    if (c1->type == freebsd) {
1051		for (c2 = c1->part; c2; c2 = c2->next) {
1052		    if (c2->type == part && c2->subtype != FS_SWAP && c2->private_data) {
1053			PartInfo *tmp = (PartInfo *)c2->private_data;
1054
1055			/* Already did root */
1056			if (c2 == rootdev)
1057			    continue;
1058
1059			sprintf(dname, "%s/dev/%s",
1060			    RunningAsInit ? "/mnt" : "", c2->name);
1061
1062			if (tmp->do_newfs && (!upgrade ||
1063			    !msgNoYes("You are upgrading - are you SURE you"
1064			    " want to newfs /dev/%s?", c2->name)))
1065				performNewfs(tmp, dname, QUEUE_YES);
1066			else
1067			    command_shell_add(tmp->mountpoint,
1068				"fsck_ffs -y %s/dev/%s", RunningAsInit ?
1069				"/mnt" : "", c2->name);
1070#if 0
1071			if (tmp->soft)
1072			    command_shell_add(tmp->mountpoint,
1073			    "tunefs -n enable %s/dev/%s", RunningAsInit ?
1074			    "/mnt" : "", c2->name);
1075#endif
1076			command_func_add(tmp->mountpoint, Mount, c2->name);
1077		    }
1078		    else if (c2->type == part && c2->subtype == FS_SWAP) {
1079			char fname[80];
1080			int i;
1081
1082			if (c2 == swapdev)
1083			    continue;
1084			sprintf(fname, "%s/dev/%s", RunningAsInit ? "/mnt" : "", c2->name);
1085			i = (Fake || swapon(fname));
1086			if (!i) {
1087			    dialog_clear_norefresh();
1088			    msgNotify("Added %s as an additional swap device", fname);
1089			}
1090			else {
1091			    msgConfirm("Unable to add %s as a swap device: %s", fname, strerror(errno));
1092			}
1093		    }
1094		}
1095	    }
1096	    else if (c1->type == fat && c1->private_data &&
1097		(root->do_newfs || upgrade)) {
1098		char name[FILENAME_MAX];
1099
1100		sprintf(name, "%s/%s", RunningAsInit ? "/mnt" : "", ((PartInfo *)c1->private_data)->mountpoint);
1101		Mkdir(name);
1102	    }
1103#if defined(__ia64__)
1104	    else if (c1->type == efi && c1->private_data) {
1105		char bootdir[FILENAME_MAX];
1106		PartInfo *pi = (PartInfo *)c1->private_data;
1107		char *p;
1108
1109		sprintf(dname, "%s/dev/%s", RunningAsInit ? "/mnt" : "",
1110		    c1->name);
1111
1112		if (pi->do_newfs && (!upgrade ||
1113		    !msgNoYes("You are upgrading - are you SURE you want to "
1114		    "newfs /dev/%s?", c1->name)))
1115			performNewfs(pi, dname, QUEUE_YES);
1116
1117		command_func_add(pi->mountpoint, Mount_msdosfs, c1->name);
1118
1119		/*
1120		 * Create a directory boot on the EFI filesystem and create a
1121		 * link boot on the root filesystem pointing to the one on the
1122		 * EFI filesystem. That way, we install the loader, kernel
1123		 * and modules on the EFI filesystem.
1124		 */
1125		sprintf(bootdir, "%s", RunningAsInit ? "/mnt" : "");
1126		sprintf(efi_bootdir, "%s/%s", bootdir, pi->mountpoint);
1127		strcat(bootdir, "/boot");
1128		strcat(efi_bootdir, "/boot");
1129		command_func_add(pi->mountpoint, Mkdir_command, efi_bootdir);
1130
1131		/* Make a relative link. */
1132		p = &efi_bootdir[(RunningAsInit) ? 4 : 0];
1133		while (*p == '/')
1134			p++;
1135		symlink(p, bootdir);
1136	    }
1137#endif
1138	}
1139    }
1140
1141    command_sort();
1142    command_execute();
1143    dialog_clear_norefresh();
1144    return DITEM_SUCCESS | DITEM_RESTORE;
1145}
1146
1147static char *
1148getRelname(void)
1149{
1150    static char buf[64];
1151    size_t sz = (sizeof buf) - 1;
1152
1153    if (sysctlbyname("kern.osrelease", buf, &sz, NULL, 0) != -1) {
1154	buf[sz] = '\0';
1155	return buf;
1156    }
1157    else
1158	return "<unknown>";
1159}
1160
1161/* Initialize various user-settable values to their defaults */
1162int
1163installVarDefaults(dialogMenuItem *self)
1164{
1165    char *cp;
1166
1167    /* Set default startup options */
1168    variable_set2(VAR_RELNAME,			getRelname(), 0);
1169    variable_set2(VAR_CPIO_VERBOSITY,		"high", 0);
1170    variable_set2(VAR_TAPE_BLOCKSIZE,		DEFAULT_TAPE_BLOCKSIZE, 0);
1171    variable_set2(VAR_INSTALL_ROOT,		"/", 0);
1172    variable_set2(VAR_INSTALL_CFG,		"install.cfg", 0);
1173    cp = getenv("EDITOR");
1174    if (!cp)
1175	cp = "/usr/bin/ee";
1176    variable_set2(VAR_EDITOR,			cp, 0);
1177    variable_set2(VAR_FTP_USER,			"ftp", 0);
1178    variable_set2(VAR_BROWSER_PACKAGE,		"links", 0);
1179    variable_set2(VAR_BROWSER_BINARY,		"/usr/local/bin/links", 0);
1180    variable_set2(VAR_FTP_STATE,		"passive", 0);
1181    variable_set2(VAR_NFS_SECURE,		"NO", -1);
1182    if (OnVTY)
1183	    variable_set2(VAR_FIXIT_TTY,		"standard", 0);
1184    else
1185	    variable_set2(VAR_FIXIT_TTY,		"serial", 0);
1186    variable_set2(VAR_PKG_TMPDIR,		"/var/tmp", 0);
1187    variable_set2(VAR_MEDIA_TIMEOUT,		itoa(MEDIA_TIMEOUT), 0);
1188    if (getpid() != 1)
1189	variable_set2(SYSTEM_STATE,		"update", 0);
1190    else
1191	variable_set2(SYSTEM_STATE,		"init", 0);
1192    variable_set2(VAR_NEWFS_ARGS,		"-b 16384 -f 2048", 0);
1193    variable_set2(VAR_CONSTERM,                 "NO", 0);
1194    return DITEM_SUCCESS;
1195}
1196
1197/* Load the environment up from various system configuration files */
1198void
1199installEnvironment(void)
1200{
1201    configEnvironmentRC_conf();
1202    if (file_readable("/etc/resolv.conf"))
1203	configEnvironmentResolv("/etc/resolv.conf");
1204}
1205
1206/* Copy the boot floppy contents into /stand */
1207Boolean
1208copySelf(void)
1209{
1210    int i;
1211
1212    if (file_readable("/boot.help"))
1213	vsystem("cp /boot.help /mnt");
1214    msgWeHaveOutput("Copying the boot floppy to /stand on root filesystem");
1215    i = vsystem("find -x /stand | cpio %s -pdum /mnt", cpioVerbosity());
1216    if (i) {
1217	msgConfirm("Copy returned error status of %d!", i);
1218	return FALSE;
1219    }
1220
1221    /* Copy the /etc files into their rightful place */
1222    if (vsystem("cd /mnt/stand; find etc | cpio %s -pdum /mnt", cpioVerbosity())) {
1223	msgConfirm("Couldn't copy up the /etc files!");
1224	return TRUE;
1225    }
1226    return TRUE;
1227}
1228
1229static void
1230create_termcap(void)
1231{
1232    FILE *fp;
1233
1234    const char *caps[] = {
1235	termcap_vt100, termcap_cons25, termcap_cons25_m, termcap_cons25r,
1236	termcap_cons25r_m, termcap_cons25l1, termcap_cons25l1_m,
1237	termcap_xterm, NULL,
1238    };
1239    const char **cp;
1240
1241    if (!file_readable(TERMCAP_FILE)) {
1242	Mkdir("/usr/share/misc");
1243	fp = fopen(TERMCAP_FILE, "w");
1244	if (!fp) {
1245	    msgConfirm("Unable to initialize termcap file. Some screen-oriented\nutilities may not work.");
1246	    return;
1247	}
1248	cp = caps;
1249	while (*cp)
1250	    fprintf(fp, "%s\n", *(cp++));
1251	fclose(fp);
1252    }
1253}
1254