1/*
2 * The new sysinstall program.
3 *
4 * This is probably the last attempt in the `sysinstall' line, the next
5 * generation being slated to essentially a complete rewrite.
6 *
7 * $FreeBSD$
8 *
9 * Copyright (c) 1995
10 *	Jordan Hubbard.  All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer,
17 *    verbatim and that no modifications are made prior to this
18 *    point in the file.
19 * 2. Redistributions in binary form must reproduce the above copyright
20 *    notice, this list of conditions and the following disclaimer in the
21 *    documentation and/or other materials provided with the distribution.
22 *
23 * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 */
36
37#include "sysinstall.h"
38#include <signal.h>
39#include <netdb.h>
40#include <sys/socket.h>
41#include <sys/param.h>
42#include <sys/mount.h>
43#include <sys/errno.h>
44#include <sys/fcntl.h>
45#include <sys/stat.h>
46#include <sys/time.h>
47#include <sys/mman.h>
48#include <sys/wait.h>
49#include <netinet/in.h>
50#include <arpa/inet.h>
51#include <resolv.h>
52
53static Boolean got_intr = FALSE;
54static Boolean ftp_skip_resolve = FALSE;
55static Boolean http_skip_resolve = FALSE;
56
57/* timeout handler */
58static void
59handle_intr(int sig)
60{
61    msgDebug("User generated interrupt.\n");
62    got_intr = TRUE;
63}
64
65static int
66check_for_interrupt(void)
67{
68    if (got_intr) {
69	got_intr = FALSE;
70	return TRUE;
71    }
72    return FALSE;
73}
74
75static int
76genericHook(dialogMenuItem *self, DeviceType type)
77{
78    Device **devs;
79
80    devs = deviceFind(self->prompt, type);
81    if (devs)
82	mediaDevice = devs[0];
83    return (devs ? DITEM_LEAVE_MENU : DITEM_FAILURE);
84}
85
86static int
87cdromHook(dialogMenuItem *self)
88{
89    return genericHook(self, DEVICE_TYPE_CDROM);
90}
91
92static void
93kickstart_dns(void)
94{
95    static Boolean initted = FALSE;
96    int time;
97    char *cp;
98
99    cp = variable_get(VAR_MEDIA_TIMEOUT);
100    if (!cp)
101	time = MEDIA_TIMEOUT;
102    else
103	time = atoi(cp);
104    if (!time)
105	time = 100;
106    if (!initted) {
107	res_init();
108	_res.retry = 2;	/* 2 times seems a reasonable number to me */
109	_res.retrans = time / 2; /* so spend half our alloted time on each try */
110	initted = TRUE;
111    }
112}
113
114char *
115cpioVerbosity()
116{
117    char *cp = variable_get(VAR_CPIO_VERBOSITY);
118
119    if (cp && !strcmp(cp, "high"))
120	return "-v";
121    return "";
122}
123
124int
125mediaOpen(void)
126{
127    if (!mediaDevice || !mediaVerify() || !DEVICE_INIT(mediaDevice))
128	return DITEM_FAILURE;
129    return DITEM_SUCCESS;
130}
131
132void
133mediaClose(void)
134{
135    if (mediaDevice)
136	DEVICE_SHUTDOWN(mediaDevice);
137    mediaDevice = NULL;
138}
139
140/*
141 * Return 1 if we successfully found and set the installation type to
142 * be a CD.
143 */
144int
145mediaSetCDROM(dialogMenuItem *self)
146{
147    Device **devs;
148    int cnt;
149
150    mediaClose();
151    devs = deviceFind(NULL, DEVICE_TYPE_CDROM);
152    cnt = deviceCount(devs);
153    if (!cnt) {
154	if (self)	/* Interactive? */
155	    msgConfirm("No CD/DVD devices found!  Please check that your system's\n"
156		       "configuration is correct and that the CD/DVD drive is of a supported\n"
157		       "type.  For more information, consult the hardware guide\n"
158		       "in the Doc menu.");
159	return DITEM_FAILURE | DITEM_CONTINUE;
160    }
161    else if (cnt > 1) {
162	DMenu *menu;
163	int status;
164
165	menu = deviceCreateMenu(&MenuMediaCDROM, DEVICE_TYPE_CDROM, cdromHook, NULL);
166	if (!menu)
167	    msgFatal("Unable to create CDROM menu!  Something is seriously wrong.");
168	status = dmenuOpenSimple(menu, FALSE);
169	free(menu);
170	if (!status)
171	    return DITEM_FAILURE;
172    }
173    else
174	mediaDevice = devs[0];
175    return (mediaDevice ? DITEM_SUCCESS | DITEM_LEAVE_MENU : DITEM_FAILURE);
176}
177
178static int
179floppyHook(dialogMenuItem *self)
180{
181    return genericHook(self, DEVICE_TYPE_FLOPPY);
182}
183
184/*
185 * Return 1 if we successfully found and set the installation type to
186 * be a floppy
187 */
188int
189mediaSetFloppy(dialogMenuItem *self)
190{
191    Device **devs;
192    int cnt;
193
194    mediaClose();
195    devs = deviceFind(NULL, DEVICE_TYPE_FLOPPY);
196    cnt = deviceCount(devs);
197    if (!cnt) {
198	msgConfirm("No floppy devices found!  Please check that your system's configuration\n"
199		   "is correct.  For more information, consult the hardware guide in the Doc\n"
200		   "menu.");
201	return DITEM_FAILURE | DITEM_CONTINUE;
202    }
203    else if (cnt > 1) {
204	DMenu *menu;
205	int status;
206
207	menu = deviceCreateMenu(&MenuMediaFloppy, DEVICE_TYPE_FLOPPY, floppyHook, NULL);
208	if (!menu)
209	    msgFatal("Unable to create Floppy menu!  Something is seriously wrong.");
210	status = dmenuOpenSimple(menu, FALSE);
211	free(menu);
212	if (!status)
213	    return DITEM_FAILURE;
214    }
215    else
216	mediaDevice = devs[0];
217    if (mediaDevice)
218	mediaDevice->private = NULL;
219    return (mediaDevice ? DITEM_LEAVE_MENU : DITEM_FAILURE);
220}
221
222static int
223USBHook(dialogMenuItem *self)
224{
225	return genericHook(self, DEVICE_TYPE_USB);
226}
227
228
229/*
230 * Attempt to use USB as the installation media type.
231 */
232int
233mediaSetUSB(dialogMenuItem *self)
234{
235	Device **devs;
236	int cnt;
237
238	mediaClose();
239	devs = deviceFind(NULL, DEVICE_TYPE_USB);
240	cnt = deviceCount(devs);
241
242	if (!cnt) {
243		msgConfirm("No USB devices found (try Options/Re-scan Devices)");
244		return DITEM_FAILURE | DITEM_CONTINUE;
245	}
246	else if (cnt > 1) {
247		DMenu *menu;
248		int status;
249
250		menu = deviceCreateMenu(&MenuMediaUSB, DEVICE_TYPE_USB, USBHook,
251		    NULL);
252		if (!menu)
253			msgFatal("Unable to create USB menu! Something is " \
254			    "seriously wrong.");
255		status = dmenuOpenSimple(menu, FALSE);
256		free(menu);
257		if (!status)
258			return DITEM_FAILURE;
259	}
260	else
261		mediaDevice = devs[0];
262	if (mediaDevice)
263		mediaDevice->private = NULL;
264	if (!variable_get(VAR_NONINTERACTIVE))
265		msgConfirm("Using USB device: %s", mediaDevice->name);
266	return (mediaDevice ? DITEM_LEAVE_MENU : DITEM_FAILURE);
267}
268
269static int
270DOSHook(dialogMenuItem *self)
271{
272    return genericHook(self, DEVICE_TYPE_DOS);
273}
274
275/*
276 * Return 1 if we successfully found and set the installation type to
277 * be a DOS partition.
278 */
279int
280mediaSetDOS(dialogMenuItem *self)
281{
282    Device **devs;
283    int cnt;
284
285    mediaClose();
286    devs = deviceFind(NULL, DEVICE_TYPE_DOS);
287    cnt = deviceCount(devs);
288    if (!cnt) {
289	msgConfirm("No DOS primary partitions found!  This installation method is unavailable");
290	return DITEM_FAILURE | DITEM_CONTINUE;
291    }
292    else if (cnt > 1) {
293	DMenu *menu;
294	int status;
295
296	menu = deviceCreateMenu(&MenuMediaDOS, DEVICE_TYPE_DOS, DOSHook, NULL);
297	if (!menu)
298	    msgFatal("Unable to create DOS menu!  Something is seriously wrong.");
299	status = dmenuOpenSimple(menu, FALSE);
300	free(menu);
301	if (!status)
302	    return DITEM_FAILURE;
303    }
304    else
305	mediaDevice = devs[0];
306    return (mediaDevice ? DITEM_LEAVE_MENU : DITEM_FAILURE);
307}
308
309/*
310 * Return 0 if we successfully found and set the installation type to
311 * be an ftp server
312 */
313int
314mediaSetFTP(dialogMenuItem *self)
315{
316    static Device ftpDevice;
317    char *cp, hbuf[MAXHOSTNAMELEN], *hostname, *dir;
318    struct addrinfo hints, *res;
319    int af;
320    size_t urllen;
321    extern int FtpPort;
322    static Device *networkDev = NULL;
323
324    mediaClose();
325    cp = variable_get(VAR_FTP_PATH);
326    /* If we've been through here before ... */
327    if (networkDev && cp && msgYesNo("Re-use old FTP site selection values?"))
328	cp = NULL;
329    if (!cp) {
330	if (!dmenuOpenSimple(&MenuMediaFTP, FALSE))
331	    return DITEM_FAILURE;
332	else
333	    cp = variable_get(VAR_FTP_PATH);
334    }
335    if (!cp)
336	return DITEM_FAILURE;
337    else if (!strcmp(cp, "other")) {
338	variable_set2(VAR_FTP_PATH, "ftp://", 0);
339	cp = variable_get_value(VAR_FTP_PATH, "Please specify the URL of a FreeBSD distribution on a\n"
340				"remote ftp site.  This site must accept either anonymous\n"
341				"ftp or you should have set an ftp username and password\n"
342				"in the Options screen.\n\n"
343				"A URL looks like this:  ftp://<hostname>/<path>\n"
344				"Where <path> is relative to the anonymous ftp directory or the\n"
345				"home directory of the user being logged in as.", 0);
346	if (!cp || !*cp || !strcmp(cp, "ftp://")) {
347	    variable_unset(VAR_FTP_PATH);
348	    return DITEM_FAILURE;
349	}
350	urllen = strlen(cp);
351	if (urllen >= sizeof(ftpDevice.name)) {
352	    msgConfirm("Length of specified URL is %zu characters. Allowable maximum is %zu.",
353			urllen,sizeof(ftpDevice.name)-1);
354	    variable_unset(VAR_FTP_PATH);
355	    return DITEM_FAILURE;
356	}
357    }
358    if (strncmp("ftp://", cp, 6)) {
359	msgConfirm("Sorry, %s is an invalid URL!", cp);
360	variable_unset(VAR_FTP_PATH);
361	return DITEM_FAILURE;
362    }
363    SAFE_STRCPY(ftpDevice.name, cp);
364    SAFE_STRCPY(hbuf, cp + 6);
365    hostname = hbuf;
366
367    if (!networkDev || msgYesNo("You've already done the network configuration once,\n"
368				"would you like to skip over it now?") != 0) {
369	if (networkDev)
370	    DEVICE_SHUTDOWN(networkDev);
371	if (!(networkDev = tcpDeviceSelect())) {
372	    variable_unset(VAR_FTP_PATH);
373	    return DITEM_FAILURE;
374	}
375    }
376    if (!DEVICE_INIT(networkDev)) {
377	if (isDebug())
378	    msgDebug("mediaSetFTP: Net device init failed.\n");
379	variable_unset(VAR_FTP_PATH);
380	return DITEM_FAILURE;
381    }
382    if (*hostname == '[' && (cp = index(hostname + 1, ']')) != NULL &&
383	(*++cp == '\0' || *cp == '/' || *cp == ':')) {
384	++hostname;
385	*(cp - 1) = '\0';
386    }
387    else
388	cp = index(hostname, ':');
389    if (cp != NULL && *cp == ':') {
390	*(cp++) = '\0';
391	FtpPort = strtol(cp, 0, 0);
392    }
393    else
394	FtpPort = 21;
395    if ((dir = index(cp ? cp : hostname, '/')) != NULL)
396	*(dir++) = '\0';
397    if (isDebug()) {
398	msgDebug("hostname = `%s'\n", hostname);
399	msgDebug("dir = `%s'\n", dir ? dir : "/");
400	msgDebug("port # = `%d'\n", FtpPort);
401    }
402    if (!ftp_skip_resolve && variable_get(VAR_NAMESERVER)) {
403	msgNotify("Looking up host %s.", hostname);
404    	if (isDebug())
405	    msgDebug("Starting DNS.\n");
406	kickstart_dns();
407    	if (isDebug())
408	    msgDebug("Looking up hostname, %s, using getaddrinfo(AI_NUMERICHOST).\n", hostname);
409	af = variable_cmp(VAR_IPV6_ENABLE, "YES") ? AF_INET : AF_UNSPEC;
410	memset(&hints, 0, sizeof(hints));
411	hints.ai_family = af;
412	hints.ai_socktype = SOCK_STREAM;
413	hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
414	if (getaddrinfo(hostname, NULL, &hints, &res) != 0) {
415	    if (isDebug())
416		msgDebug("Looking up hostname, %s, using getaddrinfo().\n",
417			 hostname);
418	    hints.ai_flags = AI_PASSIVE;
419	    if (getaddrinfo(hostname, NULL, &hints, &res) != 0) {
420		msgConfirm("Cannot resolve hostname `%s'!  Are you sure that"
421			" your\nname server, gateway and network interface are"
422			" correctly configured?", hostname);
423		if (networkDev)
424		    DEVICE_SHUTDOWN(networkDev);
425		networkDev = NULL;
426		variable_unset(VAR_FTP_PATH);
427		return DITEM_FAILURE;
428	    }
429	}
430	freeaddrinfo(res);
431	if (isDebug())
432	    msgDebug("Found DNS entry for %s successfully..\n", hostname);
433    }
434    variable_set2(VAR_FTP_HOST, hostname, 0);
435    variable_set2(VAR_FTP_DIR, dir ? dir : "/", 0);
436    variable_set2(VAR_FTP_PORT, itoa(FtpPort), 0);
437    ftpDevice.type = DEVICE_TYPE_FTP;
438    ftpDevice.init = mediaInitFTP;
439    ftpDevice.get = mediaGetFTP;
440    ftpDevice.shutdown = mediaShutdownFTP;
441    ftpDevice.private = networkDev;
442    mediaDevice = &ftpDevice;
443    return DITEM_SUCCESS | DITEM_LEAVE_MENU | DITEM_RESTORE;
444}
445
446int
447mediaSetFTPActive(dialogMenuItem *self)
448{
449    variable_set2(VAR_FTP_STATE, "active", 0);
450    return mediaSetFTP(self);
451}
452
453int
454mediaSetFTPPassive(dialogMenuItem *self)
455{
456    variable_set2(VAR_FTP_STATE, "passive", 0);
457    return mediaSetFTP(self);
458}
459
460int mediaSetHTTP(dialogMenuItem *self)
461{
462    Boolean tmp;
463    int result;
464    char *cp, *idx, hbuf[MAXHOSTNAMELEN], *hostname;
465    int HttpPort;
466    int what = DITEM_RESTORE;
467
468
469    tmp = ftp_skip_resolve;
470    ftp_skip_resolve = TRUE;
471    result = mediaSetFTP(self);
472    ftp_skip_resolve = tmp;
473
474    if (DITEM_STATUS(result) != DITEM_SUCCESS)
475	return result;
476
477    cp = variable_get_value(VAR_HTTP_PROXY,
478	"Please enter the address of the HTTP proxy in this format:\n"
479	" hostname:port (the ':port' is optional, default is 3128)",0);
480    if (!cp)
481	return DITEM_FAILURE;
482    SAFE_STRCPY(hbuf, cp);
483    hostname = hbuf;
484    if (*hostname == '[' && (idx = index(hostname + 1, ']')) != NULL &&
485	(*++idx == '\0' || *idx == ':')) {
486	++hostname;
487	*(idx - 1) = '\0';
488    } else
489	idx = index(hostname, ':');
490    if (idx == NULL || *idx != ':')
491	HttpPort = 3128;		/* try this as default */
492    else {
493	*(idx++) = '\0';
494	HttpPort = strtol(idx, 0, 0);
495    }
496
497    variable_set2(VAR_HTTP_HOST, hostname, 0);
498    variable_set2(VAR_HTTP_PORT, itoa(HttpPort), 0);
499    if (isDebug()) {
500      msgDebug("VAR_FTP_PATH : %s\n",variable_get(VAR_FTP_PATH));
501      msgDebug("VAR_HTTP_HOST, _PORT: %s:%s\n",variable_get(VAR_HTTP_HOST),
502                                             variable_get(VAR_HTTP_PORT));
503    }
504
505    /* mediaDevice has been set by mediaSetFTP(), overwrite partly: */
506    mediaDevice->type = DEVICE_TYPE_HTTP;
507    mediaDevice->init = mediaInitHTTP;
508    mediaDevice->get = mediaGetHTTP;
509    mediaDevice->shutdown = dummyShutdown;
510    return DITEM_SUCCESS | DITEM_LEAVE_MENU | what;
511}
512
513/*
514 * Return 0 if we successfully found and set the installation type to
515 * be an http server
516 */
517int
518mediaSetHTTPDirect(dialogMenuItem *self)
519{
520    static Device httpDevice;
521    char *cp, hbuf[MAXPATHLEN], *hostname, *dir;
522    struct addrinfo hints, *res;
523    int af;
524    size_t urllen;
525    int HttpPort;
526    static Device *networkDev = NULL;
527
528    mediaClose();
529    cp = variable_get(VAR_HTTP_PATH);
530    /* If we've been through here before ... */
531    if (networkDev && cp && msgYesNo("Re-use old HTTP site selection values?"))
532	cp = NULL;
533    if (!cp) {
534	if (!dmenuOpenSimple(&MenuMediaHTTPDirect, FALSE))
535	    return DITEM_FAILURE;
536	else
537	    cp = variable_get(VAR_HTTP_PATH);
538    }
539    if (!cp)
540	return DITEM_FAILURE;
541    else if (!strcmp(cp, "other")) {
542	variable_set2(VAR_HTTP_PATH, "http://", 0);
543	cp = variable_get_value(VAR_HTTP_PATH, "Please specify the URL of a FreeBSD distribution on a\n"
544				"remote http site.\n"
545				"A URL looks like this:  http://<hostname>/<path>", 0);
546	if (!cp || !*cp || !strcmp(cp, "http://")) {
547	    variable_unset(VAR_HTTP_PATH);
548	    return DITEM_FAILURE;
549	}
550	urllen = strlen(cp);
551	if (urllen >= sizeof(httpDevice.name)) {
552	    msgConfirm("Length of specified URL is %zu characters. Allowable maximum is %zu.",
553			urllen,sizeof(httpDevice.name)-1);
554	    variable_unset(VAR_HTTP_PATH);
555	    return DITEM_FAILURE;
556	}
557    }
558    if (strncmp("http://", cp, 7)) {
559	msgConfirm("Sorry, %s is an invalid URL!", cp);
560	variable_unset(VAR_HTTP_PATH);
561	return DITEM_FAILURE;
562    }
563    SAFE_STRCPY(httpDevice.name, cp);
564    SAFE_STRCPY(hbuf, cp + 7);
565    hostname = hbuf;
566
567    if (!networkDev || msgYesNo("You've already done the network configuration once,\n"
568				"would you like to skip over it now?") != 0) {
569	if (networkDev)
570	    DEVICE_SHUTDOWN(networkDev);
571	if (!(networkDev = tcpDeviceSelect())) {
572	    variable_unset(VAR_HTTP_PATH);
573	    return DITEM_FAILURE;
574	}
575    }
576    if (!DEVICE_INIT(networkDev)) {
577	if (isDebug())
578	    msgDebug("mediaSetHTTPDirect: Net device init failed.\n");
579	variable_unset(VAR_HTTP_PATH);
580	return DITEM_FAILURE;
581    }
582    if (*hostname == '[' && (cp = index(hostname + 1, ']')) != NULL &&
583	(*++cp == '\0' || *cp == '/' || *cp == ':')) {
584	++hostname;
585	*(cp - 1) = '\0';
586    }
587    else
588	cp = index(hostname, ':');
589    if (cp != NULL && *cp == ':') {
590	*(cp++) = '\0';
591	HttpPort = strtol(cp, 0, 0);
592    }
593    else
594	HttpPort = 80;
595    if ((dir = index(cp ? cp : hostname, '/')) != NULL)
596	*(dir++) = '\0';
597    if (isDebug()) {
598	msgDebug("hostname = `%s'\n", hostname);
599	msgDebug("dir = `%s'\n", dir ? dir : "/");
600	msgDebug("port # = `%d'\n", HttpPort);
601    }
602    if (!http_skip_resolve && variable_get(VAR_NAMESERVER)) {
603	msgNotify("Looking up host %s.", hostname);
604    	if (isDebug())
605	    msgDebug("Starting DNS.\n");
606	kickstart_dns();
607    	if (isDebug())
608	    msgDebug("Looking up hostname, %s, using getaddrinfo(AI_NUMERICHOST).\n", hostname);
609	af = variable_cmp(VAR_IPV6_ENABLE, "YES") ? AF_INET : AF_UNSPEC;
610	memset(&hints, 0, sizeof(hints));
611	hints.ai_family = af;
612	hints.ai_socktype = SOCK_STREAM;
613	hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
614	if (getaddrinfo(hostname, NULL, &hints, &res) != 0) {
615	    if (isDebug())
616		msgDebug("Looking up hostname, %s, using getaddrinfo().\n",
617			 hostname);
618	    hints.ai_flags = AI_PASSIVE;
619	    if (getaddrinfo(hostname, NULL, &hints, &res) != 0) {
620		msgConfirm("Cannot resolve hostname `%s'!  Are you sure that"
621			" your\nname server, gateway and network interface are"
622			" correctly configured?", hostname);
623		if (networkDev)
624		    DEVICE_SHUTDOWN(networkDev);
625		networkDev = NULL;
626		variable_unset(VAR_HTTP_PATH);
627		return DITEM_FAILURE;
628	    }
629	}
630	freeaddrinfo(res);
631	if (isDebug())
632	    msgDebug("Found DNS entry for %s successfully..\n", hostname);
633    }
634    variable_set2(VAR_HTTP_HOST, hostname, 0);
635    variable_set2(VAR_HTTP_DIR, dir ? dir : "/", 0);
636    variable_set2(VAR_HTTP_PORT, itoa(HttpPort), 0);
637    httpDevice.type = DEVICE_TYPE_HTTP_DIRECT;
638    httpDevice.init = mediaInitHTTPDirect;
639    httpDevice.get = mediaGetHTTPDirect;
640    httpDevice.shutdown = dummyShutdown;
641    httpDevice.private = networkDev;
642    mediaDevice = &httpDevice;
643    return DITEM_SUCCESS | DITEM_LEAVE_MENU | DITEM_RESTORE;
644}
645
646
647int
648mediaSetUFS(dialogMenuItem *self)
649{
650    static Device ufsDevice;
651    struct statfs st;
652    char *cp;
653
654    mediaClose();
655    cp = variable_get_value(VAR_UFS_PATH, "Enter a fully qualified pathname for the directory\n"
656			    "containing the FreeBSD distribution files:", 0);
657    if (!cp)
658	return DITEM_FAILURE;
659
660    /* If they gave us a CDROM or something, try and pick a better name */
661    if (statfs(cp, &st))
662	strcpy(ufsDevice.name, "ufs");
663    else
664	strcpy(ufsDevice.name, st.f_fstypename);
665
666    ufsDevice.type = DEVICE_TYPE_UFS;
667    ufsDevice.init = dummyInit;
668    ufsDevice.get = mediaGetUFS;
669    ufsDevice.shutdown = dummyShutdown;
670    ufsDevice.private = strdup(cp);
671    mediaDevice = &ufsDevice;
672    return DITEM_LEAVE_MENU;
673}
674
675int
676mediaSetNFS(dialogMenuItem *self)
677{
678    static Device nfsDevice;
679    static Device *networkDev = NULL;
680    char *cp, *idx;
681    char hostname[MAXPATHLEN];
682    size_t pathlen;
683
684    mediaClose();
685    cp = variable_get_value(VAR_NFS_PATH, "Please enter the full NFS file specification for the remote\n"
686			    "host and directory containing the FreeBSD distribution files.\n"
687			    "This should be in the format:  hostname:/some/freebsd/dir", 0);
688    if (!cp)
689	return DITEM_FAILURE;
690    SAFE_STRCPY(hostname, cp);
691    if (!(idx = index(hostname, ':'))) {
692	msgConfirm("Invalid NFS path specification.  Must be of the form:\n"
693		   "host:/full/pathname/to/FreeBSD/distdir");
694	return DITEM_FAILURE;
695    }
696    pathlen = strlen(hostname);
697    if (pathlen >= sizeof(nfsDevice.name)) {
698	msgConfirm("Length of specified NFS path is %zu characters. Allowable maximum is %zu.",
699		   pathlen,sizeof(nfsDevice.name)-1);
700	variable_unset(VAR_NFS_PATH);
701	return DITEM_FAILURE;
702    }
703    SAFE_STRCPY(nfsDevice.name, hostname);
704    *idx = '\0';
705    if (!networkDev || msgYesNo("You've already done the network configuration once,\n"
706				"would you like to skip over it now?") != 0) {
707	if (networkDev)
708	    DEVICE_SHUTDOWN(networkDev);
709	if (!(networkDev = tcpDeviceSelect()))
710	    return DITEM_FAILURE;
711    }
712    if (!DEVICE_INIT(networkDev)) {
713	if (isDebug())
714	    msgDebug("mediaSetNFS: Net device init failed\n");
715    }
716    if (variable_get(VAR_NAMESERVER)) {
717	kickstart_dns();
718	if ((inet_addr(hostname) == INADDR_NONE) && (gethostbyname(hostname) == NULL)) {
719	    msgConfirm("Cannot resolve hostname `%s'!  Are you sure that your\n"
720		       "name server, gateway and network interface are correctly configured?", hostname);
721	    if (networkDev)
722		DEVICE_SHUTDOWN(networkDev);
723	    networkDev = NULL;
724	    variable_unset(VAR_NFS_PATH);
725	    return DITEM_FAILURE;
726	}
727	else {
728	    if (isDebug())
729		msgDebug("Found DNS entry for %s successfully..\n", hostname);
730	}
731    }
732    variable_set2(VAR_NFS_HOST, hostname, 0);
733    nfsDevice.type = DEVICE_TYPE_NFS;
734    nfsDevice.init = mediaInitNFS;
735    nfsDevice.get = mediaGetNFS;
736    nfsDevice.shutdown = mediaShutdownNFS;
737    nfsDevice.private = networkDev;
738    mediaDevice = &nfsDevice;
739    return DITEM_LEAVE_MENU;
740}
741
742Boolean
743mediaExtractDistBegin(char *dir, int *fd, int *zpid, int *cpid)
744{
745    int i, pfd[2],qfd[2];
746
747    if (!dir)
748	dir = "/";
749    Mkdir(dir);
750    chdir(dir);
751    pipe(pfd);
752    pipe(qfd);
753    *zpid = fork();
754    if (!*zpid) {
755	char *unzipper = RunningAsInit ? "/stand/" UNZIPPER
756	    : "/usr/bin/" UNZIPPER;
757
758	dup2(qfd[0], 0); close(qfd[0]);
759	dup2(pfd[1], 1); close(pfd[1]);
760	if (DebugFD != -1)
761	    dup2(DebugFD, 2);
762	else {
763	    close(2);
764	    open("/dev/null", O_WRONLY);
765	}
766	close(qfd[1]);
767	close(pfd[0]);
768	i = execl(unzipper, unzipper, (char *)0);
769	if (isDebug())
770	    msgDebug("%s command returns %d status\n", unzipper, i);
771	exit(i);
772    }
773    *fd = qfd[1];
774    close(qfd[0]);
775    *cpid = fork();
776    if (!*cpid) {
777	char *cpio = RunningAsInit ? "/stand/cpio" : "/usr/bin/cpio";
778
779	dup2(pfd[0], 0); close(pfd[0]);
780	close(pfd[1]);
781	close(qfd[1]);
782	if (DebugFD != -1) {
783	    dup2(DebugFD, 1);
784	    dup2(DebugFD, 2);
785	}
786	else {
787	    close(1); open("/dev/null", O_WRONLY);
788	    dup2(1, 2);
789	}
790	if (strlen(cpioVerbosity()))
791	    i = execl(cpio, cpio, "-idum", cpioVerbosity(), (char *)0);
792	else
793	    i = execl(cpio, cpio, "-idum", (char *)0);
794	if (isDebug())
795	    msgDebug("%s command returns %d status\n", cpio, i);
796	exit(i);
797    }
798    close(pfd[0]);
799    close(pfd[1]);
800    return TRUE;
801}
802
803Boolean
804mediaExtractDistEnd(int zpid, int cpid)
805{
806    int i,j;
807
808    i = waitpid(zpid, &j, 0);
809    /* Don't check exit status - gunzip seems to return a bogus one! */
810    if (i < 0) {
811	if (isDebug())
812	    msgDebug("wait for %s returned status of %d!\n", UNZIPPER, i);
813	return FALSE;
814    }
815    i = waitpid(cpid, &j, 0);
816    if (i < 0 || WEXITSTATUS(j)) {
817	if (isDebug())
818	    msgDebug("cpio returned error status of %d!\n", WEXITSTATUS(j));
819	return FALSE;
820    }
821    return TRUE;
822}
823
824Boolean
825mediaExtractDist(char *dir, char *dist, FILE *fp)
826{
827    int i, j, total, seconds, zpid, cpid, pfd[2], qfd[2];
828    char buf[BUFSIZ];
829    struct timeval start, stop;
830    struct sigaction new, old;
831
832    if (!dir)
833	dir = "/";
834
835    Mkdir(dir);
836    chdir(dir);
837    pipe(pfd);	/* read end */
838    pipe(qfd);	/* write end */
839    zpid = fork();
840    if (!zpid) {
841	char *unzipper = RunningAsInit ? "/stand/" UNZIPPER
842	    : "/usr/bin/" UNZIPPER;
843
844	fclose(fp);
845	close(qfd[1]);
846	dup2(qfd[0], 0); close(qfd[0]);
847
848	close(pfd[0]);
849	dup2(pfd[1], 1); close(pfd[1]);
850
851	if (DebugFD != -1)
852	    dup2(DebugFD, 2);
853	else {
854	    close(2);
855	    open("/dev/null", O_WRONLY);
856	}
857	i = execl(unzipper, unzipper, (char *)0);
858	if (isDebug())
859	    msgDebug("%s command returns %d status\n", unzipper, i);
860	exit(i);
861    }
862    cpid = fork();
863    if (!cpid) {
864	char *cpio = RunningAsInit ? "/stand/cpio" : "/usr/bin/cpio";
865
866	close(pfd[1]);
867	dup2(pfd[0], 0); close(pfd[0]);
868	close (qfd[0]); close(qfd[1]);
869	fclose(fp);
870	if (DebugFD != -1) {
871	    dup2(DebugFD, 1);
872	    dup2(DebugFD, 2);
873	}
874	else {
875	    dup2(open("/dev/null", O_WRONLY), 1);
876	    dup2(1, 2);
877	}
878	if (strlen(cpioVerbosity()))
879	    i = execl(cpio, cpio, "-idum", cpioVerbosity(), (char *)0);
880	else
881	    i = execl(cpio, cpio, "-idum", "--block-size", (char *)0);
882	if (isDebug())
883	    msgDebug("%s command returns %d status\n", cpio, i);
884	exit(i);
885    }
886    close(pfd[0]); close(pfd[1]);
887    close(qfd[0]);
888
889    total = 0;
890    (void)gettimeofday(&start, (struct timezone *)0);
891
892    /* Make ^C abort the current transfer rather than the whole show */
893    new.sa_handler = handle_intr;
894    new.sa_flags = 0;
895    (void)sigemptyset(&new.sa_mask);
896    sigaction(SIGINT, &new, &old);
897
898    while ((i = fread(buf, 1, BUFSIZ, fp)) > 0) {
899	if (check_for_interrupt()) {
900	    msgConfirm("Failure to read from media:  User interrupt.");
901	    break;
902	}
903	if (write(qfd[1], buf, i) != i) {
904	    msgConfirm("Write error on transfer to cpio process, try of %d bytes.", i);
905	    break;
906	}
907	else {
908	    (void)gettimeofday(&stop, (struct timezone *)0);
909	    stop.tv_sec = stop.tv_sec - start.tv_sec;
910	    stop.tv_usec = stop.tv_usec - start.tv_usec;
911	    if (stop.tv_usec < 0)
912		stop.tv_sec--, stop.tv_usec += 1000000;
913	    seconds = stop.tv_sec + (stop.tv_usec / 1000000.0);
914	    if (!seconds)
915		seconds = 1;
916	    total += i;
917	    msgInfo("%10d bytes read from %s dist @ %.1f KB/sec.",
918		    total, dist, (total / seconds) / 1024.0);
919	}
920    }
921    sigaction(SIGINT, &old, NULL);	/* restore sigint */
922    close(qfd[1]);
923
924    i = waitpid(zpid, &j, 0);
925    /* Don't check exit status - gunzip seems to return a bogus one! */
926    if (i < 0) {
927	if (isDebug())
928	    msgDebug("wait for %s returned status of %d!\n", UNZIPPER, i);
929	return FALSE;
930    }
931    i = waitpid(cpid, &j, 0);
932    if (i < 0 || WEXITSTATUS(j)) {
933	if (isDebug())
934	    msgDebug("cpio returned error status of %d!\n", WEXITSTATUS(j));
935	return FALSE;
936    }
937    return TRUE;
938}
939
940int
941mediaGetType(dialogMenuItem *self)
942{
943    return ((dmenuOpenSimple(&MenuMedia, FALSE) && mediaDevice) ? DITEM_SUCCESS : DITEM_FAILURE);
944}
945
946/* Return TRUE if all the media variables are set up correctly */
947Boolean
948mediaVerify(void)
949{
950    if (!mediaDevice)
951	return (DITEM_STATUS(mediaGetType(NULL)) == DITEM_SUCCESS);
952    return TRUE;
953}
954
955/* Set the FTP username and password fields */
956int
957mediaSetFTPUserPass(dialogMenuItem *self)
958{
959    char *pass;
960
961    if (variable_get_value(VAR_FTP_USER, "Please enter the username you wish to login as:", 0)) {
962	DialogInputAttrs |= DITEM_NO_ECHO;
963	pass = variable_get_value(VAR_FTP_PASS, "Please enter the password for this user:", 0);
964	DialogInputAttrs &= ~DITEM_NO_ECHO;
965    }
966    else
967	pass = NULL;
968    return (pass ? DITEM_SUCCESS : DITEM_FAILURE);
969}
970
971/* Set CPIO verbosity level */
972int
973mediaSetCPIOVerbosity(dialogMenuItem *self)
974{
975    char *cp = variable_get(VAR_CPIO_VERBOSITY);
976
977    if (!cp) {
978	msgConfirm("CPIO Verbosity is not set to anything!");
979	return DITEM_FAILURE;
980    }
981    else {
982	if (!strcmp(cp, "low"))
983	    variable_set2(VAR_CPIO_VERBOSITY, "high", 0);
984	else /* must be "high" - wrap around */
985	    variable_set2(VAR_CPIO_VERBOSITY, "low", 0);
986    }
987    return DITEM_SUCCESS;
988}
989
990/* A generic open which follows a well-known "path" of places to look */
991FILE *
992mediaGenericGet(char *base, const char *file)
993{
994    char	buf[PATH_MAX];
995
996    snprintf(buf, PATH_MAX, "%s/%s", base, file);
997    if (file_readable(buf))
998	return fopen(buf, "r");
999    snprintf(buf, PATH_MAX, "%s/FreeBSD/%s", base, file);
1000    if (file_readable(buf))
1001	return fopen(buf, "r");
1002    snprintf(buf, PATH_MAX, "%s/releases/%s", base, file);
1003    if (file_readable(buf))
1004	return fopen(buf, "r");
1005    snprintf(buf, PATH_MAX, "%s/%s/%s", base, variable_get(VAR_RELNAME), file);
1006    if (file_readable(buf))
1007	return fopen(buf, "r");
1008    snprintf(buf, PATH_MAX, "%s/releases/%s/%s", base, variable_get(VAR_RELNAME), file);
1009    return fopen(buf, "r");
1010}
1011
1012