Do not use the display function if the -o (opaque) or -x (hexdump)
[dragonfly.git] / release / 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: 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 $
9  *
10  * Copyright (c) 1995
11  *      Jordan Hubbard.  All rights reserved.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
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
19  *    point in the file.
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.
23  *
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
34  * SUCH DAMAGE.
35  *
36  */
37
38 #include "sysinstall.h"
39 #include <signal.h>
40 #include <netdb.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>
46 #include <sys/stat.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 void
126 mediaClose(void)
127 {
128     if (mediaDevice)
129         DEVICE_SHUTDOWN(mediaDevice);
130     mediaDevice = NULL;
131 }
132
133 /*
134  * Return 1 if we successfully found and set the installation type to
135  * be a CD.
136  */
137 int
138 mediaSetCDROM(dialogMenuItem *self)
139 {
140     Device **devs;
141     int cnt;
142
143     mediaClose();
144     devs = deviceFind(NULL, DEVICE_TYPE_CDROM);
145     cnt = deviceCount(devs);
146     if (!cnt) {
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"
151                        "in the Doc menu.");
152         return DITEM_FAILURE | DITEM_CONTINUE;
153     }
154     else if (cnt > 1) {
155         DMenu *menu;
156         int status;
157         
158         menu = deviceCreateMenu(&MenuMediaCDROM, DEVICE_TYPE_CDROM, cdromHook, NULL);
159         if (!menu)
160             msgFatal("Unable to create CDROM menu!  Something is seriously wrong.");
161         status = dmenuOpenSimple(menu, FALSE);
162         free(menu);
163         if (!status)
164             return DITEM_FAILURE;
165     }
166     else
167         mediaDevice = devs[0];
168     return (mediaDevice ? DITEM_SUCCESS | DITEM_LEAVE_MENU : DITEM_FAILURE);
169 }
170
171 static int
172 floppyHook(dialogMenuItem *self)
173 {
174     return genericHook(self, DEVICE_TYPE_FLOPPY);
175 }
176
177 /*
178  * Return 1 if we successfully found and set the installation type to
179  * be a floppy
180  */
181 int
182 mediaSetFloppy(dialogMenuItem *self)
183 {
184     Device **devs;
185     int cnt;
186
187     mediaClose();
188     devs = deviceFind(NULL, DEVICE_TYPE_FLOPPY);
189     cnt = deviceCount(devs);
190     if (!cnt) {
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"
193                    "menu.");
194         return DITEM_FAILURE | DITEM_CONTINUE;
195     }
196     else if (cnt > 1) {
197         DMenu *menu;
198         int status;
199
200         menu = deviceCreateMenu(&MenuMediaFloppy, DEVICE_TYPE_FLOPPY, floppyHook, NULL);
201         if (!menu)
202             msgFatal("Unable to create Floppy menu!  Something is seriously wrong.");
203         status = dmenuOpenSimple(menu, FALSE);
204         free(menu);
205         if (!status)
206             return DITEM_FAILURE;
207     }
208     else
209         mediaDevice = devs[0];
210     if (mediaDevice)
211         mediaDevice->private = NULL;
212     return (mediaDevice ? DITEM_LEAVE_MENU : DITEM_FAILURE);
213 }
214
215 static int
216 DOSHook(dialogMenuItem *self)
217 {
218     return genericHook(self, DEVICE_TYPE_DOS);
219 }
220
221 /*
222  * Return 1 if we successfully found and set the installation type to
223  * be a DOS partition.
224  */
225 int
226 mediaSetDOS(dialogMenuItem *self)
227 {
228     Device **devs;
229     int cnt;
230
231     mediaClose();
232     devs = deviceFind(NULL, DEVICE_TYPE_DOS);
233     cnt = deviceCount(devs);
234     if (!cnt) {
235         msgConfirm("No DOS primary partitions found!  This installation method is unavailable");
236         return DITEM_FAILURE | DITEM_CONTINUE;
237     }
238     else if (cnt > 1) {
239         DMenu *menu;
240         int status;
241
242         menu = deviceCreateMenu(&MenuMediaDOS, DEVICE_TYPE_DOS, DOSHook, NULL);
243         if (!menu)
244             msgFatal("Unable to create DOS menu!  Something is seriously wrong.");
245         status = dmenuOpenSimple(menu, FALSE);
246         free(menu);
247         if (!status)
248             return DITEM_FAILURE;
249     }
250     else
251         mediaDevice = devs[0];
252     return (mediaDevice ? DITEM_LEAVE_MENU : DITEM_FAILURE);
253 }
254
255 static int
256 tapeHook(dialogMenuItem *self)
257 {
258     return genericHook(self, DEVICE_TYPE_TAPE);
259 }
260
261 /*
262  * Return 1 if we successfully found and set the installation type to
263  * be a tape drive.
264  */
265 int
266 mediaSetTape(dialogMenuItem *self)
267 {
268     Device **devs;
269     int cnt;
270
271     mediaClose();
272     devs = deviceFind(NULL, DEVICE_TYPE_TAPE);
273     cnt = deviceCount(devs);
274     if (!cnt) {
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"
277                    "menu.");
278         return DITEM_FAILURE | DITEM_CONTINUE;
279     }
280     else if (cnt > 1) {
281         DMenu *menu;
282         int status;
283
284         menu = deviceCreateMenu(&MenuMediaTape, DEVICE_TYPE_TAPE, tapeHook, NULL);
285         if (!menu)
286             msgFatal("Unable to create tape drive menu!  Something is seriously wrong.");
287         status = dmenuOpenSimple(menu, FALSE);
288         free(menu);
289         if (!status)
290             return DITEM_FAILURE;
291     }
292     else
293         mediaDevice = devs[0];
294     if (mediaDevice) {
295         char *val;
296
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");
302         if (!val)
303             mediaDevice = NULL;
304         else
305             mediaDevice->private = strdup(val);
306     }
307     return (mediaDevice ? DITEM_LEAVE_MENU : DITEM_FAILURE);
308 }
309
310 /*
311  * Return 0 if we successfully found and set the installation type to
312  * be an ftp server
313  */
314 int
315 mediaSetFTP(dialogMenuItem *self)
316 {
317     static Device ftpDevice;
318     char *cp, hbuf[MAXHOSTNAMELEN], *hostname, *dir;
319     struct addrinfo hints, *res;
320     int af;
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     }
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;
355     }
356     SAFE_STRCPY(ftpDevice.name, cp);
357     SAFE_STRCPY(hbuf, cp + 6);
358     hostname = hbuf;
359
360     if (!networkDev || msgYesNo("You've already done the network configuration once,\n"
361                                 "would you like to skip over it now?") != 0) {
362         if (networkDev)
363             DEVICE_SHUTDOWN(networkDev);
364         if (!(networkDev = tcpDeviceSelect())) {
365             variable_unset(VAR_FTP_PATH);
366             return DITEM_FAILURE;
367         }
368     }
369     if (!DEVICE_INIT(networkDev)) {
370         if (isDebug())
371             msgDebug("mediaSetFTP: Net device init failed.\n");
372         variable_unset(VAR_FTP_PATH);
373         return DITEM_FAILURE;
374     }
375     if (*hostname == '[' && (cp = index(hostname + 1, ']')) != NULL &&
376         (*++cp == '\0' || *cp == '/' || *cp == ':')) {
377         ++hostname;
378         *(cp - 1) = '\0';
379     }
380     else
381         cp = index(hostname, ':');
382     if (cp != NULL && *cp == ':') {
383         *(cp++) = '\0';
384         FtpPort = strtol(cp, 0, 0);
385     }
386     else
387         FtpPort = 21;
388     if ((dir = index(cp ? cp : hostname, '/')) != NULL)
389         *(dir++) = '\0';
390     if (isDebug()) {
391         msgDebug("hostname = `%s'\n", hostname);
392         msgDebug("dir = `%s'\n", dir ? dir : "/");
393         msgDebug("port # = `%d'\n", FtpPort);
394     }
395     if (!ftp_skip_resolve && variable_get(VAR_NAMESERVER)) {
396         msgNotify("Looking up host %s.", hostname);
397         if (isDebug())
398             msgDebug("Starting DNS.\n");
399         kickstart_dns();
400         if (isDebug())
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) {
408             if (isDebug())
409                 msgDebug("Looking up hostname, %s, using getaddrinfo().\n",
410                          hostname);
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);
416                 if (networkDev)
417                     DEVICE_SHUTDOWN(networkDev);
418                 networkDev = NULL;
419                 variable_unset(VAR_FTP_PATH);
420                 return DITEM_FAILURE;
421             }
422         }
423         freeaddrinfo(res);
424         if (isDebug())
425             msgDebug("Found DNS entry for %s successfully..\n", hostname);
426     }
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;
437 }
438
439 int
440 mediaSetFTPActive(dialogMenuItem *self)
441 {
442     variable_set2(VAR_FTP_STATE, "active", 0);
443     return mediaSetFTP(self);
444 }
445
446 int
447 mediaSetFTPPassive(dialogMenuItem *self)
448 {
449     variable_set2(VAR_FTP_STATE, "passive", 0);
450     return mediaSetFTP(self);
451 }
452
453 int mediaSetHTTP(dialogMenuItem *self)
454 {
455     Boolean tmp;
456     int result;
457     char *cp, *idx, hbuf[MAXHOSTNAMELEN], *hostname;
458     int HttpPort;
459     int what = DITEM_RESTORE;
460
461
462     tmp = ftp_skip_resolve;
463     ftp_skip_resolve = TRUE;
464     result = mediaSetFTP(self);
465     ftp_skip_resolve = tmp;
466
467     if (DITEM_STATUS(result) != DITEM_SUCCESS)
468         return result;
469  
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);
473     if (!cp)
474         return DITEM_FAILURE;
475     SAFE_STRCPY(hbuf, cp);
476     hostname = hbuf;
477     if (*hostname == '[' && (idx = index(hostname + 1, ']')) != NULL &&
478         (*++idx == '\0' || *idx == ':')) {
479         ++hostname;
480         *(idx - 1) = '\0';
481     } else
482         idx = index(hostname, ':');
483     if (idx == NULL || *idx != ':')
484         HttpPort = 3128;                /* try this as default */
485     else {
486         *(idx++) = '\0';
487         HttpPort = strtol(idx, 0, 0);
488     }
489
490     variable_set2(VAR_HTTP_HOST, hostname, 0);
491     variable_set2(VAR_HTTP_PORT, itoa(HttpPort), 0);
492     if (isDebug()) {
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));
496     }
497
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;
504 }
505    
506
507 int
508 mediaSetUFS(dialogMenuItem *self)
509 {
510     static Device ufsDevice;
511     struct statfs st;
512     char *cp;
513
514     mediaClose();
515     cp = variable_get_value(VAR_UFS_PATH, "Enter a fully qualified pathname for the directory\n"
516                             "containing the FreeBSD distribution files:", 0);
517     if (!cp)
518         return DITEM_FAILURE;
519
520     /* If they gave us a CDROM or something, try and pick a better name */
521     if (statfs(cp, &st))
522         strcpy(ufsDevice.name, "ufs");
523     else
524         strcpy(ufsDevice.name, st.f_fstypename);
525
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;
533 }
534
535 int
536 mediaSetNFS(dialogMenuItem *self)
537 {
538     static Device nfsDevice;
539     static Device *networkDev = NULL;
540     char *cp, *idx;
541     char hostname[MAXPATHLEN];
542
543     mediaClose();
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);
547     if (!cp)
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;
554     }
555     SAFE_STRCPY(nfsDevice.name, hostname);
556     *idx = '\0';
557     if (!networkDev || msgYesNo("You've already done the network configuration once,\n"
558                                 "would you like to skip over it now?") != 0) {
559         if (networkDev)
560             DEVICE_SHUTDOWN(networkDev);
561         if (!(networkDev = tcpDeviceSelect()))
562             return DITEM_FAILURE;
563     }
564     if (!DEVICE_INIT(networkDev)) {
565         if (isDebug())
566             msgDebug("mediaSetNFS: Net device init failed\n");
567     }
568     if (variable_get(VAR_NAMESERVER)) {
569         kickstart_dns();
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);
573             if (networkDev)
574                 DEVICE_SHUTDOWN(networkDev);
575             networkDev = NULL;
576             variable_unset(VAR_NFS_PATH);
577             return DITEM_FAILURE;
578         }
579         else {
580             if (isDebug())
581                 msgDebug("Found DNS entry for %s successfully..", hostname);
582         }
583     }
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;
592 }
593
594 Boolean
595 mediaExtractDistBegin(char *dir, int *fd, int *zpid, int *cpid)
596 {
597     int i, pfd[2],qfd[2];
598
599     if (!dir)
600         dir = "/";
601     Mkdir(dir);
602     chdir(dir);
603     pipe(pfd);
604     pipe(qfd);
605     *zpid = fork();
606     if (!*zpid) {
607         char *gunzip = RunningAsInit ? "/stand/gunzip" : "/usr/bin/gunzip";
608
609         dup2(qfd[0], 0); close(qfd[0]);
610         dup2(pfd[1], 1); close(pfd[1]);
611         if (DebugFD != -1)
612             dup2(DebugFD, 2);
613         else {
614             close(2);
615             open("/dev/null", O_WRONLY);
616         }
617         close(qfd[1]);
618         close(pfd[0]);
619         i = execl(gunzip, gunzip, 0);
620         if (isDebug())
621             msgDebug("%s command returns %d status\n", gunzip, i);
622         exit(i);
623     }
624     *fd = qfd[1];
625     close(qfd[0]);
626     *cpid = fork();
627     if (!*cpid) {
628         char *cpio = RunningAsInit ? "/stand/cpio" : "/usr/bin/cpio";
629
630         dup2(pfd[0], 0); close(pfd[0]);
631         close(pfd[1]);
632         close(qfd[1]);
633         if (DebugFD != -1) {
634             dup2(DebugFD, 1);
635             dup2(DebugFD, 2);
636         }
637         else {
638             close(1); open("/dev/null", O_WRONLY);
639             dup2(1, 2);
640         }
641         if (strlen(cpioVerbosity()))
642             i = execl(cpio, cpio, "-idum", cpioVerbosity(), "--block-size", mediaTapeBlocksize(), (char *)0);
643         else
644             i = execl(cpio, cpio, "-idum", "--block-size", mediaTapeBlocksize(), (char *)0);
645         if (isDebug())
646             msgDebug("%s command returns %d status\n", cpio, i);
647         exit(i);
648     }
649     close(pfd[0]);
650     close(pfd[1]);
651     return TRUE;
652 }
653
654 Boolean
655 mediaExtractDistEnd(int zpid, int cpid)
656 {
657     int i,j;
658
659     i = waitpid(zpid, &j, 0);
660     /* Don't check exit status - gunzip seems to return a bogus one! */
661     if (i < 0) {
662         if (isDebug())
663             msgDebug("wait for gunzip returned status of %d!\n", i);
664         return FALSE;
665     }
666     i = waitpid(cpid, &j, 0);
667     if (i < 0 || WEXITSTATUS(j)) {
668         if (isDebug())
669             msgDebug("cpio returned error status of %d!\n", WEXITSTATUS(j));
670         return FALSE;
671     }
672     return TRUE;
673 }
674
675 Boolean
676 mediaExtractDist(char *dir, char *dist, FILE *fp)
677 {
678     int i, j, total, seconds, zpid, cpid, pfd[2], qfd[2];
679     char buf[BUFSIZ];
680     struct timeval start, stop;
681     struct sigaction new, old;
682
683     if (!dir)
684         dir = "/";
685
686     Mkdir(dir);
687     chdir(dir);
688     pipe(pfd);  /* read end */
689     pipe(qfd);  /* write end */
690     zpid = fork();
691     if (!zpid) {
692         char *gunzip = RunningAsInit ? "/stand/gunzip" : "/usr/bin/gunzip";
693
694         fclose(fp);
695         close(qfd[1]);
696         dup2(qfd[0], 0); close(qfd[0]);
697
698         close(pfd[0]); 
699         dup2(pfd[1], 1); close(pfd[1]);
700
701         if (DebugFD != -1)
702             dup2(DebugFD, 2);
703         else {
704             close(2);
705             open("/dev/null", O_WRONLY);
706         }
707         i = execl(gunzip, gunzip, 0);
708         if (isDebug())
709             msgDebug("%s command returns %d status\n", gunzip, i);
710         exit(i);
711     }
712     cpid = fork();
713     if (!cpid) {
714         char *cpio = RunningAsInit ? "/stand/cpio" : "/usr/bin/cpio";
715
716         close(pfd[1]);
717         dup2(pfd[0], 0); close(pfd[0]);
718         close (qfd[0]); close(qfd[1]);
719         fclose(fp);
720         if (DebugFD != -1) {
721             dup2(DebugFD, 1);
722             dup2(DebugFD, 2);
723         }
724         else {
725             dup2(open("/dev/null", O_WRONLY), 1);
726             dup2(1, 2);
727         }
728         if (strlen(cpioVerbosity()))
729             i = execl(cpio, cpio, "-idum", cpioVerbosity(), "--block-size", mediaTapeBlocksize(), (char *)0);
730         else
731             i = execl(cpio, cpio, "-idum", "--block-size", mediaTapeBlocksize(), (char *)0);
732         if (isDebug())
733             msgDebug("%s command returns %d status\n", cpio, i);
734         exit(i);
735     }
736     close(pfd[0]); close(pfd[1]);
737     close(qfd[0]);
738
739     total = 0;
740     (void)gettimeofday(&start, (struct timezone *)0);
741
742     /* Make ^C abort the current transfer rather than the whole show */
743     new.sa_handler = handle_intr;
744     new.sa_flags = 0;
745     (void)sigemptyset(&new.sa_mask);
746     sigaction(SIGINT, &new, &old);
747
748     while ((i = fread(buf, 1, BUFSIZ, fp)) > 0) {
749         if (check_for_interrupt()) {
750             msgConfirm("Failure to read from media:  User interrupt.");
751             break;
752         }
753         if (write(qfd[1], buf, i) != i) {
754             msgConfirm("Write error on transfer to cpio process, try of %d bytes.", i);
755             break;
756         }
757         else {
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);
764             if (!seconds)
765                 seconds = 1;
766             total += i;
767             msgInfo("%10d bytes read from %s dist @ %.1f KB/sec.",
768                     total, dist, (total / seconds) / 1024.0);
769         }
770     }
771     sigaction(SIGINT, &old, NULL);      /* restore sigint */
772     close(qfd[1]);
773
774     i = waitpid(zpid, &j, 0);
775     /* Don't check exit status - gunzip seems to return a bogus one! */
776     if (i < 0) {
777         if (isDebug())
778             msgDebug("wait for gunzip returned status of %d!\n", i);
779         return FALSE;
780     }
781     i = waitpid(cpid, &j, 0);
782     if (i < 0 || WEXITSTATUS(j)) {
783         if (isDebug())
784             msgDebug("cpio returned error status of %d!\n", WEXITSTATUS(j));
785         return FALSE;
786     }
787     return TRUE;
788 }
789
790 int
791 mediaGetType(dialogMenuItem *self)
792 {
793     return ((dmenuOpenSimple(&MenuMedia, FALSE) && mediaDevice) ? DITEM_SUCCESS : DITEM_FAILURE);
794 }
795
796 /* Return TRUE if all the media variables are set up correctly */
797 Boolean
798 mediaVerify(void)
799 {
800     if (!mediaDevice)
801         return (DITEM_STATUS(mediaGetType(NULL)) == DITEM_SUCCESS);
802     return TRUE;
803 }
804
805 /* Set the FTP username and password fields */
806 int
807 mediaSetFTPUserPass(dialogMenuItem *self)
808 {
809     char *pass;
810
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;
815     }
816     else
817         pass = NULL;
818     return (pass ? DITEM_SUCCESS : DITEM_FAILURE);
819 }
820
821 /* Set CPIO verbosity level */
822 int
823 mediaSetCPIOVerbosity(dialogMenuItem *self)
824 {
825     char *cp = variable_get(VAR_CPIO_VERBOSITY);
826
827     if (!cp) {
828         msgConfirm("CPIO Verbosity is not set to anything!");
829         return DITEM_FAILURE;
830     }
831     else {
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);
838     }
839     return DITEM_SUCCESS;
840 }
841
842 /* A generic open which follows a well-known "path" of places to look */
843 FILE *
844 mediaGenericGet(char *base, const char *file)
845 {
846     char        buf[PATH_MAX];
847
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");
862 }
863