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 $
8 * $DragonFly: src/release/sysinstall/Attic/media.c,v 1.2 2003/06/17 04:27:21 dillon Exp $
11 * Jordan Hubbard. All rights reserved.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer,
18 * verbatim and that no modifications are made prior to this
20 * 2. Redistributions in binary form must reproduce the above copyright
21 * notice, this list of conditions and the following disclaimer in the
22 * documentation and/or other materials provided with the distribution.
24 * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 #include "sysinstall.h"
41 #include <sys/socket.h>
42 #include <sys/param.h>
43 #include <sys/mount.h>
44 #include <sys/errno.h>
45 #include <sys/fcntl.h>
49 #include <netinet/in.h>
50 #include <arpa/inet.h>
53 static Boolean got_intr = FALSE;
54 static Boolean ftp_skip_resolve = FALSE;
60 msgDebug("User generated interrupt.\n");
65 check_for_interrupt(void)
75 genericHook(dialogMenuItem *self, DeviceType type)
79 devs = deviceFind(self->prompt, type);
81 mediaDevice = devs[0];
82 return (devs ? DITEM_LEAVE_MENU : DITEM_FAILURE);
86 cdromHook(dialogMenuItem *self)
88 return genericHook(self, DEVICE_TYPE_CDROM);
94 static Boolean initted = FALSE;
98 cp = variable_get(VAR_MEDIA_TIMEOUT);
100 time = MEDIA_TIMEOUT;
107 _res.retry = 2; /* 2 times seems a reasonable number to me */
108 _res.retrans = time / 2; /* so spend half our alloted time on each try */
116 char *cp = variable_get(VAR_CPIO_VERBOSITY);
118 if (cp && !strcmp(cp, "high"))
120 else if (cp && !strcmp(cp, "medium"))
129 DEVICE_SHUTDOWN(mediaDevice);
134 * Return 1 if we successfully found and set the installation type to
138 mediaSetCDROM(dialogMenuItem *self)
144 devs = deviceFind(NULL, DEVICE_TYPE_CDROM);
145 cnt = deviceCount(devs);
147 if (self) /* Interactive? */
148 msgConfirm("No CD/DVD devices found! Please check that your system's\n"
149 "configuration is correct and that the CD/DVD drive is of a supported\n"
150 "type. For more information, consult the hardware guide\n"
152 return DITEM_FAILURE | DITEM_CONTINUE;
158 menu = deviceCreateMenu(&MenuMediaCDROM, DEVICE_TYPE_CDROM, cdromHook, NULL);
160 msgFatal("Unable to create CDROM menu! Something is seriously wrong.");
161 status = dmenuOpenSimple(menu, FALSE);
164 return DITEM_FAILURE;
167 mediaDevice = devs[0];
168 return (mediaDevice ? DITEM_SUCCESS | DITEM_LEAVE_MENU : DITEM_FAILURE);
172 floppyHook(dialogMenuItem *self)
174 return genericHook(self, DEVICE_TYPE_FLOPPY);
178 * Return 1 if we successfully found and set the installation type to
182 mediaSetFloppy(dialogMenuItem *self)
188 devs = deviceFind(NULL, DEVICE_TYPE_FLOPPY);
189 cnt = deviceCount(devs);
191 msgConfirm("No floppy devices found! Please check that your system's configuration\n"
192 "is correct. For more information, consult the hardware guide in the Doc\n"
194 return DITEM_FAILURE | DITEM_CONTINUE;
200 menu = deviceCreateMenu(&MenuMediaFloppy, DEVICE_TYPE_FLOPPY, floppyHook, NULL);
202 msgFatal("Unable to create Floppy menu! Something is seriously wrong.");
203 status = dmenuOpenSimple(menu, FALSE);
206 return DITEM_FAILURE;
209 mediaDevice = devs[0];
211 mediaDevice->private = NULL;
212 return (mediaDevice ? DITEM_LEAVE_MENU : DITEM_FAILURE);
216 DOSHook(dialogMenuItem *self)
218 return genericHook(self, DEVICE_TYPE_DOS);
222 * Return 1 if we successfully found and set the installation type to
223 * be a DOS partition.
226 mediaSetDOS(dialogMenuItem *self)
232 devs = deviceFind(NULL, DEVICE_TYPE_DOS);
233 cnt = deviceCount(devs);
235 msgConfirm("No DOS primary partitions found! This installation method is unavailable");
236 return DITEM_FAILURE | DITEM_CONTINUE;
242 menu = deviceCreateMenu(&MenuMediaDOS, DEVICE_TYPE_DOS, DOSHook, NULL);
244 msgFatal("Unable to create DOS menu! Something is seriously wrong.");
245 status = dmenuOpenSimple(menu, FALSE);
248 return DITEM_FAILURE;
251 mediaDevice = devs[0];
252 return (mediaDevice ? DITEM_LEAVE_MENU : DITEM_FAILURE);
256 tapeHook(dialogMenuItem *self)
258 return genericHook(self, DEVICE_TYPE_TAPE);
262 * Return 1 if we successfully found and set the installation type to
266 mediaSetTape(dialogMenuItem *self)
272 devs = deviceFind(NULL, DEVICE_TYPE_TAPE);
273 cnt = deviceCount(devs);
275 msgConfirm("No tape drive devices found! Please check that your system's configuration\n"
276 "is correct. For more information, consult the hardware guide in the Doc\n"
278 return DITEM_FAILURE | DITEM_CONTINUE;
284 menu = deviceCreateMenu(&MenuMediaTape, DEVICE_TYPE_TAPE, tapeHook, NULL);
286 msgFatal("Unable to create tape drive menu! Something is seriously wrong.");
287 status = dmenuOpenSimple(menu, FALSE);
290 return DITEM_FAILURE;
293 mediaDevice = devs[0];
297 val = msgGetInput("/var/tmp", "Please enter the name of a temporary directory containing\n"
298 "sufficient space for holding the contents of this tape (or\n"
299 "tapes). The contents of this directory will be removed\n"
300 "after installation, so be sure to specify a directory that\n"
301 "can be erased afterwards!\n");
305 mediaDevice->private = strdup(val);
307 return (mediaDevice ? DITEM_LEAVE_MENU : DITEM_FAILURE);
311 * Return 0 if we successfully found and set the installation type to
315 mediaSetFTP(dialogMenuItem *self)
317 static Device ftpDevice;
318 char *cp, hbuf[MAXHOSTNAMELEN], *hostname, *dir;
319 struct addrinfo hints, *res;
322 static Device *networkDev = NULL;
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?"))
330 if (!dmenuOpenSimple(&MenuMediaFTP, FALSE))
331 return DITEM_FAILURE;
333 cp = variable_get(VAR_FTP_PATH);
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;
351 if (strncmp("ftp://", cp, 6)) {
352 msgConfirm("Sorry, %s is an invalid URL!", cp);
353 variable_unset(VAR_FTP_PATH);
354 return DITEM_FAILURE;
356 SAFE_STRCPY(ftpDevice.name, cp);
357 SAFE_STRCPY(hbuf, cp + 6);
360 if (!networkDev || msgYesNo("You've already done the network configuration once,\n"
361 "would you like to skip over it now?") != 0) {
363 DEVICE_SHUTDOWN(networkDev);
364 if (!(networkDev = tcpDeviceSelect())) {
365 variable_unset(VAR_FTP_PATH);
366 return DITEM_FAILURE;
369 if (!DEVICE_INIT(networkDev)) {
371 msgDebug("mediaSetFTP: Net device init failed.\n");
372 variable_unset(VAR_FTP_PATH);
373 return DITEM_FAILURE;
375 if (*hostname == '[' && (cp = index(hostname + 1, ']')) != NULL &&
376 (*++cp == '\0' || *cp == '/' || *cp == ':')) {
381 cp = index(hostname, ':');
382 if (cp != NULL && *cp == ':') {
384 FtpPort = strtol(cp, 0, 0);
388 if ((dir = index(cp ? cp : hostname, '/')) != NULL)
391 msgDebug("hostname = `%s'\n", hostname);
392 msgDebug("dir = `%s'\n", dir ? dir : "/");
393 msgDebug("port # = `%d'\n", FtpPort);
395 if (!ftp_skip_resolve && variable_get(VAR_NAMESERVER)) {
396 msgNotify("Looking up host %s.", hostname);
398 msgDebug("Starting DNS.\n");
401 msgDebug("Looking up hostname, %s, using getaddrinfo(AI_NUMERICHOST).\n", hostname);
402 af = variable_cmp(VAR_IPV6_ENABLE, "YES") ? AF_INET : AF_UNSPEC;
403 memset(&hints, 0, sizeof(hints));
404 hints.ai_family = af;
405 hints.ai_socktype = SOCK_STREAM;
406 hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
407 if (getaddrinfo(hostname, NULL, &hints, &res) != 0) {
409 msgDebug("Looking up hostname, %s, using getaddrinfo().\n",
411 hints.ai_flags = AI_PASSIVE;
412 if (getaddrinfo(hostname, NULL, &hints, &res) != 0) {
413 msgConfirm("Cannot resolve hostname `%s'! Are you sure that"
414 " your\nname server, gateway and network interface are"
415 " correctly configured?", hostname);
417 DEVICE_SHUTDOWN(networkDev);
419 variable_unset(VAR_FTP_PATH);
420 return DITEM_FAILURE;
425 msgDebug("Found DNS entry for %s successfully..\n", hostname);
427 variable_set2(VAR_FTP_HOST, hostname, 0);
428 variable_set2(VAR_FTP_DIR, dir ? dir : "/", 0);
429 variable_set2(VAR_FTP_PORT, itoa(FtpPort), 0);
430 ftpDevice.type = DEVICE_TYPE_FTP;
431 ftpDevice.init = mediaInitFTP;
432 ftpDevice.get = mediaGetFTP;
433 ftpDevice.shutdown = mediaShutdownFTP;
434 ftpDevice.private = networkDev;
435 mediaDevice = &ftpDevice;
436 return DITEM_SUCCESS | DITEM_LEAVE_MENU | DITEM_RESTORE;
440 mediaSetFTPActive(dialogMenuItem *self)
442 variable_set2(VAR_FTP_STATE, "active", 0);
443 return mediaSetFTP(self);
447 mediaSetFTPPassive(dialogMenuItem *self)
449 variable_set2(VAR_FTP_STATE, "passive", 0);
450 return mediaSetFTP(self);
453 int mediaSetHTTP(dialogMenuItem *self)
457 char *cp, *idx, hbuf[MAXHOSTNAMELEN], *hostname;
459 int what = DITEM_RESTORE;
462 tmp = ftp_skip_resolve;
463 ftp_skip_resolve = TRUE;
464 result = mediaSetFTP(self);
465 ftp_skip_resolve = tmp;
467 if (DITEM_STATUS(result) != DITEM_SUCCESS)
470 cp = variable_get_value(VAR_HTTP_PROXY,
471 "Please enter the address of the HTTP proxy in this format:\n"
472 " hostname:port (the ':port' is optional, default is 3128)",0);
474 return DITEM_FAILURE;
475 SAFE_STRCPY(hbuf, cp);
477 if (*hostname == '[' && (idx = index(hostname + 1, ']')) != NULL &&
478 (*++idx == '\0' || *idx == ':')) {
482 idx = index(hostname, ':');
483 if (idx == NULL || *idx != ':')
484 HttpPort = 3128; /* try this as default */
487 HttpPort = strtol(idx, 0, 0);
490 variable_set2(VAR_HTTP_HOST, hostname, 0);
491 variable_set2(VAR_HTTP_PORT, itoa(HttpPort), 0);
493 msgDebug("VAR_FTP_PATH : %s",variable_get(VAR_FTP_PATH));
494 msgDebug("VAR_HTTP_HOST, _PORT: %s:%s",variable_get(VAR_HTTP_HOST),
495 variable_get(VAR_HTTP_PORT));
498 /* mediaDevice has been set by mediaSetFTP(), overwrite partly: */
499 mediaDevice->type = DEVICE_TYPE_HTTP;
500 mediaDevice->init = mediaInitHTTP;
501 mediaDevice->get = mediaGetHTTP;
502 mediaDevice->shutdown = dummyShutdown;
503 return DITEM_SUCCESS | DITEM_LEAVE_MENU | what;
508 mediaSetUFS(dialogMenuItem *self)
510 static Device ufsDevice;
515 cp = variable_get_value(VAR_UFS_PATH, "Enter a fully qualified pathname for the directory\n"
516 "containing the FreeBSD distribution files:", 0);
518 return DITEM_FAILURE;
520 /* If they gave us a CDROM or something, try and pick a better name */
522 strcpy(ufsDevice.name, "ufs");
524 strcpy(ufsDevice.name, st.f_fstypename);
526 ufsDevice.type = DEVICE_TYPE_UFS;
527 ufsDevice.init = dummyInit;
528 ufsDevice.get = mediaGetUFS;
529 ufsDevice.shutdown = dummyShutdown;
530 ufsDevice.private = strdup(cp);
531 mediaDevice = &ufsDevice;
532 return DITEM_LEAVE_MENU;
536 mediaSetNFS(dialogMenuItem *self)
538 static Device nfsDevice;
539 static Device *networkDev = NULL;
541 char hostname[MAXPATHLEN];
544 cp = variable_get_value(VAR_NFS_PATH, "Please enter the full NFS file specification for the remote\n"
545 "host and directory containing the FreeBSD distribution files.\n"
546 "This should be in the format: hostname:/some/freebsd/dir", 0);
548 return DITEM_FAILURE;
549 SAFE_STRCPY(hostname, cp);
550 if (!(idx = index(hostname, ':'))) {
551 msgConfirm("Invalid NFS path specification. Must be of the form:\n"
552 "host:/full/pathname/to/FreeBSD/distdir");
553 return DITEM_FAILURE;
555 SAFE_STRCPY(nfsDevice.name, hostname);
557 if (!networkDev || msgYesNo("You've already done the network configuration once,\n"
558 "would you like to skip over it now?") != 0) {
560 DEVICE_SHUTDOWN(networkDev);
561 if (!(networkDev = tcpDeviceSelect()))
562 return DITEM_FAILURE;
564 if (!DEVICE_INIT(networkDev)) {
566 msgDebug("mediaSetNFS: Net device init failed\n");
568 if (variable_get(VAR_NAMESERVER)) {
570 if ((inet_addr(hostname) == INADDR_NONE) && (gethostbyname(hostname) == NULL)) {
571 msgConfirm("Cannot resolve hostname `%s'! Are you sure that your\n"
572 "name server, gateway and network interface are correctly configured?", hostname);
574 DEVICE_SHUTDOWN(networkDev);
576 variable_unset(VAR_NFS_PATH);
577 return DITEM_FAILURE;
581 msgDebug("Found DNS entry for %s successfully..", hostname);
584 variable_set2(VAR_NFS_HOST, hostname, 0);
585 nfsDevice.type = DEVICE_TYPE_NFS;
586 nfsDevice.init = mediaInitNFS;
587 nfsDevice.get = mediaGetNFS;
588 nfsDevice.shutdown = mediaShutdownNFS;
589 nfsDevice.private = networkDev;
590 mediaDevice = &nfsDevice;
591 return DITEM_LEAVE_MENU;
595 mediaExtractDistBegin(char *dir, int *fd, int *zpid, int *cpid)
597 int i, pfd[2],qfd[2];
607 char *gunzip = RunningAsInit ? "/stand/gunzip" : "/usr/bin/gunzip";
609 dup2(qfd[0], 0); close(qfd[0]);
610 dup2(pfd[1], 1); close(pfd[1]);
615 open("/dev/null", O_WRONLY);
619 i = execl(gunzip, gunzip, 0);
621 msgDebug("%s command returns %d status\n", gunzip, i);
628 char *cpio = RunningAsInit ? "/stand/cpio" : "/usr/bin/cpio";
630 dup2(pfd[0], 0); close(pfd[0]);
638 close(1); open("/dev/null", O_WRONLY);
641 if (strlen(cpioVerbosity()))
642 i = execl(cpio, cpio, "-idum", cpioVerbosity(), "--block-size", mediaTapeBlocksize(), (char *)0);
644 i = execl(cpio, cpio, "-idum", "--block-size", mediaTapeBlocksize(), (char *)0);
646 msgDebug("%s command returns %d status\n", cpio, i);
655 mediaExtractDistEnd(int zpid, int cpid)
659 i = waitpid(zpid, &j, 0);
660 /* Don't check exit status - gunzip seems to return a bogus one! */
663 msgDebug("wait for gunzip returned status of %d!\n", i);
666 i = waitpid(cpid, &j, 0);
667 if (i < 0 || WEXITSTATUS(j)) {
669 msgDebug("cpio returned error status of %d!\n", WEXITSTATUS(j));
676 mediaExtractDist(char *dir, char *dist, FILE *fp)
678 int i, j, total, seconds, zpid, cpid, pfd[2], qfd[2];
680 struct timeval start, stop;
681 struct sigaction new, old;
688 pipe(pfd); /* read end */
689 pipe(qfd); /* write end */
692 char *gunzip = RunningAsInit ? "/stand/gunzip" : "/usr/bin/gunzip";
696 dup2(qfd[0], 0); close(qfd[0]);
699 dup2(pfd[1], 1); close(pfd[1]);
705 open("/dev/null", O_WRONLY);
707 i = execl(gunzip, gunzip, 0);
709 msgDebug("%s command returns %d status\n", gunzip, i);
714 char *cpio = RunningAsInit ? "/stand/cpio" : "/usr/bin/cpio";
717 dup2(pfd[0], 0); close(pfd[0]);
718 close (qfd[0]); close(qfd[1]);
725 dup2(open("/dev/null", O_WRONLY), 1);
728 if (strlen(cpioVerbosity()))
729 i = execl(cpio, cpio, "-idum", cpioVerbosity(), "--block-size", mediaTapeBlocksize(), (char *)0);
731 i = execl(cpio, cpio, "-idum", "--block-size", mediaTapeBlocksize(), (char *)0);
733 msgDebug("%s command returns %d status\n", cpio, i);
736 close(pfd[0]); close(pfd[1]);
740 (void)gettimeofday(&start, (struct timezone *)0);
742 /* Make ^C abort the current transfer rather than the whole show */
743 new.sa_handler = handle_intr;
745 (void)sigemptyset(&new.sa_mask);
746 sigaction(SIGINT, &new, &old);
748 while ((i = fread(buf, 1, BUFSIZ, fp)) > 0) {
749 if (check_for_interrupt()) {
750 msgConfirm("Failure to read from media: User interrupt.");
753 if (write(qfd[1], buf, i) != i) {
754 msgConfirm("Write error on transfer to cpio process, try of %d bytes.", i);
758 (void)gettimeofday(&stop, (struct timezone *)0);
759 stop.tv_sec = stop.tv_sec - start.tv_sec;
760 stop.tv_usec = stop.tv_usec - start.tv_usec;
761 if (stop.tv_usec < 0)
762 stop.tv_sec--, stop.tv_usec += 1000000;
763 seconds = stop.tv_sec + (stop.tv_usec / 1000000.0);
767 msgInfo("%10d bytes read from %s dist @ %.1f KB/sec.",
768 total, dist, (total / seconds) / 1024.0);
771 sigaction(SIGINT, &old, NULL); /* restore sigint */
774 i = waitpid(zpid, &j, 0);
775 /* Don't check exit status - gunzip seems to return a bogus one! */
778 msgDebug("wait for gunzip returned status of %d!\n", i);
781 i = waitpid(cpid, &j, 0);
782 if (i < 0 || WEXITSTATUS(j)) {
784 msgDebug("cpio returned error status of %d!\n", WEXITSTATUS(j));
791 mediaGetType(dialogMenuItem *self)
793 return ((dmenuOpenSimple(&MenuMedia, FALSE) && mediaDevice) ? DITEM_SUCCESS : DITEM_FAILURE);
796 /* Return TRUE if all the media variables are set up correctly */
801 return (DITEM_STATUS(mediaGetType(NULL)) == DITEM_SUCCESS);
805 /* Set the FTP username and password fields */
807 mediaSetFTPUserPass(dialogMenuItem *self)
811 if (variable_get_value(VAR_FTP_USER, "Please enter the username you wish to login as:", 0)) {
812 DialogInputAttrs |= DITEM_NO_ECHO;
813 pass = variable_get_value(VAR_FTP_PASS, "Please enter the password for this user:", 0);
814 DialogInputAttrs &= ~DITEM_NO_ECHO;
818 return (pass ? DITEM_SUCCESS : DITEM_FAILURE);
821 /* Set CPIO verbosity level */
823 mediaSetCPIOVerbosity(dialogMenuItem *self)
825 char *cp = variable_get(VAR_CPIO_VERBOSITY);
828 msgConfirm("CPIO Verbosity is not set to anything!");
829 return DITEM_FAILURE;
832 if (!strcmp(cp, "low"))
833 variable_set2(VAR_CPIO_VERBOSITY, "medium", 0);
834 else if (!strcmp(cp, "medium"))
835 variable_set2(VAR_CPIO_VERBOSITY, "high", 0);
836 else /* must be "high" - wrap around */
837 variable_set2(VAR_CPIO_VERBOSITY, "low", 0);
839 return DITEM_SUCCESS;
842 /* A generic open which follows a well-known "path" of places to look */
844 mediaGenericGet(char *base, const char *file)
848 snprintf(buf, PATH_MAX, "%s/%s", base, file);
849 if (file_readable(buf))
850 return fopen(buf, "r");
851 snprintf(buf, PATH_MAX, "%s/FreeBSD/%s", base, file);
852 if (file_readable(buf))
853 return fopen(buf, "r");
854 snprintf(buf, PATH_MAX, "%s/releases/%s", base, file);
855 if (file_readable(buf))
856 return fopen(buf, "r");
857 snprintf(buf, PATH_MAX, "%s/%s/%s", base, variable_get(VAR_RELNAME), file);
858 if (file_readable(buf))
859 return fopen(buf, "r");
860 snprintf(buf, PATH_MAX, "%s/releases/%s/%s", base, variable_get(VAR_RELNAME), file);
861 return fopen(buf, "r");