]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - usr.sbin/sysinstall/media.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / usr.sbin / sysinstall / media.c
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
53 static Boolean got_intr = FALSE;
54 static Boolean ftp_skip_resolve = FALSE;
55
56 /* timeout handler */
57 static void
58 handle_intr(int sig)
59 {
60     msgDebug("User generated interrupt.\n");
61     got_intr = TRUE;
62 }
63
64 static int
65 check_for_interrupt(void)
66 {
67     if (got_intr) {
68         got_intr = FALSE;
69         return TRUE;
70     }
71     return FALSE;
72 }
73
74 static int
75 genericHook(dialogMenuItem *self, DeviceType type)
76 {
77     Device **devs;
78
79     devs = deviceFind(self->prompt, type);
80     if (devs)
81         mediaDevice = devs[0];
82     return (devs ? DITEM_LEAVE_MENU : DITEM_FAILURE);
83 }
84
85 static int
86 cdromHook(dialogMenuItem *self)
87 {
88     return genericHook(self, DEVICE_TYPE_CDROM);
89 }
90
91 static void
92 kickstart_dns(void)
93 {
94     static Boolean initted = FALSE;
95     int time;
96     char *cp;
97
98     cp = variable_get(VAR_MEDIA_TIMEOUT);
99     if (!cp)
100         time = MEDIA_TIMEOUT;
101     else
102         time = atoi(cp);
103     if (!time)
104         time = 100;
105     if (!initted) {
106         res_init();
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 */
109         initted = TRUE;
110     }
111 }
112
113 char *
114 cpioVerbosity()
115 {
116     char *cp = variable_get(VAR_CPIO_VERBOSITY);
117
118     if (cp && !strcmp(cp, "high"))
119         return "-v";
120     else if (cp && !strcmp(cp, "medium"))
121         return "-V";
122     return "";
123 }
124
125 int
126 mediaOpen(void)
127 {
128     if (!mediaDevice || !mediaVerify() || !DEVICE_INIT(mediaDevice))
129         return DITEM_FAILURE;
130     return DITEM_SUCCESS;
131 }
132
133 void
134 mediaClose(void)
135 {
136     if (mediaDevice)
137         DEVICE_SHUTDOWN(mediaDevice);
138     mediaDevice = NULL;
139 }
140
141 /*
142  * Return 1 if we successfully found and set the installation type to
143  * be a CD.
144  */
145 int
146 mediaSetCDROM(dialogMenuItem *self)
147 {
148     Device **devs;
149     int cnt;
150
151     mediaClose();
152     devs = deviceFind(NULL, DEVICE_TYPE_CDROM);
153     cnt = deviceCount(devs);
154     if (!cnt) {
155         if (self)       /* Interactive? */
156             msgConfirm("No CD/DVD devices found!  Please check that your system's\n"
157                        "configuration is correct and that the CD/DVD drive is of a supported\n"
158                        "type.  For more information, consult the hardware guide\n"
159                        "in the Doc menu.");
160         return DITEM_FAILURE | DITEM_CONTINUE;
161     }
162     else if (cnt > 1) {
163         DMenu *menu;
164         int status;
165         
166         menu = deviceCreateMenu(&MenuMediaCDROM, DEVICE_TYPE_CDROM, cdromHook, NULL);
167         if (!menu)
168             msgFatal("Unable to create CDROM menu!  Something is seriously wrong.");
169         status = dmenuOpenSimple(menu, FALSE);
170         free(menu);
171         if (!status)
172             return DITEM_FAILURE;
173     }
174     else
175         mediaDevice = devs[0];
176     return (mediaDevice ? DITEM_SUCCESS | DITEM_LEAVE_MENU : DITEM_FAILURE);
177 }
178
179 static int
180 floppyHook(dialogMenuItem *self)
181 {
182     return genericHook(self, DEVICE_TYPE_FLOPPY);
183 }
184
185 /*
186  * Return 1 if we successfully found and set the installation type to
187  * be a floppy
188  */
189 int
190 mediaSetFloppy(dialogMenuItem *self)
191 {
192     Device **devs;
193     int cnt;
194
195     mediaClose();
196     devs = deviceFind(NULL, DEVICE_TYPE_FLOPPY);
197     cnt = deviceCount(devs);
198     if (!cnt) {
199         msgConfirm("No floppy devices found!  Please check that your system's configuration\n"
200                    "is correct.  For more information, consult the hardware guide in the Doc\n"
201                    "menu.");
202         return DITEM_FAILURE | DITEM_CONTINUE;
203     }
204     else if (cnt > 1) {
205         DMenu *menu;
206         int status;
207
208         menu = deviceCreateMenu(&MenuMediaFloppy, DEVICE_TYPE_FLOPPY, floppyHook, NULL);
209         if (!menu)
210             msgFatal("Unable to create Floppy menu!  Something is seriously wrong.");
211         status = dmenuOpenSimple(menu, FALSE);
212         free(menu);
213         if (!status)
214             return DITEM_FAILURE;
215     }
216     else
217         mediaDevice = devs[0];
218     if (mediaDevice)
219         mediaDevice->private = NULL;
220     return (mediaDevice ? DITEM_LEAVE_MENU : DITEM_FAILURE);
221 }
222
223 static int
224 DOSHook(dialogMenuItem *self)
225 {
226     return genericHook(self, DEVICE_TYPE_DOS);
227 }
228
229 /*
230  * Return 1 if we successfully found and set the installation type to
231  * be a DOS partition.
232  */
233 int
234 mediaSetDOS(dialogMenuItem *self)
235 {
236     Device **devs;
237     int cnt;
238
239     mediaClose();
240     devs = deviceFind(NULL, DEVICE_TYPE_DOS);
241     cnt = deviceCount(devs);
242     if (!cnt) {
243         msgConfirm("No DOS primary partitions found!  This installation method is unavailable");
244         return DITEM_FAILURE | DITEM_CONTINUE;
245     }
246     else if (cnt > 1) {
247         DMenu *menu;
248         int status;
249
250         menu = deviceCreateMenu(&MenuMediaDOS, DEVICE_TYPE_DOS, DOSHook, NULL);
251         if (!menu)
252             msgFatal("Unable to create DOS menu!  Something is seriously wrong.");
253         status = dmenuOpenSimple(menu, FALSE);
254         free(menu);
255         if (!status)
256             return DITEM_FAILURE;
257     }
258     else
259         mediaDevice = devs[0];
260     return (mediaDevice ? DITEM_LEAVE_MENU : DITEM_FAILURE);
261 }
262
263 static int
264 tapeHook(dialogMenuItem *self)
265 {
266     return genericHook(self, DEVICE_TYPE_TAPE);
267 }
268
269 /*
270  * Return 1 if we successfully found and set the installation type to
271  * be a tape drive.
272  */
273 int
274 mediaSetTape(dialogMenuItem *self)
275 {
276     Device **devs;
277     int cnt;
278
279     mediaClose();
280     devs = deviceFind(NULL, DEVICE_TYPE_TAPE);
281     cnt = deviceCount(devs);
282     if (!cnt) {
283         msgConfirm("No tape drive devices found!  Please check that your system's configuration\n"
284                    "is correct.  For more information, consult the hardware guide in the Doc\n"
285                    "menu.");
286         return DITEM_FAILURE | DITEM_CONTINUE;
287     }
288     else if (cnt > 1) {
289         DMenu *menu;
290         int status;
291
292         menu = deviceCreateMenu(&MenuMediaTape, DEVICE_TYPE_TAPE, tapeHook, NULL);
293         if (!menu)
294             msgFatal("Unable to create tape drive menu!  Something is seriously wrong.");
295         status = dmenuOpenSimple(menu, FALSE);
296         free(menu);
297         if (!status)
298             return DITEM_FAILURE;
299     }
300     else
301         mediaDevice = devs[0];
302     if (mediaDevice) {
303         char *val;
304
305         val = msgGetInput("/var/tmp", "Please enter the name of a temporary directory containing\n"
306                           "sufficient space for holding the contents of this tape (or\n"
307                           "tapes).  The contents of this directory will be removed\n"
308                           "after installation, so be sure to specify a directory that\n"
309                           "can be erased afterwards!\n");
310         if (!val)
311             mediaDevice = NULL;
312         else
313             mediaDevice->private = strdup(val);
314     }
315     return (mediaDevice ? DITEM_LEAVE_MENU : DITEM_FAILURE);
316 }
317
318 /*
319  * Return 0 if we successfully found and set the installation type to
320  * be an ftp server
321  */
322 int
323 mediaSetFTP(dialogMenuItem *self)
324 {
325     static Device ftpDevice;
326     char *cp, hbuf[MAXHOSTNAMELEN], *hostname, *dir;
327     struct addrinfo hints, *res;
328     int af;
329     size_t urllen;
330     extern int FtpPort;
331     static Device *networkDev = NULL;
332
333     mediaClose();
334     cp = variable_get(VAR_FTP_PATH);
335     /* If we've been through here before ... */
336     if (networkDev && cp && msgYesNo("Re-use old FTP site selection values?"))
337         cp = NULL;
338     if (!cp) {
339         if (!dmenuOpenSimple(&MenuMediaFTP, FALSE))
340             return DITEM_FAILURE;
341         else
342             cp = variable_get(VAR_FTP_PATH);
343     }
344     if (!cp)
345         return DITEM_FAILURE;
346     else if (!strcmp(cp, "other")) {
347         variable_set2(VAR_FTP_PATH, "ftp://", 0);
348         cp = variable_get_value(VAR_FTP_PATH, "Please specify the URL of a FreeBSD distribution on a\n"
349                                 "remote ftp site.  This site must accept either anonymous\n"
350                                 "ftp or you should have set an ftp username and password\n"
351                                 "in the Options screen.\n\n"
352                                 "A URL looks like this:  ftp://<hostname>/<path>\n"
353                                 "Where <path> is relative to the anonymous ftp directory or the\n"
354                                 "home directory of the user being logged in as.", 0);
355         if (!cp || !*cp || !strcmp(cp, "ftp://")) {
356             variable_unset(VAR_FTP_PATH);
357             return DITEM_FAILURE;
358         }
359         urllen = strlen(cp);
360         if (urllen >= sizeof(ftpDevice.name)) {
361             msgConfirm("Length of specified URL is %d characters. Allowable maximum is %d.",
362                         urllen,sizeof(ftpDevice.name)-1);
363             variable_unset(VAR_FTP_PATH);
364             return DITEM_FAILURE;
365         }
366     }
367     if (strncmp("ftp://", cp, 6)) {
368         msgConfirm("Sorry, %s is an invalid URL!", cp);
369         variable_unset(VAR_FTP_PATH);
370         return DITEM_FAILURE;
371     }
372     SAFE_STRCPY(ftpDevice.name, cp);
373     SAFE_STRCPY(hbuf, cp + 6);
374     hostname = hbuf;
375
376     if (!networkDev || msgYesNo("You've already done the network configuration once,\n"
377                                 "would you like to skip over it now?") != 0) {
378         if (networkDev)
379             DEVICE_SHUTDOWN(networkDev);
380         if (!(networkDev = tcpDeviceSelect())) {
381             variable_unset(VAR_FTP_PATH);
382             return DITEM_FAILURE;
383         }
384     }
385     if (!DEVICE_INIT(networkDev)) {
386         if (isDebug())
387             msgDebug("mediaSetFTP: Net device init failed.\n");
388         variable_unset(VAR_FTP_PATH);
389         return DITEM_FAILURE;
390     }
391     if (*hostname == '[' && (cp = index(hostname + 1, ']')) != NULL &&
392         (*++cp == '\0' || *cp == '/' || *cp == ':')) {
393         ++hostname;
394         *(cp - 1) = '\0';
395     }
396     else
397         cp = index(hostname, ':');
398     if (cp != NULL && *cp == ':') {
399         *(cp++) = '\0';
400         FtpPort = strtol(cp, 0, 0);
401     }
402     else
403         FtpPort = 21;
404     if ((dir = index(cp ? cp : hostname, '/')) != NULL)
405         *(dir++) = '\0';
406     if (isDebug()) {
407         msgDebug("hostname = `%s'\n", hostname);
408         msgDebug("dir = `%s'\n", dir ? dir : "/");
409         msgDebug("port # = `%d'\n", FtpPort);
410     }
411     if (!ftp_skip_resolve && variable_get(VAR_NAMESERVER)) {
412         msgNotify("Looking up host %s.", hostname);
413         if (isDebug())
414             msgDebug("Starting DNS.\n");
415         kickstart_dns();
416         if (isDebug())
417             msgDebug("Looking up hostname, %s, using getaddrinfo(AI_NUMERICHOST).\n", hostname);
418         af = variable_cmp(VAR_IPV6_ENABLE, "YES") ? AF_INET : AF_UNSPEC;
419         memset(&hints, 0, sizeof(hints));
420         hints.ai_family = af;
421         hints.ai_socktype = SOCK_STREAM;
422         hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
423         if (getaddrinfo(hostname, NULL, &hints, &res) != 0) {
424             if (isDebug())
425                 msgDebug("Looking up hostname, %s, using getaddrinfo().\n",
426                          hostname);
427             hints.ai_flags = AI_PASSIVE;
428             if (getaddrinfo(hostname, NULL, &hints, &res) != 0) {
429                 msgConfirm("Cannot resolve hostname `%s'!  Are you sure that"
430                         " your\nname server, gateway and network interface are"
431                         " correctly configured?", hostname);
432                 if (networkDev)
433                     DEVICE_SHUTDOWN(networkDev);
434                 networkDev = NULL;
435                 variable_unset(VAR_FTP_PATH);
436                 return DITEM_FAILURE;
437             }
438         }
439         freeaddrinfo(res);
440         if (isDebug())
441             msgDebug("Found DNS entry for %s successfully..\n", hostname);
442     }
443     variable_set2(VAR_FTP_HOST, hostname, 0);
444     variable_set2(VAR_FTP_DIR, dir ? dir : "/", 0);
445     variable_set2(VAR_FTP_PORT, itoa(FtpPort), 0);
446     ftpDevice.type = DEVICE_TYPE_FTP;
447     ftpDevice.init = mediaInitFTP;
448     ftpDevice.get = mediaGetFTP;
449     ftpDevice.shutdown = mediaShutdownFTP;
450     ftpDevice.private = networkDev;
451     mediaDevice = &ftpDevice;
452     return DITEM_SUCCESS | DITEM_LEAVE_MENU | DITEM_RESTORE;
453 }
454
455 int
456 mediaSetFTPActive(dialogMenuItem *self)
457 {
458     variable_set2(VAR_FTP_STATE, "active", 0);
459     return mediaSetFTP(self);
460 }
461
462 int
463 mediaSetFTPPassive(dialogMenuItem *self)
464 {
465     variable_set2(VAR_FTP_STATE, "passive", 0);
466     return mediaSetFTP(self);
467 }
468
469 int mediaSetHTTP(dialogMenuItem *self)
470 {
471     Boolean tmp;
472     int result;
473     char *cp, *idx, hbuf[MAXHOSTNAMELEN], *hostname;
474     int HttpPort;
475     int what = DITEM_RESTORE;
476
477
478     tmp = ftp_skip_resolve;
479     ftp_skip_resolve = TRUE;
480     result = mediaSetFTP(self);
481     ftp_skip_resolve = tmp;
482
483     if (DITEM_STATUS(result) != DITEM_SUCCESS)
484         return result;
485  
486     cp = variable_get_value(VAR_HTTP_PROXY,
487         "Please enter the address of the HTTP proxy in this format:\n"
488         " hostname:port (the ':port' is optional, default is 3128)",0);
489     if (!cp)
490         return DITEM_FAILURE;
491     SAFE_STRCPY(hbuf, cp);
492     hostname = hbuf;
493     if (*hostname == '[' && (idx = index(hostname + 1, ']')) != NULL &&
494         (*++idx == '\0' || *idx == ':')) {
495         ++hostname;
496         *(idx - 1) = '\0';
497     } else
498         idx = index(hostname, ':');
499     if (idx == NULL || *idx != ':')
500         HttpPort = 3128;                /* try this as default */
501     else {
502         *(idx++) = '\0';
503         HttpPort = strtol(idx, 0, 0);
504     }
505
506     variable_set2(VAR_HTTP_HOST, hostname, 0);
507     variable_set2(VAR_HTTP_PORT, itoa(HttpPort), 0);
508     if (isDebug()) {
509       msgDebug("VAR_FTP_PATH : %s\n",variable_get(VAR_FTP_PATH));
510       msgDebug("VAR_HTTP_HOST, _PORT: %s:%s\n",variable_get(VAR_HTTP_HOST),
511                                              variable_get(VAR_HTTP_PORT));
512     }
513
514     /* mediaDevice has been set by mediaSetFTP(), overwrite partly: */
515     mediaDevice->type = DEVICE_TYPE_HTTP;
516     mediaDevice->init = mediaInitHTTP;
517     mediaDevice->get = mediaGetHTTP;
518     mediaDevice->shutdown = dummyShutdown;
519     return DITEM_SUCCESS | DITEM_LEAVE_MENU | what;
520 }
521    
522
523 int
524 mediaSetUFS(dialogMenuItem *self)
525 {
526     static Device ufsDevice;
527     struct statfs st;
528     char *cp;
529
530     mediaClose();
531     cp = variable_get_value(VAR_UFS_PATH, "Enter a fully qualified pathname for the directory\n"
532                             "containing the FreeBSD distribution files:", 0);
533     if (!cp)
534         return DITEM_FAILURE;
535
536     /* If they gave us a CDROM or something, try and pick a better name */
537     if (statfs(cp, &st))
538         strcpy(ufsDevice.name, "ufs");
539     else
540         strcpy(ufsDevice.name, st.f_fstypename);
541
542     ufsDevice.type = DEVICE_TYPE_UFS;
543     ufsDevice.init = dummyInit;
544     ufsDevice.get = mediaGetUFS;
545     ufsDevice.shutdown = dummyShutdown;
546     ufsDevice.private = strdup(cp);
547     mediaDevice = &ufsDevice;
548     return DITEM_LEAVE_MENU;
549 }
550
551 int
552 mediaSetNFS(dialogMenuItem *self)
553 {
554     static Device nfsDevice;
555     static Device *networkDev = NULL;
556     char *cp, *idx;
557     char hostname[MAXPATHLEN];
558     size_t pathlen;
559
560     mediaClose();
561     cp = variable_get_value(VAR_NFS_PATH, "Please enter the full NFS file specification for the remote\n"
562                             "host and directory containing the FreeBSD distribution files.\n"
563                             "This should be in the format:  hostname:/some/freebsd/dir", 0);
564     if (!cp)
565         return DITEM_FAILURE;
566     SAFE_STRCPY(hostname, cp);
567     if (!(idx = index(hostname, ':'))) {
568         msgConfirm("Invalid NFS path specification.  Must be of the form:\n"
569                    "host:/full/pathname/to/FreeBSD/distdir");
570         return DITEM_FAILURE;
571     }
572     pathlen = strlen(hostname);
573     if (pathlen >= sizeof(nfsDevice.name)) {
574         msgConfirm("Length of specified NFS path is %d characters. Allowable maximum is %d.",
575                    pathlen,sizeof(nfsDevice.name)-1);
576         variable_unset(VAR_NFS_PATH);
577         return DITEM_FAILURE;
578     }
579     SAFE_STRCPY(nfsDevice.name, hostname);
580     *idx = '\0';
581     if (!networkDev || msgYesNo("You've already done the network configuration once,\n"
582                                 "would you like to skip over it now?") != 0) {
583         if (networkDev)
584             DEVICE_SHUTDOWN(networkDev);
585         if (!(networkDev = tcpDeviceSelect()))
586             return DITEM_FAILURE;
587     }
588     if (!DEVICE_INIT(networkDev)) {
589         if (isDebug())
590             msgDebug("mediaSetNFS: Net device init failed\n");
591     }
592     if (variable_get(VAR_NAMESERVER)) {
593         kickstart_dns();
594         if ((inet_addr(hostname) == INADDR_NONE) && (gethostbyname(hostname) == NULL)) {
595             msgConfirm("Cannot resolve hostname `%s'!  Are you sure that your\n"
596                        "name server, gateway and network interface are correctly configured?", hostname);
597             if (networkDev)
598                 DEVICE_SHUTDOWN(networkDev);
599             networkDev = NULL;
600             variable_unset(VAR_NFS_PATH);
601             return DITEM_FAILURE;
602         }
603         else {
604             if (isDebug())
605                 msgDebug("Found DNS entry for %s successfully..\n", hostname);
606         }
607     }
608     variable_set2(VAR_NFS_HOST, hostname, 0);
609     nfsDevice.type = DEVICE_TYPE_NFS;
610     nfsDevice.init = mediaInitNFS;
611     nfsDevice.get = mediaGetNFS;
612     nfsDevice.shutdown = mediaShutdownNFS;
613     nfsDevice.private = networkDev;
614     mediaDevice = &nfsDevice;
615     return DITEM_LEAVE_MENU;
616 }
617
618 Boolean
619 mediaExtractDistBegin(char *dir, int *fd, int *zpid, int *cpid)
620 {
621     int i, pfd[2],qfd[2];
622
623     if (!dir)
624         dir = "/";
625     Mkdir(dir);
626     chdir(dir);
627     pipe(pfd);
628     pipe(qfd);
629     *zpid = fork();
630     if (!*zpid) {
631         char *unzipper = RunningAsInit ? "/stand/" UNZIPPER
632             : "/usr/bin/" UNZIPPER;
633
634         dup2(qfd[0], 0); close(qfd[0]);
635         dup2(pfd[1], 1); close(pfd[1]);
636         if (DebugFD != -1)
637             dup2(DebugFD, 2);
638         else {
639             close(2);
640             open("/dev/null", O_WRONLY);
641         }
642         close(qfd[1]);
643         close(pfd[0]);
644         i = execl(unzipper, unzipper, (char *)0);
645         if (isDebug())
646             msgDebug("%s command returns %d status\n", unzipper, i);
647         exit(i);
648     }
649     *fd = qfd[1];
650     close(qfd[0]);
651     *cpid = fork();
652     if (!*cpid) {
653         char *cpio = RunningAsInit ? "/stand/cpio" : "/usr/bin/cpio";
654
655         dup2(pfd[0], 0); close(pfd[0]);
656         close(pfd[1]);
657         close(qfd[1]);
658         if (DebugFD != -1) {
659             dup2(DebugFD, 1);
660             dup2(DebugFD, 2);
661         }
662         else {
663             close(1); open("/dev/null", O_WRONLY);
664             dup2(1, 2);
665         }
666         if (strlen(cpioVerbosity()))
667             i = execl(cpio, cpio, "-idum", cpioVerbosity(), "--block-size", mediaTapeBlocksize(), (char *)0);
668         else
669             i = execl(cpio, cpio, "-idum", "--block-size", mediaTapeBlocksize(), (char *)0);
670         if (isDebug())
671             msgDebug("%s command returns %d status\n", cpio, i);
672         exit(i);
673     }
674     close(pfd[0]);
675     close(pfd[1]);
676     return TRUE;
677 }
678
679 Boolean
680 mediaExtractDistEnd(int zpid, int cpid)
681 {
682     int i,j;
683
684     i = waitpid(zpid, &j, 0);
685     /* Don't check exit status - gunzip seems to return a bogus one! */
686     if (i < 0) {
687         if (isDebug())
688             msgDebug("wait for %s returned status of %d!\n", UNZIPPER, i);
689         return FALSE;
690     }
691     i = waitpid(cpid, &j, 0);
692     if (i < 0 || WEXITSTATUS(j)) {
693         if (isDebug())
694             msgDebug("cpio returned error status of %d!\n", WEXITSTATUS(j));
695         return FALSE;
696     }
697     return TRUE;
698 }
699
700 Boolean
701 mediaExtractDist(char *dir, char *dist, FILE *fp)
702 {
703     int i, j, total, seconds, zpid, cpid, pfd[2], qfd[2];
704     char buf[BUFSIZ];
705     struct timeval start, stop;
706     struct sigaction new, old;
707
708     if (!dir)
709         dir = "/";
710
711     Mkdir(dir);
712     chdir(dir);
713     pipe(pfd);  /* read end */
714     pipe(qfd);  /* write end */
715     zpid = fork();
716     if (!zpid) {
717         char *unzipper = RunningAsInit ? "/stand/" UNZIPPER
718             : "/usr/bin/" UNZIPPER;
719
720         fclose(fp);
721         close(qfd[1]);
722         dup2(qfd[0], 0); close(qfd[0]);
723
724         close(pfd[0]); 
725         dup2(pfd[1], 1); close(pfd[1]);
726
727         if (DebugFD != -1)
728             dup2(DebugFD, 2);
729         else {
730             close(2);
731             open("/dev/null", O_WRONLY);
732         }
733         i = execl(unzipper, unzipper, (char *)0);
734         if (isDebug())
735             msgDebug("%s command returns %d status\n", unzipper, i);
736         exit(i);
737     }
738     cpid = fork();
739     if (!cpid) {
740         char *cpio = RunningAsInit ? "/stand/cpio" : "/usr/bin/cpio";
741
742         close(pfd[1]);
743         dup2(pfd[0], 0); close(pfd[0]);
744         close (qfd[0]); close(qfd[1]);
745         fclose(fp);
746         if (DebugFD != -1) {
747             dup2(DebugFD, 1);
748             dup2(DebugFD, 2);
749         }
750         else {
751             dup2(open("/dev/null", O_WRONLY), 1);
752             dup2(1, 2);
753         }
754         if (strlen(cpioVerbosity()))
755             i = execl(cpio, cpio, "-idum", cpioVerbosity(), "--block-size", mediaTapeBlocksize(), (char *)0);
756         else
757             i = execl(cpio, cpio, "-idum", "--block-size", mediaTapeBlocksize(), (char *)0);
758         if (isDebug())
759             msgDebug("%s command returns %d status\n", cpio, i);
760         exit(i);
761     }
762     close(pfd[0]); close(pfd[1]);
763     close(qfd[0]);
764
765     total = 0;
766     (void)gettimeofday(&start, (struct timezone *)0);
767
768     /* Make ^C abort the current transfer rather than the whole show */
769     new.sa_handler = handle_intr;
770     new.sa_flags = 0;
771     (void)sigemptyset(&new.sa_mask);
772     sigaction(SIGINT, &new, &old);
773
774     while ((i = fread(buf, 1, BUFSIZ, fp)) > 0) {
775         if (check_for_interrupt()) {
776             msgConfirm("Failure to read from media:  User interrupt.");
777             break;
778         }
779         if (write(qfd[1], buf, i) != i) {
780             msgConfirm("Write error on transfer to cpio process, try of %d bytes.", i);
781             break;
782         }
783         else {
784             (void)gettimeofday(&stop, (struct timezone *)0);
785             stop.tv_sec = stop.tv_sec - start.tv_sec;
786             stop.tv_usec = stop.tv_usec - start.tv_usec;
787             if (stop.tv_usec < 0)
788                 stop.tv_sec--, stop.tv_usec += 1000000;
789             seconds = stop.tv_sec + (stop.tv_usec / 1000000.0);
790             if (!seconds)
791                 seconds = 1;
792             total += i;
793             msgInfo("%10d bytes read from %s dist @ %.1f KB/sec.",
794                     total, dist, (total / seconds) / 1024.0);
795         }
796     }
797     sigaction(SIGINT, &old, NULL);      /* restore sigint */
798     close(qfd[1]);
799
800     i = waitpid(zpid, &j, 0);
801     /* Don't check exit status - gunzip seems to return a bogus one! */
802     if (i < 0) {
803         if (isDebug())
804             msgDebug("wait for %s returned status of %d!\n", UNZIPPER, i);
805         return FALSE;
806     }
807     i = waitpid(cpid, &j, 0);
808     if (i < 0 || WEXITSTATUS(j)) {
809         if (isDebug())
810             msgDebug("cpio returned error status of %d!\n", WEXITSTATUS(j));
811         return FALSE;
812     }
813     return TRUE;
814 }
815
816 int
817 mediaGetType(dialogMenuItem *self)
818 {
819     return ((dmenuOpenSimple(&MenuMedia, FALSE) && mediaDevice) ? DITEM_SUCCESS : DITEM_FAILURE);
820 }
821
822 /* Return TRUE if all the media variables are set up correctly */
823 Boolean
824 mediaVerify(void)
825 {
826     if (!mediaDevice)
827         return (DITEM_STATUS(mediaGetType(NULL)) == DITEM_SUCCESS);
828     return TRUE;
829 }
830
831 /* Set the FTP username and password fields */
832 int
833 mediaSetFTPUserPass(dialogMenuItem *self)
834 {
835     char *pass;
836
837     if (variable_get_value(VAR_FTP_USER, "Please enter the username you wish to login as:", 0)) {
838         DialogInputAttrs |= DITEM_NO_ECHO;
839         pass = variable_get_value(VAR_FTP_PASS, "Please enter the password for this user:", 0);
840         DialogInputAttrs &= ~DITEM_NO_ECHO;
841     }
842     else
843         pass = NULL;
844     return (pass ? DITEM_SUCCESS : DITEM_FAILURE);
845 }
846
847 /* Set CPIO verbosity level */
848 int
849 mediaSetCPIOVerbosity(dialogMenuItem *self)
850 {
851     char *cp = variable_get(VAR_CPIO_VERBOSITY);
852
853     if (!cp) {
854         msgConfirm("CPIO Verbosity is not set to anything!");
855         return DITEM_FAILURE;
856     }
857     else {
858         if (!strcmp(cp, "low"))
859             variable_set2(VAR_CPIO_VERBOSITY, "medium", 0);
860         else if (!strcmp(cp, "medium"))
861             variable_set2(VAR_CPIO_VERBOSITY, "high", 0);
862         else /* must be "high" - wrap around */
863             variable_set2(VAR_CPIO_VERBOSITY, "low", 0);
864     }
865     return DITEM_SUCCESS;
866 }
867
868 /* A generic open which follows a well-known "path" of places to look */
869 FILE *
870 mediaGenericGet(char *base, const char *file)
871 {
872     char        buf[PATH_MAX];
873
874     snprintf(buf, PATH_MAX, "%s/%s", base, file);
875     if (file_readable(buf))
876         return fopen(buf, "r");
877     snprintf(buf, PATH_MAX, "%s/FreeBSD/%s", base, file);
878     if (file_readable(buf))
879         return fopen(buf, "r");
880     snprintf(buf, PATH_MAX, "%s/releases/%s", base, file);
881     if (file_readable(buf))
882         return fopen(buf, "r");
883     snprintf(buf, PATH_MAX, "%s/%s/%s", base, variable_get(VAR_RELNAME), file);
884     if (file_readable(buf))
885         return fopen(buf, "r");
886     snprintf(buf, PATH_MAX, "%s/releases/%s/%s", base, variable_get(VAR_RELNAME), file);
887     return fopen(buf, "r");
888 }
889