2 * The new sysinstall program.
4 * This is probably the last attempt in the `sysinstall' line, the next
5 * generation being slated to essentially a complete rewrite.
7 * $FreeBSD: src/release/sysinstall/media.c,v 1.107.2.9 2002/07/02 22:24:20 jhb Exp $
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"
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>
48 #include <netinet/in.h>
49 #include <arpa/inet.h>
52 static Boolean got_intr = FALSE;
53 static Boolean ftp_skip_resolve = FALSE;
59 msgDebug("User generated interrupt.\n");
64 check_for_interrupt(void)
74 genericHook(dialogMenuItem *self, DeviceType type)
78 devs = deviceFind(self->prompt, type);
80 mediaDevice = devs[0];
81 return (devs ? DITEM_LEAVE_MENU : DITEM_FAILURE);
85 cdromHook(dialogMenuItem *self)
87 return genericHook(self, DEVICE_TYPE_CDROM);
93 static Boolean initted = FALSE;
97 cp = variable_get(VAR_MEDIA_TIMEOUT);
106 _res.retry = 2; /* 2 times seems a reasonable number to me */
107 _res.retrans = time / 2; /* so spend half our alloted time on each try */
115 char *cp = variable_get(VAR_CPIO_VERBOSITY);
117 if (cp && !strcmp(cp, "high"))
119 else if (cp && !strcmp(cp, "medium"))
128 DEVICE_SHUTDOWN(mediaDevice);
133 * Return 1 if we successfully found and set the installation type to
137 mediaSetCDROM(dialogMenuItem *self)
143 devs = deviceFind(NULL, DEVICE_TYPE_CDROM);
144 cnt = deviceCount(devs);
146 if (self) /* Interactive? */
147 msgConfirm("No CD/DVD devices found! Please check that your system's\n"
148 "configuration is correct and that the CD/DVD drive is of a supported\n"
149 "type. For more information, consult the hardware guide\n"
151 return DITEM_FAILURE | DITEM_CONTINUE;
157 menu = deviceCreateMenu(&MenuMediaCDROM, DEVICE_TYPE_CDROM, cdromHook, NULL);
159 msgFatal("Unable to create CDROM menu! Something is seriously wrong.");
160 status = dmenuOpenSimple(menu, FALSE);
163 return DITEM_FAILURE;
166 mediaDevice = devs[0];
167 return (mediaDevice ? DITEM_SUCCESS | DITEM_LEAVE_MENU : DITEM_FAILURE);
171 floppyHook(dialogMenuItem *self)
173 return genericHook(self, DEVICE_TYPE_FLOPPY);
177 * Return 1 if we successfully found and set the installation type to
181 mediaSetFloppy(dialogMenuItem *self)
187 devs = deviceFind(NULL, DEVICE_TYPE_FLOPPY);
188 cnt = deviceCount(devs);
190 msgConfirm("No floppy devices found! Please check that your system's configuration\n"
191 "is correct. For more information, consult the hardware guide in the Doc\n"
193 return DITEM_FAILURE | DITEM_CONTINUE;
199 menu = deviceCreateMenu(&MenuMediaFloppy, DEVICE_TYPE_FLOPPY, floppyHook, NULL);
201 msgFatal("Unable to create Floppy menu! Something is seriously wrong.");
202 status = dmenuOpenSimple(menu, FALSE);
205 return DITEM_FAILURE;
208 mediaDevice = devs[0];
210 mediaDevice->private = NULL;
211 return (mediaDevice ? DITEM_LEAVE_MENU : DITEM_FAILURE);
215 DOSHook(dialogMenuItem *self)
217 return genericHook(self, DEVICE_TYPE_DOS);
221 * Return 1 if we successfully found and set the installation type to
222 * be a DOS partition.
225 mediaSetDOS(dialogMenuItem *self)
231 devs = deviceFind(NULL, DEVICE_TYPE_DOS);
232 cnt = deviceCount(devs);
234 msgConfirm("No DOS primary partitions found! This installation method is unavailable");
235 return DITEM_FAILURE | DITEM_CONTINUE;
241 menu = deviceCreateMenu(&MenuMediaDOS, DEVICE_TYPE_DOS, DOSHook, NULL);
243 msgFatal("Unable to create DOS menu! Something is seriously wrong.");
244 status = dmenuOpenSimple(menu, FALSE);
247 return DITEM_FAILURE;
250 mediaDevice = devs[0];
251 return (mediaDevice ? DITEM_LEAVE_MENU : DITEM_FAILURE);
255 tapeHook(dialogMenuItem *self)
257 return genericHook(self, DEVICE_TYPE_TAPE);
261 * Return 1 if we successfully found and set the installation type to
265 mediaSetTape(dialogMenuItem *self)
271 devs = deviceFind(NULL, DEVICE_TYPE_TAPE);
272 cnt = deviceCount(devs);
274 msgConfirm("No tape drive devices found! Please check that your system's configuration\n"
275 "is correct. For more information, consult the hardware guide in the Doc\n"
277 return DITEM_FAILURE | DITEM_CONTINUE;
283 menu = deviceCreateMenu(&MenuMediaTape, DEVICE_TYPE_TAPE, tapeHook, NULL);
285 msgFatal("Unable to create tape drive menu! Something is seriously wrong.");
286 status = dmenuOpenSimple(menu, FALSE);
289 return DITEM_FAILURE;
292 mediaDevice = devs[0];
296 val = msgGetInput("/var/tmp", "Please enter the name of a temporary directory containing\n"
297 "sufficient space for holding the contents of this tape (or\n"
298 "tapes). The contents of this directory will be removed\n"
299 "after installation, so be sure to specify a directory that\n"
300 "can be erased afterwards!\n");
304 mediaDevice->private = strdup(val);
306 return (mediaDevice ? DITEM_LEAVE_MENU : DITEM_FAILURE);
310 * Return 0 if we successfully found and set the installation type to
314 mediaSetFTP(dialogMenuItem *self)
316 static Device ftpDevice;
317 char *cp, hbuf[MAXHOSTNAMELEN], *hostname, *dir;
318 struct addrinfo hints, *res;
321 static Device *networkDev = NULL;
324 cp = variable_get(VAR_FTP_PATH);
325 /* If we've been through here before ... */
326 if (networkDev && cp && msgYesNo("Re-use old FTP site selection values?"))
329 if (!dmenuOpenSimple(&MenuMediaFTP, FALSE))
330 return DITEM_FAILURE;
332 cp = variable_get(VAR_FTP_PATH);
335 return DITEM_FAILURE;
336 else if (!strcmp(cp, "other")) {
337 variable_set2(VAR_FTP_PATH, "ftp://", 0);
338 cp = variable_get_value(VAR_FTP_PATH, "Please specify the URL of a FreeBSD distribution on a\n"
339 "remote ftp site. This site must accept either anonymous\n"
340 "ftp or you should have set an ftp username and password\n"
341 "in the Options screen.\n\n"
342 "A URL looks like this: ftp://<hostname>/<path>\n"
343 "Where <path> is relative to the anonymous ftp directory or the\n"
344 "home directory of the user being logged in as.", 0);
345 if (!cp || !*cp || !strcmp(cp, "ftp://")) {
346 variable_unset(VAR_FTP_PATH);
347 return DITEM_FAILURE;
350 if (strncmp("ftp://", cp, 6)) {
351 msgConfirm("Sorry, %s is an invalid URL!", cp);
352 variable_unset(VAR_FTP_PATH);
353 return DITEM_FAILURE;
355 SAFE_STRCPY(ftpDevice.name, cp);
356 SAFE_STRCPY(hbuf, cp + 6);
359 if (!networkDev || msgYesNo("You've already done the network configuration once,\n"
360 "would you like to skip over it now?") != 0) {
362 DEVICE_SHUTDOWN(networkDev);
363 if (!(networkDev = tcpDeviceSelect())) {
364 variable_unset(VAR_FTP_PATH);
365 return DITEM_FAILURE;
368 if (!DEVICE_INIT(networkDev)) {
370 msgDebug("mediaSetFTP: Net device init failed.\n");
371 variable_unset(VAR_FTP_PATH);
372 return DITEM_FAILURE;
374 if (*hostname == '[' && (cp = index(hostname + 1, ']')) != NULL &&
375 (*++cp == '\0' || *cp == '/' || *cp == ':')) {
380 cp = index(hostname, ':');
381 if (cp != NULL && *cp == ':') {
383 FtpPort = strtol(cp, 0, 0);
387 if ((dir = index(cp ? cp : hostname, '/')) != NULL)
390 msgDebug("hostname = `%s'\n", hostname);
391 msgDebug("dir = `%s'\n", dir ? dir : "/");
392 msgDebug("port # = `%d'\n", FtpPort);
394 if (!ftp_skip_resolve && variable_get(VAR_NAMESERVER)) {
395 msgNotify("Looking up host %s.", hostname);
397 msgDebug("Starting DNS.\n");
400 msgDebug("Looking up hostname, %s, using getaddrinfo(AI_NUMERICHOST).\n", hostname);
401 af = variable_cmp(VAR_IPV6_ENABLE, "YES") ? AF_INET : AF_UNSPEC;
402 memset(&hints, 0, sizeof(hints));
403 hints.ai_family = af;
404 hints.ai_socktype = SOCK_STREAM;
405 hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
406 if (getaddrinfo(hostname, NULL, &hints, &res) != 0) {
408 msgDebug("Looking up hostname, %s, using getaddrinfo().\n",
410 hints.ai_flags = AI_PASSIVE;
411 if (getaddrinfo(hostname, NULL, &hints, &res) != 0) {
412 msgConfirm("Cannot resolve hostname `%s'! Are you sure that"
413 " your\nname server, gateway and network interface are"
414 " correctly configured?", hostname);
416 DEVICE_SHUTDOWN(networkDev);
418 variable_unset(VAR_FTP_PATH);
419 return DITEM_FAILURE;
424 msgDebug("Found DNS entry for %s successfully..\n", hostname);
426 variable_set2(VAR_FTP_HOST, hostname, 0);
427 variable_set2(VAR_FTP_DIR, dir ? dir : "/", 0);
428 variable_set2(VAR_FTP_PORT, itoa(FtpPort), 0);
429 ftpDevice.type = DEVICE_TYPE_FTP;
430 ftpDevice.init = mediaInitFTP;
431 ftpDevice.get = mediaGetFTP;
432 ftpDevice.shutdown = mediaShutdownFTP;
433 ftpDevice.private = networkDev;
434 mediaDevice = &ftpDevice;
435 return DITEM_SUCCESS | DITEM_LEAVE_MENU | DITEM_RESTORE;
439 mediaSetFTPActive(dialogMenuItem *self)
441 variable_set2(VAR_FTP_STATE, "active", 0);
442 return mediaSetFTP(self);
446 mediaSetFTPPassive(dialogMenuItem *self)
448 variable_set2(VAR_FTP_STATE, "passive", 0);
449 return mediaSetFTP(self);
452 int mediaSetHTTP(dialogMenuItem *self)
456 char *cp, *idx, hbuf[MAXHOSTNAMELEN], *hostname;
458 int what = DITEM_RESTORE;
461 tmp = ftp_skip_resolve;
462 ftp_skip_resolve = TRUE;
463 result = mediaSetFTP(self);
464 ftp_skip_resolve = tmp;
466 if (DITEM_STATUS(result) != DITEM_SUCCESS)
469 cp = variable_get_value(VAR_HTTP_PROXY,
470 "Please enter the address of the HTTP proxy in this format:\n"
471 " hostname:port (the ':port' is optional, default is 3128)",0);
473 return DITEM_FAILURE;
474 SAFE_STRCPY(hbuf, cp);
476 if (*hostname == '[' && (idx = index(hostname + 1, ']')) != NULL &&
477 (*++idx == '\0' || *idx == ':')) {
481 idx = index(hostname, ':');
482 if (idx == NULL || *idx != ':')
483 HttpPort = 3128; /* try this as default */
486 HttpPort = strtol(idx, 0, 0);
489 variable_set2(VAR_HTTP_HOST, hostname, 0);
490 variable_set2(VAR_HTTP_PORT, itoa(HttpPort), 0);
492 msgDebug("VAR_FTP_PATH : %s",variable_get(VAR_FTP_PATH));
493 msgDebug("VAR_HTTP_HOST, _PORT: %s:%s",variable_get(VAR_HTTP_HOST),
494 variable_get(VAR_HTTP_PORT));
497 /* mediaDevice has been set by mediaSetFTP(), overwrite partly: */
498 mediaDevice->type = DEVICE_TYPE_HTTP;
499 mediaDevice->init = mediaInitHTTP;
500 mediaDevice->get = mediaGetHTTP;
501 mediaDevice->shutdown = dummyShutdown;
502 return DITEM_SUCCESS | DITEM_LEAVE_MENU | what;
507 mediaSetUFS(dialogMenuItem *self)
509 static Device ufsDevice;
514 cp = variable_get_value(VAR_UFS_PATH, "Enter a fully qualified pathname for the directory\n"
515 "containing the FreeBSD distribution files:", 0);
517 return DITEM_FAILURE;
519 /* If they gave us a CDROM or something, try and pick a better name */
521 strcpy(ufsDevice.name, "ufs");
523 strcpy(ufsDevice.name, st.f_fstypename);
525 ufsDevice.type = DEVICE_TYPE_UFS;
526 ufsDevice.init = dummyInit;
527 ufsDevice.get = mediaGetUFS;
528 ufsDevice.shutdown = dummyShutdown;
529 ufsDevice.private = strdup(cp);
530 mediaDevice = &ufsDevice;
531 return DITEM_LEAVE_MENU;
535 mediaSetNFS(dialogMenuItem *self)
537 static Device nfsDevice;
538 static Device *networkDev = NULL;
540 char hostname[MAXPATHLEN];
543 cp = variable_get_value(VAR_NFS_PATH, "Please enter the full NFS file specification for the remote\n"
544 "host and directory containing the FreeBSD distribution files.\n"
545 "This should be in the format: hostname:/some/freebsd/dir", 0);
547 return DITEM_FAILURE;
548 SAFE_STRCPY(hostname, cp);
549 if (!(idx = index(hostname, ':'))) {
550 msgConfirm("Invalid NFS path specification. Must be of the form:\n"
551 "host:/full/pathname/to/FreeBSD/distdir");
552 return DITEM_FAILURE;
554 SAFE_STRCPY(nfsDevice.name, hostname);
556 if (!networkDev || msgYesNo("You've already done the network configuration once,\n"
557 "would you like to skip over it now?") != 0) {
559 DEVICE_SHUTDOWN(networkDev);
560 if (!(networkDev = tcpDeviceSelect()))
561 return DITEM_FAILURE;
563 if (!DEVICE_INIT(networkDev)) {
565 msgDebug("mediaSetNFS: Net device init failed\n");
567 if (variable_get(VAR_NAMESERVER)) {
569 if ((inet_addr(hostname) == INADDR_NONE) && (gethostbyname(hostname) == NULL)) {
570 msgConfirm("Cannot resolve hostname `%s'! Are you sure that your\n"
571 "name server, gateway and network interface are correctly configured?", hostname);
573 DEVICE_SHUTDOWN(networkDev);
575 variable_unset(VAR_NFS_PATH);
576 return DITEM_FAILURE;
580 msgDebug("Found DNS entry for %s successfully..", hostname);
583 variable_set2(VAR_NFS_HOST, hostname, 0);
584 nfsDevice.type = DEVICE_TYPE_NFS;
585 nfsDevice.init = mediaInitNFS;
586 nfsDevice.get = mediaGetNFS;
587 nfsDevice.shutdown = mediaShutdownNFS;
588 nfsDevice.private = networkDev;
589 mediaDevice = &nfsDevice;
590 return DITEM_LEAVE_MENU;
594 mediaExtractDistBegin(char *dir, int *fd, int *zpid, int *cpid)
596 int i, pfd[2],qfd[2];
606 char *gunzip = RunningAsInit ? "/stand/gunzip" : "/usr/bin/gunzip";
608 dup2(qfd[0], 0); close(qfd[0]);
609 dup2(pfd[1], 1); close(pfd[1]);
614 open("/dev/null", O_WRONLY);
618 i = execl(gunzip, gunzip, 0);
620 msgDebug("%s command returns %d status\n", gunzip, i);
627 char *cpio = RunningAsInit ? "/stand/cpio" : "/usr/bin/cpio";
629 dup2(pfd[0], 0); close(pfd[0]);
637 close(1); open("/dev/null", O_WRONLY);
640 if (strlen(cpioVerbosity()))
641 i = execl(cpio, cpio, "-idum", cpioVerbosity(), "--block-size", mediaTapeBlocksize(), (char *)0);
643 i = execl(cpio, cpio, "-idum", "--block-size", mediaTapeBlocksize(), (char *)0);
645 msgDebug("%s command returns %d status\n", cpio, i);
654 mediaExtractDistEnd(int zpid, int cpid)
658 i = waitpid(zpid, &j, 0);
659 /* Don't check exit status - gunzip seems to return a bogus one! */
662 msgDebug("wait for gunzip returned status of %d!\n", i);
665 i = waitpid(cpid, &j, 0);
666 if (i < 0 || WEXITSTATUS(j)) {
668 msgDebug("cpio returned error status of %d!\n", WEXITSTATUS(j));
675 mediaExtractDist(char *dir, char *dist, FILE *fp)
677 int i, j, total, seconds, zpid, cpid, pfd[2], qfd[2];
679 struct timeval start, stop;
680 struct sigaction new, old;
687 pipe(pfd); /* read end */
688 pipe(qfd); /* write end */
691 char *gunzip = RunningAsInit ? "/stand/gunzip" : "/usr/bin/gunzip";
695 dup2(qfd[0], 0); close(qfd[0]);
698 dup2(pfd[1], 1); close(pfd[1]);
704 open("/dev/null", O_WRONLY);
706 i = execl(gunzip, gunzip, 0);
708 msgDebug("%s command returns %d status\n", gunzip, i);
713 char *cpio = RunningAsInit ? "/stand/cpio" : "/usr/bin/cpio";
716 dup2(pfd[0], 0); close(pfd[0]);
717 close (qfd[0]); close(qfd[1]);
724 dup2(open("/dev/null", O_WRONLY), 1);
727 if (strlen(cpioVerbosity()))
728 i = execl(cpio, cpio, "-idum", cpioVerbosity(), "--block-size", mediaTapeBlocksize(), (char *)0);
730 i = execl(cpio, cpio, "-idum", "--block-size", mediaTapeBlocksize(), (char *)0);
732 msgDebug("%s command returns %d status\n", cpio, i);
735 close(pfd[0]); close(pfd[1]);
739 (void)gettimeofday(&start, (struct timezone *)0);
741 /* Make ^C abort the current transfer rather than the whole show */
742 new.sa_handler = handle_intr;
744 (void)sigemptyset(&new.sa_mask);
745 sigaction(SIGINT, &new, &old);
747 while ((i = fread(buf, 1, BUFSIZ, fp)) > 0) {
748 if (check_for_interrupt()) {
749 msgConfirm("Failure to read from media: User interrupt.");
752 if (write(qfd[1], buf, i) != i) {
753 msgConfirm("Write error on transfer to cpio process, try of %d bytes.", i);
757 (void)gettimeofday(&stop, (struct timezone *)0);
758 stop.tv_sec = stop.tv_sec - start.tv_sec;
759 stop.tv_usec = stop.tv_usec - start.tv_usec;
760 if (stop.tv_usec < 0)
761 stop.tv_sec--, stop.tv_usec += 1000000;
762 seconds = stop.tv_sec + (stop.tv_usec / 1000000.0);
766 msgInfo("%10d bytes read from %s dist @ %.1f KB/sec.",
767 total, dist, (total / seconds) / 1024.0);
770 sigaction(SIGINT, &old, NULL); /* restore sigint */
773 i = waitpid(zpid, &j, 0);
774 /* Don't check exit status - gunzip seems to return a bogus one! */
777 msgDebug("wait for gunzip returned status of %d!\n", i);
780 i = waitpid(cpid, &j, 0);
781 if (i < 0 || WEXITSTATUS(j)) {
783 msgDebug("cpio returned error status of %d!\n", WEXITSTATUS(j));
790 mediaGetType(dialogMenuItem *self)
792 return ((dmenuOpenSimple(&MenuMedia, FALSE) && mediaDevice) ? DITEM_SUCCESS : DITEM_FAILURE);
795 /* Return TRUE if all the media variables are set up correctly */
800 return (DITEM_STATUS(mediaGetType(NULL)) == DITEM_SUCCESS);
804 /* Set the FTP username and password fields */
806 mediaSetFTPUserPass(dialogMenuItem *self)
810 if (variable_get_value(VAR_FTP_USER, "Please enter the username you wish to login as:", 0)) {
811 DialogInputAttrs |= DITEM_NO_ECHO;
812 pass = variable_get_value(VAR_FTP_PASS, "Please enter the password for this user:", 0);
813 DialogInputAttrs &= ~DITEM_NO_ECHO;
817 return (pass ? DITEM_SUCCESS : DITEM_FAILURE);
820 /* Set CPIO verbosity level */
822 mediaSetCPIOVerbosity(dialogMenuItem *self)
824 char *cp = variable_get(VAR_CPIO_VERBOSITY);
827 msgConfirm("CPIO Verbosity is not set to anything!");
828 return DITEM_FAILURE;
831 if (!strcmp(cp, "low"))
832 variable_set2(VAR_CPIO_VERBOSITY, "medium", 0);
833 else if (!strcmp(cp, "medium"))
834 variable_set2(VAR_CPIO_VERBOSITY, "high", 0);
835 else /* must be "high" - wrap around */
836 variable_set2(VAR_CPIO_VERBOSITY, "low", 0);
838 return DITEM_SUCCESS;
841 /* A generic open which follows a well-known "path" of places to look */
843 mediaGenericGet(char *base, const char *file)
847 snprintf(buf, PATH_MAX, "%s/%s", base, file);
848 if (file_readable(buf))
849 return fopen(buf, "r");
850 snprintf(buf, PATH_MAX, "%s/FreeBSD/%s", base, file);
851 if (file_readable(buf))
852 return fopen(buf, "r");
853 snprintf(buf, PATH_MAX, "%s/releases/%s", base, file);
854 if (file_readable(buf))
855 return fopen(buf, "r");
856 snprintf(buf, PATH_MAX, "%s/%s/%s", base, variable_get(VAR_RELNAME), file);
857 if (file_readable(buf))
858 return fopen(buf, "r");
859 snprintf(buf, PATH_MAX, "%s/releases/%s/%s", base, variable_get(VAR_RELNAME), file);
860 return fopen(buf, "r");