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.
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>
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"))
126 if (!mediaDevice || !mediaVerify() || !DEVICE_INIT(mediaDevice))
127 return DITEM_FAILURE;
128 return DITEM_SUCCESS;
135 DEVICE_SHUTDOWN(mediaDevice);
140 * Return 1 if we successfully found and set the installation type to
144 mediaSetCDROM(dialogMenuItem *self)
150 devs = deviceFind(NULL, DEVICE_TYPE_CDROM);
151 cnt = deviceCount(devs);
153 if (self) /* Interactive? */
154 msgConfirm("No CD/DVD devices found! Please check that your system's\n"
155 "configuration is correct and that the CD/DVD drive is of a supported\n"
156 "type. For more information, consult the hardware guide\n"
158 return DITEM_FAILURE | DITEM_CONTINUE;
164 menu = deviceCreateMenu(&MenuMediaCDROM, DEVICE_TYPE_CDROM, cdromHook, NULL);
166 msgFatal("Unable to create CDROM menu! Something is seriously wrong.");
167 status = dmenuOpenSimple(menu, FALSE);
170 return DITEM_FAILURE;
173 mediaDevice = devs[0];
174 return (mediaDevice ? DITEM_SUCCESS | DITEM_LEAVE_MENU : DITEM_FAILURE);
178 floppyHook(dialogMenuItem *self)
180 return genericHook(self, DEVICE_TYPE_FLOPPY);
184 * Return 1 if we successfully found and set the installation type to
188 mediaSetFloppy(dialogMenuItem *self)
194 devs = deviceFind(NULL, DEVICE_TYPE_FLOPPY);
195 cnt = deviceCount(devs);
197 msgConfirm("No floppy devices found! Please check that your system's configuration\n"
198 "is correct. For more information, consult the hardware guide in the Doc\n"
200 return DITEM_FAILURE | DITEM_CONTINUE;
206 menu = deviceCreateMenu(&MenuMediaFloppy, DEVICE_TYPE_FLOPPY, floppyHook, NULL);
208 msgFatal("Unable to create Floppy menu! Something is seriously wrong.");
209 status = dmenuOpenSimple(menu, FALSE);
212 return DITEM_FAILURE;
215 mediaDevice = devs[0];
217 mediaDevice->private = NULL;
218 return (mediaDevice ? DITEM_LEAVE_MENU : DITEM_FAILURE);
222 USBHook(dialogMenuItem *self)
224 return genericHook(self, DEVICE_TYPE_USB);
229 * Attempt to use USB as the installation media type.
232 mediaSetUSB(dialogMenuItem *self)
238 devs = deviceFind(NULL, DEVICE_TYPE_USB);
239 cnt = deviceCount(devs);
242 msgConfirm("No USB devices found (try Options/Re-scan Devices)");
243 return DITEM_FAILURE | DITEM_CONTINUE;
249 menu = deviceCreateMenu(&MenuMediaUSB, DEVICE_TYPE_USB, USBHook,
252 msgFatal("Unable to create USB menu! Something is " \
254 status = dmenuOpenSimple(menu, FALSE);
257 return DITEM_FAILURE;
260 mediaDevice = devs[0];
262 mediaDevice->private = NULL;
263 if (!variable_get(VAR_NONINTERACTIVE))
264 msgConfirm("Using USB device: %s", mediaDevice->name);
265 return (mediaDevice ? DITEM_LEAVE_MENU : DITEM_FAILURE);
269 DOSHook(dialogMenuItem *self)
271 return genericHook(self, DEVICE_TYPE_DOS);
275 * Return 1 if we successfully found and set the installation type to
276 * be a DOS partition.
279 mediaSetDOS(dialogMenuItem *self)
285 devs = deviceFind(NULL, DEVICE_TYPE_DOS);
286 cnt = deviceCount(devs);
288 msgConfirm("No DOS primary partitions found! This installation method is unavailable");
289 return DITEM_FAILURE | DITEM_CONTINUE;
295 menu = deviceCreateMenu(&MenuMediaDOS, DEVICE_TYPE_DOS, DOSHook, NULL);
297 msgFatal("Unable to create DOS menu! Something is seriously wrong.");
298 status = dmenuOpenSimple(menu, FALSE);
301 return DITEM_FAILURE;
304 mediaDevice = devs[0];
305 return (mediaDevice ? DITEM_LEAVE_MENU : DITEM_FAILURE);
309 * Return 0 if we successfully found and set the installation type to
313 mediaSetFTP(dialogMenuItem *self)
315 static Device ftpDevice;
316 char *cp, hbuf[MAXHOSTNAMELEN], *hostname, *dir;
317 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 (urllen >= sizeof(ftpDevice.name)) {
351 msgConfirm("Length of specified URL is %zu characters. Allowable maximum is %zu.",
352 urllen,sizeof(ftpDevice.name)-1);
353 variable_unset(VAR_FTP_PATH);
354 return DITEM_FAILURE;
357 if (strncmp("ftp://", cp, 6)) {
358 msgConfirm("Sorry, %s is an invalid URL!", cp);
359 variable_unset(VAR_FTP_PATH);
360 return DITEM_FAILURE;
362 SAFE_STRCPY(ftpDevice.name, cp);
363 SAFE_STRCPY(hbuf, cp + 6);
366 if (!networkDev || msgYesNo("You've already done the network configuration once,\n"
367 "would you like to skip over it now?") != 0) {
369 DEVICE_SHUTDOWN(networkDev);
370 if (!(networkDev = tcpDeviceSelect())) {
371 variable_unset(VAR_FTP_PATH);
372 return DITEM_FAILURE;
375 if (!DEVICE_INIT(networkDev)) {
377 msgDebug("mediaSetFTP: Net device init failed.\n");
378 variable_unset(VAR_FTP_PATH);
379 return DITEM_FAILURE;
381 if (*hostname == '[' && (cp = index(hostname + 1, ']')) != NULL &&
382 (*++cp == '\0' || *cp == '/' || *cp == ':')) {
387 cp = index(hostname, ':');
388 if (cp != NULL && *cp == ':') {
390 FtpPort = strtol(cp, 0, 0);
394 if ((dir = index(cp ? cp : hostname, '/')) != NULL)
397 msgDebug("hostname = `%s'\n", hostname);
398 msgDebug("dir = `%s'\n", dir ? dir : "/");
399 msgDebug("port # = `%d'\n", FtpPort);
401 if (!ftp_skip_resolve && variable_get(VAR_NAMESERVER)) {
402 msgNotify("Looking up host %s.", hostname);
404 msgDebug("Starting DNS.\n");
407 msgDebug("Looking up hostname, %s, using getaddrinfo(AI_NUMERICHOST).\n", hostname);
408 af = variable_cmp(VAR_IPV6_ENABLE, "YES") ? AF_INET : AF_UNSPEC;
409 memset(&hints, 0, sizeof(hints));
410 hints.ai_family = af;
411 hints.ai_socktype = SOCK_STREAM;
412 hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
413 if (getaddrinfo(hostname, NULL, &hints, &res) != 0) {
415 msgDebug("Looking up hostname, %s, using getaddrinfo().\n",
417 hints.ai_flags = AI_PASSIVE;
418 if (getaddrinfo(hostname, NULL, &hints, &res) != 0) {
419 msgConfirm("Cannot resolve hostname `%s'! Are you sure that"
420 " your\nname server, gateway and network interface are"
421 " correctly configured?", hostname);
423 DEVICE_SHUTDOWN(networkDev);
425 variable_unset(VAR_FTP_PATH);
426 return DITEM_FAILURE;
431 msgDebug("Found DNS entry for %s successfully..\n", hostname);
433 variable_set2(VAR_FTP_HOST, hostname, 0);
434 variable_set2(VAR_FTP_DIR, dir ? dir : "/", 0);
435 variable_set2(VAR_FTP_PORT, itoa(FtpPort), 0);
436 ftpDevice.type = DEVICE_TYPE_FTP;
437 ftpDevice.init = mediaInitFTP;
438 ftpDevice.get = mediaGetFTP;
439 ftpDevice.shutdown = mediaShutdownFTP;
440 ftpDevice.private = networkDev;
441 mediaDevice = &ftpDevice;
442 return DITEM_SUCCESS | DITEM_LEAVE_MENU | DITEM_RESTORE;
446 mediaSetFTPActive(dialogMenuItem *self)
448 variable_set2(VAR_FTP_STATE, "active", 0);
449 return mediaSetFTP(self);
453 mediaSetFTPPassive(dialogMenuItem *self)
455 variable_set2(VAR_FTP_STATE, "passive", 0);
456 return mediaSetFTP(self);
459 int mediaSetHTTP(dialogMenuItem *self)
463 char *cp, *idx, hbuf[MAXHOSTNAMELEN], *hostname;
465 int what = DITEM_RESTORE;
468 tmp = ftp_skip_resolve;
469 ftp_skip_resolve = TRUE;
470 result = mediaSetFTP(self);
471 ftp_skip_resolve = tmp;
473 if (DITEM_STATUS(result) != DITEM_SUCCESS)
476 cp = variable_get_value(VAR_HTTP_PROXY,
477 "Please enter the address of the HTTP proxy in this format:\n"
478 " hostname:port (the ':port' is optional, default is 3128)",0);
480 return DITEM_FAILURE;
481 SAFE_STRCPY(hbuf, cp);
483 if (*hostname == '[' && (idx = index(hostname + 1, ']')) != NULL &&
484 (*++idx == '\0' || *idx == ':')) {
488 idx = index(hostname, ':');
489 if (idx == NULL || *idx != ':')
490 HttpPort = 3128; /* try this as default */
493 HttpPort = strtol(idx, 0, 0);
496 variable_set2(VAR_HTTP_HOST, hostname, 0);
497 variable_set2(VAR_HTTP_PORT, itoa(HttpPort), 0);
499 msgDebug("VAR_FTP_PATH : %s\n",variable_get(VAR_FTP_PATH));
500 msgDebug("VAR_HTTP_HOST, _PORT: %s:%s\n",variable_get(VAR_HTTP_HOST),
501 variable_get(VAR_HTTP_PORT));
504 /* mediaDevice has been set by mediaSetFTP(), overwrite partly: */
505 mediaDevice->type = DEVICE_TYPE_HTTP;
506 mediaDevice->init = mediaInitHTTP;
507 mediaDevice->get = mediaGetHTTP;
508 mediaDevice->shutdown = dummyShutdown;
509 return DITEM_SUCCESS | DITEM_LEAVE_MENU | what;
514 mediaSetUFS(dialogMenuItem *self)
516 static Device ufsDevice;
521 cp = variable_get_value(VAR_UFS_PATH, "Enter a fully qualified pathname for the directory\n"
522 "containing the FreeBSD distribution files:", 0);
524 return DITEM_FAILURE;
526 /* If they gave us a CDROM or something, try and pick a better name */
528 strcpy(ufsDevice.name, "ufs");
530 strcpy(ufsDevice.name, st.f_fstypename);
532 ufsDevice.type = DEVICE_TYPE_UFS;
533 ufsDevice.init = dummyInit;
534 ufsDevice.get = mediaGetUFS;
535 ufsDevice.shutdown = dummyShutdown;
536 ufsDevice.private = strdup(cp);
537 mediaDevice = &ufsDevice;
538 return DITEM_LEAVE_MENU;
542 mediaSetNFS(dialogMenuItem *self)
544 static Device nfsDevice;
545 static Device *networkDev = NULL;
547 char hostname[MAXPATHLEN];
551 cp = variable_get_value(VAR_NFS_PATH, "Please enter the full NFS file specification for the remote\n"
552 "host and directory containing the FreeBSD distribution files.\n"
553 "This should be in the format: hostname:/some/freebsd/dir", 0);
555 return DITEM_FAILURE;
556 SAFE_STRCPY(hostname, cp);
557 if (!(idx = index(hostname, ':'))) {
558 msgConfirm("Invalid NFS path specification. Must be of the form:\n"
559 "host:/full/pathname/to/FreeBSD/distdir");
560 return DITEM_FAILURE;
562 pathlen = strlen(hostname);
563 if (pathlen >= sizeof(nfsDevice.name)) {
564 msgConfirm("Length of specified NFS path is %zu characters. Allowable maximum is %zu.",
565 pathlen,sizeof(nfsDevice.name)-1);
566 variable_unset(VAR_NFS_PATH);
567 return DITEM_FAILURE;
569 SAFE_STRCPY(nfsDevice.name, hostname);
571 if (!networkDev || msgYesNo("You've already done the network configuration once,\n"
572 "would you like to skip over it now?") != 0) {
574 DEVICE_SHUTDOWN(networkDev);
575 if (!(networkDev = tcpDeviceSelect()))
576 return DITEM_FAILURE;
578 if (!DEVICE_INIT(networkDev)) {
580 msgDebug("mediaSetNFS: Net device init failed\n");
582 if (variable_get(VAR_NAMESERVER)) {
584 if ((inet_addr(hostname) == INADDR_NONE) && (gethostbyname(hostname) == NULL)) {
585 msgConfirm("Cannot resolve hostname `%s'! Are you sure that your\n"
586 "name server, gateway and network interface are correctly configured?", hostname);
588 DEVICE_SHUTDOWN(networkDev);
590 variable_unset(VAR_NFS_PATH);
591 return DITEM_FAILURE;
595 msgDebug("Found DNS entry for %s successfully..\n", hostname);
598 variable_set2(VAR_NFS_HOST, hostname, 0);
599 nfsDevice.type = DEVICE_TYPE_NFS;
600 nfsDevice.init = mediaInitNFS;
601 nfsDevice.get = mediaGetNFS;
602 nfsDevice.shutdown = mediaShutdownNFS;
603 nfsDevice.private = networkDev;
604 mediaDevice = &nfsDevice;
605 return DITEM_LEAVE_MENU;
609 mediaExtractDistBegin(char *dir, int *fd, int *zpid, int *cpid)
611 int i, pfd[2],qfd[2];
621 char *unzipper = RunningAsInit ? "/stand/" UNZIPPER
622 : "/usr/bin/" UNZIPPER;
624 dup2(qfd[0], 0); close(qfd[0]);
625 dup2(pfd[1], 1); close(pfd[1]);
630 open("/dev/null", O_WRONLY);
634 i = execl(unzipper, unzipper, (char *)0);
636 msgDebug("%s command returns %d status\n", unzipper, i);
643 char *cpio = RunningAsInit ? "/stand/cpio" : "/usr/bin/cpio";
645 dup2(pfd[0], 0); close(pfd[0]);
653 close(1); open("/dev/null", O_WRONLY);
656 if (strlen(cpioVerbosity()))
657 i = execl(cpio, cpio, "-idum", cpioVerbosity(), (char *)0);
659 i = execl(cpio, cpio, "-idum", (char *)0);
661 msgDebug("%s command returns %d status\n", cpio, i);
670 mediaExtractDistEnd(int zpid, int cpid)
674 i = waitpid(zpid, &j, 0);
675 /* Don't check exit status - gunzip seems to return a bogus one! */
678 msgDebug("wait for %s returned status of %d!\n", UNZIPPER, i);
681 i = waitpid(cpid, &j, 0);
682 if (i < 0 || WEXITSTATUS(j)) {
684 msgDebug("cpio returned error status of %d!\n", WEXITSTATUS(j));
691 mediaExtractDist(char *dir, char *dist, FILE *fp)
693 int i, j, total, seconds, zpid, cpid, pfd[2], qfd[2];
695 struct timeval start, stop;
696 struct sigaction new, old;
703 pipe(pfd); /* read end */
704 pipe(qfd); /* write end */
707 char *unzipper = RunningAsInit ? "/stand/" UNZIPPER
708 : "/usr/bin/" UNZIPPER;
712 dup2(qfd[0], 0); close(qfd[0]);
715 dup2(pfd[1], 1); close(pfd[1]);
721 open("/dev/null", O_WRONLY);
723 i = execl(unzipper, unzipper, (char *)0);
725 msgDebug("%s command returns %d status\n", unzipper, i);
730 char *cpio = RunningAsInit ? "/stand/cpio" : "/usr/bin/cpio";
733 dup2(pfd[0], 0); close(pfd[0]);
734 close (qfd[0]); close(qfd[1]);
741 dup2(open("/dev/null", O_WRONLY), 1);
744 if (strlen(cpioVerbosity()))
745 i = execl(cpio, cpio, "-idum", cpioVerbosity(), (char *)0);
747 i = execl(cpio, cpio, "-idum", "--block-size", (char *)0);
749 msgDebug("%s command returns %d status\n", cpio, i);
752 close(pfd[0]); close(pfd[1]);
756 (void)gettimeofday(&start, (struct timezone *)0);
758 /* Make ^C abort the current transfer rather than the whole show */
759 new.sa_handler = handle_intr;
761 (void)sigemptyset(&new.sa_mask);
762 sigaction(SIGINT, &new, &old);
764 while ((i = fread(buf, 1, BUFSIZ, fp)) > 0) {
765 if (check_for_interrupt()) {
766 msgConfirm("Failure to read from media: User interrupt.");
769 if (write(qfd[1], buf, i) != i) {
770 msgConfirm("Write error on transfer to cpio process, try of %d bytes.", i);
774 (void)gettimeofday(&stop, (struct timezone *)0);
775 stop.tv_sec = stop.tv_sec - start.tv_sec;
776 stop.tv_usec = stop.tv_usec - start.tv_usec;
777 if (stop.tv_usec < 0)
778 stop.tv_sec--, stop.tv_usec += 1000000;
779 seconds = stop.tv_sec + (stop.tv_usec / 1000000.0);
783 msgInfo("%10d bytes read from %s dist @ %.1f KB/sec.",
784 total, dist, (total / seconds) / 1024.0);
787 sigaction(SIGINT, &old, NULL); /* restore sigint */
790 i = waitpid(zpid, &j, 0);
791 /* Don't check exit status - gunzip seems to return a bogus one! */
794 msgDebug("wait for %s returned status of %d!\n", UNZIPPER, i);
797 i = waitpid(cpid, &j, 0);
798 if (i < 0 || WEXITSTATUS(j)) {
800 msgDebug("cpio returned error status of %d!\n", WEXITSTATUS(j));
807 mediaGetType(dialogMenuItem *self)
809 return ((dmenuOpenSimple(&MenuMedia, FALSE) && mediaDevice) ? DITEM_SUCCESS : DITEM_FAILURE);
812 /* Return TRUE if all the media variables are set up correctly */
817 return (DITEM_STATUS(mediaGetType(NULL)) == DITEM_SUCCESS);
821 /* Set the FTP username and password fields */
823 mediaSetFTPUserPass(dialogMenuItem *self)
827 if (variable_get_value(VAR_FTP_USER, "Please enter the username you wish to login as:", 0)) {
828 DialogInputAttrs |= DITEM_NO_ECHO;
829 pass = variable_get_value(VAR_FTP_PASS, "Please enter the password for this user:", 0);
830 DialogInputAttrs &= ~DITEM_NO_ECHO;
834 return (pass ? DITEM_SUCCESS : DITEM_FAILURE);
837 /* Set CPIO verbosity level */
839 mediaSetCPIOVerbosity(dialogMenuItem *self)
841 char *cp = variable_get(VAR_CPIO_VERBOSITY);
844 msgConfirm("CPIO Verbosity is not set to anything!");
845 return DITEM_FAILURE;
848 if (!strcmp(cp, "low"))
849 variable_set2(VAR_CPIO_VERBOSITY, "high", 0);
850 else /* must be "high" - wrap around */
851 variable_set2(VAR_CPIO_VERBOSITY, "low", 0);
853 return DITEM_SUCCESS;
856 /* A generic open which follows a well-known "path" of places to look */
858 mediaGenericGet(char *base, const char *file)
862 snprintf(buf, PATH_MAX, "%s/%s", base, file);
863 if (file_readable(buf))
864 return fopen(buf, "r");
865 snprintf(buf, PATH_MAX, "%s/FreeBSD/%s", base, file);
866 if (file_readable(buf))
867 return fopen(buf, "r");
868 snprintf(buf, PATH_MAX, "%s/releases/%s", base, file);
869 if (file_readable(buf))
870 return fopen(buf, "r");
871 snprintf(buf, PATH_MAX, "%s/%s/%s", base, variable_get(VAR_RELNAME), file);
872 if (file_readable(buf))
873 return fopen(buf, "r");
874 snprintf(buf, PATH_MAX, "%s/releases/%s/%s", base, variable_get(VAR_RELNAME), file);
875 return fopen(buf, "r");