]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - usr.sbin/sysinstall/dist.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / usr.sbin / sysinstall / dist.c
1 /*
2  * The new sysinstall program.
3  *
4  * This is probably the last program in the `sysinstall' line - the next
5  * generation being 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 <sys/param.h>
39 #include <sys/mount.h>
40 #include <sys/time.h>
41 #include <sys/uio.h>
42 #include <ctype.h>
43 #include <signal.h>
44 #include <libutil.h>
45
46 unsigned int Dists;
47 unsigned int SrcDists;
48 unsigned int XOrgDists;
49 unsigned int KernelDists;
50
51 enum _disttype { DT_TARBALL, DT_SUBDIST, DT_PACKAGE };
52
53 typedef struct _dist {
54     char *my_name;
55     unsigned int *my_mask;
56     unsigned int my_bit;
57     enum _disttype my_type;
58     union {
59         char *my_string;        /* DT_TARBALL & DT_PACKAGE */
60         struct _dist *my_dist;  /* DT_SUBDIST */
61     } my_data;
62 } Distribution;
63
64 static Distribution KernelDistTable[];
65 static Distribution SrcDistTable[];
66 static Distribution XOrgDistTable[];
67
68 #define DTE_TARBALL(name, mask, flag, directory)                        \
69         { name, mask, DIST_ ## flag, DT_TARBALL, { directory } }
70 #define DTE_PACKAGE(name, mask, flag, package)                          \
71         { name, mask, DIST_ ## flag, DT_PACKAGE, { package } }
72 #define DTE_SUBDIST(name, mask, flag, subdist)                          \
73         { name, mask, DIST_ ## flag, DT_SUBDIST, { .my_dist = subdist } }
74 #define DTE_END                 { NULL, NULL, 0, 0, { NULL } }
75
76 #define BASE_DIST       (&DistTable[0])
77
78 /* The top-level distribution categories */
79 static Distribution DistTable[] = {
80     DTE_TARBALL("base",     &Dists, BASE,     "/"),
81     DTE_SUBDIST("kernels",  &Dists, KERNEL,   KernelDistTable),
82     DTE_TARBALL("doc",      &Dists, DOC,      "/"),
83     DTE_TARBALL("games",    &Dists, GAMES,    "/"),
84     DTE_TARBALL("manpages", &Dists, MANPAGES, "/"),
85     DTE_TARBALL("catpages", &Dists, CATPAGES, "/"),
86     DTE_TARBALL("proflibs", &Dists, PROFLIBS, "/"),
87     DTE_TARBALL("dict",     &Dists, DICT,     "/"),
88     DTE_TARBALL("info",     &Dists, INFO,     "/"),
89 #ifdef __amd64__
90     DTE_TARBALL("lib32",    &Dists, LIB32,    "/"),
91 #endif
92     DTE_SUBDIST("src",      &Dists, SRC,      SrcDistTable),
93     DTE_TARBALL("ports",    &Dists, PORTS,    "/usr"),
94     DTE_TARBALL("local",    &Dists, LOCAL,    "/"),
95     DTE_PACKAGE("X.Org",    &Dists, XORG,     "xorg"),
96     DTE_END,
97 };
98
99 /* The kernel distributions */
100 static Distribution KernelDistTable[] = {
101     DTE_TARBALL("GENERIC",  &KernelDists, KERNEL_GENERIC, "/boot"),
102 #ifdef WITH_SMP
103     DTE_TARBALL("SMP",      &KernelDists, KERNEL_SMP,     "/boot"),
104 #endif
105     DTE_END,
106 };
107
108 /* The /usr/src distribution */
109 static Distribution SrcDistTable[] = {
110     DTE_TARBALL("sbase",    &SrcDists, SRC_BASE,    "/usr/src"),
111     DTE_TARBALL("scddl",    &SrcDists, SRC_CDDL,    "/usr/src"),
112     DTE_TARBALL("scontrib", &SrcDists, SRC_CONTRIB, "/usr/src"),
113     DTE_TARBALL("scrypto",  &SrcDists, SRC_SCRYPTO, "/usr/src"),
114     DTE_TARBALL("sgnu",     &SrcDists, SRC_GNU,     "/usr/src"),
115     DTE_TARBALL("setc",     &SrcDists, SRC_ETC,     "/usr/src"),
116     DTE_TARBALL("sgames",   &SrcDists, SRC_GAMES,   "/usr/src"),
117     DTE_TARBALL("sinclude", &SrcDists, SRC_INCLUDE, "/usr/src"),
118     DTE_TARBALL("skrb5",    &SrcDists, SRC_SKERBEROS5, "/usr/src"),
119     DTE_TARBALL("slib",     &SrcDists, SRC_LIB,     "/usr/src"),
120     DTE_TARBALL("slibexec", &SrcDists, SRC_LIBEXEC, "/usr/src"),
121     DTE_TARBALL("srelease", &SrcDists, SRC_RELEASE, "/usr/src"),
122     DTE_TARBALL("sbin",     &SrcDists, SRC_BIN,     "/usr/src"),
123     DTE_TARBALL("ssecure",  &SrcDists, SRC_SSECURE, "/usr/src"),
124     DTE_TARBALL("ssbin",    &SrcDists, SRC_SBIN,    "/usr/src"),
125     DTE_TARBALL("sshare",   &SrcDists, SRC_SHARE,   "/usr/src"),
126     DTE_TARBALL("ssys",     &SrcDists, SRC_SYS,     "/usr/src"),
127     DTE_TARBALL("subin",    &SrcDists, SRC_UBIN,    "/usr/src"),
128     DTE_TARBALL("susbin",   &SrcDists, SRC_USBIN,   "/usr/src"),
129     DTE_TARBALL("stools",   &SrcDists, SRC_TOOLS,   "/usr/src"),
130     DTE_TARBALL("srescue",  &SrcDists, SRC_RESCUE,  "/usr/src"),
131     DTE_END,
132 };
133
134 static int      distMaybeSetPorts(dialogMenuItem *self);
135
136 static void
137 distVerifyFlags(void)
138 {
139     if (SrcDists)
140         Dists |= DIST_SRC;
141     if (XOrgDists)
142         Dists |= DIST_XORG;
143     if (KernelDists)
144         Dists |= DIST_KERNEL;
145     if (isDebug()) {
146         msgDebug("Dist Masks: Dists: %0x, Srcs: %0x Kernels: %0x\n", Dists,
147             SrcDists, KernelDists);
148         msgDebug("XServer: %0x\n", XOrgDists);
149     }
150 }
151
152 int
153 distReset(dialogMenuItem *self)
154 {
155     Dists = 0;
156     SrcDists = 0;
157     XOrgDists = 0;
158     KernelDists = 0;
159     return DITEM_SUCCESS | DITEM_REDRAW;
160 }
161
162 int
163 distConfig(dialogMenuItem *self)
164 {
165     char *cp;
166
167     distReset(NULL);
168
169     if ((cp = variable_get(VAR_DIST_MAIN)) != NULL)
170         Dists = atoi(cp);
171
172     if ((cp = variable_get(VAR_DIST_SRC)) != NULL)
173         SrcDists = atoi(cp);
174
175     if ((cp = variable_get(VAR_DIST_X11)) != NULL)
176         XOrgDists = atoi(cp);
177
178     if ((cp = variable_get(VAR_DIST_KERNEL)) != NULL)
179         KernelDists = atoi(cp);
180
181     distVerifyFlags();
182     return DITEM_SUCCESS | DITEM_REDRAW;
183 }
184
185 static int
186 distSetX(void)
187 {
188     Dists |= DIST_XORG;
189     XOrgDists = DIST_XORG_ALL;
190     return DITEM_SUCCESS;
191 }
192
193 int
194 selectKernel(void)
195 {
196 #ifdef WITH_SMP
197     /* select default kernel based on deduced cpu count */
198     return NCpus > 1 ? DIST_KERNEL_SMP : DIST_KERNEL_GENERIC;
199 #else
200     return DIST_KERNEL_GENERIC;
201 #endif
202 }
203
204 int
205 distSetDeveloper(dialogMenuItem *self)
206 {
207     int i;
208
209     distReset(NULL);
210     Dists = _DIST_DEVELOPER;
211     SrcDists = DIST_SRC_ALL;
212     KernelDists = selectKernel();
213     i = distMaybeSetPorts(self);
214     distVerifyFlags();
215     return i;
216 }
217
218 int
219 distSetXDeveloper(dialogMenuItem *self)
220 {
221     int i;
222
223     i = distSetDeveloper(self);
224     i |= distSetX();
225     distVerifyFlags();
226     return i;
227 }
228
229 int
230 distSetKernDeveloper(dialogMenuItem *self)
231 {
232     int i;
233
234     distReset(NULL);
235     Dists = _DIST_DEVELOPER;
236     SrcDists = DIST_SRC_SYS | DIST_SRC_BASE;
237     KernelDists = selectKernel();
238     i = distMaybeSetPorts(self);
239     distVerifyFlags();
240     return i;
241 }
242
243 int
244 distSetXKernDeveloper(dialogMenuItem *self)
245 {
246     int i;
247
248     i = distSetKernDeveloper(self);
249     i |= distSetX();
250     distVerifyFlags();
251     return i;
252 }
253
254 int
255 distSetUser(dialogMenuItem *self)
256 {
257     int i;
258
259     distReset(NULL);
260     Dists = _DIST_USER;
261     KernelDists = selectKernel();
262     i = distMaybeSetPorts(self);
263     distVerifyFlags();
264     return i;
265 }
266
267 int
268 distSetXUser(dialogMenuItem *self)
269 {
270     int i;
271
272     i = distSetUser(self);
273     i |= distSetX();
274     distVerifyFlags();
275     return i;
276 }
277
278 int
279 distSetMinimum(dialogMenuItem *self)
280 {
281     distReset(NULL);
282     Dists = DIST_BASE | DIST_KERNEL;
283     KernelDists = selectKernel();
284     distVerifyFlags();
285     return DITEM_SUCCESS | DITEM_REDRAW;
286 }
287
288 int
289 distSetEverything(dialogMenuItem *self)
290 {
291     int i;
292
293     Dists = DIST_ALL;
294     SrcDists = DIST_SRC_ALL;
295     XOrgDists = DIST_XORG_ALL;
296     KernelDists = DIST_KERNEL_ALL;
297     i = distMaybeSetPorts(self);
298     distVerifyFlags();
299     return i | DITEM_REDRAW;
300 }
301
302 static int
303 distMaybeSetPorts(dialogMenuItem *self)
304 {
305     dialog_clear_norefresh();
306     if (!msgYesNo("Would you like to install the FreeBSD ports collection?\n\n"
307                   "This will give you ready access to over 19,000 ported software packages,\n"
308                   "at a cost of around 445MB of disk space when \"clean\" and possibly\n"
309                   "much more than that when a lot of the distribution tarballs are loaded\n"
310                   "(unless you have the extra discs available from a FreeBSD CD/DVD distribution\n"
311                   "and can mount them on /cdrom, in which case this is far less of a problem).\n\n"
312                   "The ports collection is a very valuable resource and well worth having\n"
313                   "on your /usr partition, so it is advisable to say Yes to this option.\n\n"
314                   "For more information on the ports collection & the latest ports, visit:\n"
315                   "    http://www.freebsd.org/ports\n"))
316         Dists |= DIST_PORTS;
317     else
318         Dists &= ~DIST_PORTS;
319     return DITEM_SUCCESS | DITEM_RESTORE;
320 }
321
322 static Boolean
323 distSetByName(Distribution *dist, char *name)
324 {
325     int i, status = FALSE;
326     
327     /* Loop through current set */
328     for (i = 0; dist[i].my_name; i++) {
329         switch (dist[i].my_type) {
330         case DT_TARBALL:
331         case DT_PACKAGE:
332             if (!strcmp(dist[i].my_name, name)) {
333                 *(dist[i].my_mask) |= dist[i].my_bit;
334                 status = TRUE;
335             }
336             break;
337         case DT_SUBDIST:
338             if (distSetByName(dist[i].my_data.my_dist, name)) {
339                 status = TRUE;
340             }
341             break;
342         }
343     }
344     distVerifyFlags();
345     return status;
346 }
347
348 static Boolean
349 distUnsetByName(Distribution *dist, char *name)
350 {
351     int i, status = FALSE;
352     
353     /* Loop through current set */
354     for (i = 0; dist[i].my_name; i++) {
355         switch (dist[i].my_type) {
356         case DT_TARBALL:
357         case DT_PACKAGE:
358             if (!strcmp(dist[i].my_name, name)) {
359                 *(dist[i].my_mask) &= ~(dist[i].my_bit);
360                 status = TRUE;
361             }
362             break;
363         case DT_SUBDIST:
364             if (distUnsetByName(dist[i].my_data.my_dist, name)) {
365                 status = TRUE;
366             }
367             break;
368         }
369     }
370     return status;
371 }
372
373 /* Just for the dispatch stuff */
374 int
375 distSetCustom(dialogMenuItem *self)
376 {
377     char *cp, *cp2, *tmp;
378
379     if (!(tmp = variable_get(VAR_DISTS))) {
380         msgDebug("distSetCustom() called without %s variable set.\n", VAR_DISTS);
381         return DITEM_FAILURE;
382     }
383
384     cp = alloca(strlen(tmp) + 1);
385     if (!cp)
386         msgFatal("Couldn't alloca() %d bytes!\n", (int)(strlen(tmp) + 1));
387     strcpy(cp, tmp);
388     while (cp) {
389         if ((cp2 = index(cp, ' ')) != NULL)
390             *(cp2++) = '\0';
391         if (!distSetByName(DistTable, cp))
392             msgDebug("distSetCustom: Warning, no such release \"%s\"\n", cp);
393         cp = cp2;
394     }
395     distVerifyFlags();
396     return DITEM_SUCCESS;
397 }
398     
399 /* Just for the dispatch stuff */
400 int
401 distUnsetCustom(dialogMenuItem *self)
402 {
403     char *cp, *cp2, *tmp;
404
405     if (!(tmp = variable_get(VAR_DISTS))) {
406         msgDebug("distUnsetCustom() called without %s variable set.\n", VAR_DISTS);
407         return DITEM_FAILURE;
408     }
409
410     cp = alloca(strlen(tmp) + 1);
411     if (!cp)
412         msgFatal("Couldn't alloca() %d bytes!\n", (int)(strlen(tmp) + 1));
413     strcpy(cp, tmp);
414     while (cp) {
415         if ((cp2 = index(cp, ' ')) != NULL)
416             *(cp2++) = '\0';
417         if (!distUnsetByName(DistTable, cp))
418             msgDebug("distUnsetCustom: Warning, no such release \"%s\"\n", cp);
419         cp = cp2;
420     }
421     return DITEM_SUCCESS;
422 }
423
424 int
425 distSetSrc(dialogMenuItem *self)
426 {
427     int i;
428
429     dialog_clear_norefresh();
430     if (!dmenuOpenSimple(&MenuSrcDistributions, FALSE))
431         i = DITEM_FAILURE;
432     else
433         i = DITEM_SUCCESS;
434     distVerifyFlags();
435     return i | DITEM_RESTORE;
436 }
437
438 int
439 distSetKernel(dialogMenuItem *self)
440 {
441     int i;
442
443     dialog_clear_norefresh();
444     if (!dmenuOpenSimple(&MenuKernelDistributions, FALSE))
445         i = DITEM_FAILURE;
446     else
447         i = DITEM_SUCCESS;
448     distVerifyFlags();
449     return i | DITEM_RESTORE;
450 }
451
452 static Boolean got_intr = FALSE;
453
454 /* timeout handler */
455 static void
456 handle_intr(int sig)
457 {
458     msgDebug("User generated interrupt.\n");
459     got_intr = TRUE;
460 }
461
462 static int
463 check_for_interrupt(void)
464 {
465     if (got_intr) {
466         got_intr = FALSE;
467         return TRUE;
468     }
469     return FALSE;
470 }
471
472 /*
473  * translate distribution filename to lower case
474  * as doTARBALL does in release/Makefile
475  */
476 static void
477 translateDist(char trdist[PATH_MAX], const char *dist)
478 {
479     int j;
480
481     /*
482      * translate distribution filename to lower case
483      * as doTARBALL does in release/Makefile
484      */
485     for (j = 0; j < PATH_MAX-1 && dist[j] != '\0'; j++)
486         trdist[j] = tolower(dist[j]);
487     trdist[j] = '\0';
488 }
489
490 /*
491  * Try to get distribution as multiple pieces, locating and parsing an
492  * info file which tells us how many we need for this distribution.
493  */
494 static Boolean
495 distExtractTarball(char *path, char *dist, char *my_dir, int is_base)
496 {
497     char *buf = NULL, trdist[PATH_MAX], fname[PATH_MAX];
498     struct timeval start, stop;
499     int j, status, total, intr;
500     int cpid, zpid, fd2, chunk, numchunks;
501     properties dist_attr = NULL;
502     const char *tmp;
503     FILE *fp;
504
505     translateDist(trdist, dist);
506     if (isDebug())
507         msgDebug("%s: path \"%s\" dist \"%s\" trdist \"%s\" "
508                 "my_dir \"%s\" %sis_base\n",
509                 __func__, path, dist, trdist, my_dir, is_base ? "" : "!");
510
511     status = TRUE;
512     numchunks = 0;
513     snprintf(fname, sizeof (fname), "%s/%s.inf", path, trdist);
514
515 getinfo:
516     fp = DEVICE_GET(mediaDevice, fname, TRUE);
517     intr = check_for_interrupt();
518     if (fp == (FILE *)IO_ERROR || intr || !mediaDevice) {
519         if (isDebug())
520             msgDebug("%s: fname %s fp: %p, intr: %d mediaDevice: %p\n",
521                 __func__, fname, fp, intr, mediaDevice);
522         /* Hard error, can't continue */
523         if (!msgYesNo("Unable to open %s: %s.\nReinitialize media?",
524                 fname, !intr ? "I/O error." : "User interrupt.")) {
525             DEVICE_SHUTDOWN(mediaDevice);
526             if (!DEVICE_INIT(mediaDevice))
527                 return (FALSE);
528             goto getinfo;
529         } else
530             return (FALSE);
531     } else if (fp == NULL) {
532         /* No attributes file, so try as a single file. */
533         snprintf(fname, sizeof(fname), "%s/%s.%s", path, trdist,
534             USE_GZIP ? "tgz" : "tbz");
535         if (isDebug())
536             msgDebug("%s: fp is NULL (1) fname: %s\n", __func__, fname);
537         /*
538          * Passing TRUE as 3rd parm to get routine makes this a "probing"
539          * get, for which errors are not considered too significant.
540          */
541     getsingle:
542         fp = DEVICE_GET(mediaDevice, fname, TRUE);
543         intr = check_for_interrupt();
544         if (fp == (FILE *)IO_ERROR || intr || !mediaDevice) {
545             if (isDebug())
546                 msgDebug("%s: fname %s fp: %p, intr: %d mediaDevice: %p\n",
547                     __func__, fname, fp, intr, mediaDevice);
548             /* Hard error, can't continue */
549             msgConfirm("Unable to open %s: %s", fname,
550                 !intr ? "I/O error" : "User interrupt");
551             DEVICE_SHUTDOWN(mediaDevice);
552             if (!DEVICE_INIT(mediaDevice))
553                 return (FALSE);
554             goto getsingle;
555         } else if (fp != NULL) {
556             char *dir = root_bias(my_dir);
557
558             dialog_clear_norefresh();
559             msgNotify("Extracting %s into %s directory...", dist, dir);
560             status = mediaExtractDist(dir, dist, fp);
561             fclose(fp);
562             return (status);
563         } else {
564             if (isDebug())
565                 msgDebug("%s: fp is NULL (2) fname %s\n", __func__, fname);
566             return (FALSE);
567         }
568     }
569
570     if (isDebug())
571         msgDebug("Parsing attributes file for distribution %s\n", dist);
572
573     dist_attr = properties_read(fileno(fp));
574     intr = check_for_interrupt();
575     if (intr || !dist_attr) {
576         if (isDebug())
577             msgDebug("%s: intr %d dist_attr %p\n", __func__, intr, dist_attr);
578         msgConfirm("Cannot parse information file for the %s distribution: %s\n"
579                    "Please verify that your media is valid and try again.",
580                    dist, !intr ? "I/O error" : "User interrupt");
581     } else {
582         tmp = property_find(dist_attr, "Pieces");
583         if (tmp)
584             numchunks = strtol(tmp, 0, 0);
585     }
586     fclose(fp);
587     if (!numchunks) {
588         if (isDebug())
589             msgDebug("%s: numchunks is zero\n", __func__);
590         return (TRUE);
591     }
592
593     if (isDebug())
594         msgDebug("Attempting to extract distribution from %u chunks.\n",
595             numchunks);
596
597     total = 0;
598     (void)gettimeofday(&start, (struct timezone *)NULL);
599
600     /* We have one or more chunks, initialize unpackers... */
601     mediaExtractDistBegin(root_bias(my_dir), &fd2, &zpid, &cpid);
602
603     /* And go for all the chunks */
604     dialog_clear_norefresh();
605     for (chunk = 0; chunk < numchunks; chunk++) {
606         int n, retval, last_msg, chunksize, realsize;
607         char prompt[80];
608
609         last_msg = 0;
610
611     getchunk:
612         snprintf(fname, sizeof(fname), "cksum.%c%c",  (chunk / 26) + 'a',
613             (chunk % 26) + 'a');
614         tmp = property_find(dist_attr, fname);
615         chunksize = 0;
616         if (tmp) {
617             tmp = index(tmp, ' ');
618             chunksize = strtol(tmp, 0, 0);
619         }
620         snprintf(fname, sizeof(fname), "%s/%s.%c%c", path, trdist, (chunk / 26) + 'a',
621             (chunk % 26) + 'a');
622         if (isDebug())
623             msgDebug("trying for piece %d of %d: %s\n", chunk + 1, numchunks,
624                 fname);
625         fp = DEVICE_GET(mediaDevice, fname, FALSE);
626         intr = check_for_interrupt();
627         /* XXX: this can't work if we get an I/O error */
628         if (fp <= (FILE *)NULL || intr) {
629             if (fp == NULL)
630                 msgConfirm("Failed to find %s on this media.  Reinitializing media.", fname);
631             else
632                 msgConfirm("Failed to retreive piece file %s.\n"
633                            "%s: Reinitializing media.",
634                            fname, !intr ? "I/O error" : "User interrupt");
635             DEVICE_SHUTDOWN(mediaDevice);
636             if (!DEVICE_INIT(mediaDevice))
637                 goto punt;
638             else
639                 goto getchunk;
640         }
641
642         snprintf(prompt, sizeof(prompt), "Extracting %s into %s directory...",
643             dist, root_bias(my_dir));
644         dialog_gauge("Progress", prompt, 8, 15, 6, 50,
645             (chunk + 1) * 100 / numchunks);
646
647         buf = safe_realloc(buf, chunksize);
648         realsize = 0;
649         while (1) {
650             int seconds;
651
652             n = fread(buf + realsize, 1, BUFSIZ, fp);
653             if (check_for_interrupt()) {
654                 msgConfirm("Media read error:  User interrupt.");
655                 fclose(fp);
656                 goto punt;
657             } else if (n <= 0)
658                 break;
659             total += n;
660             realsize += n;
661
662             /* Print statistics about how we're doing */
663             (void) gettimeofday(&stop, (struct timezone *)0);
664             stop.tv_sec = stop.tv_sec - start.tv_sec;
665             stop.tv_usec = stop.tv_usec - start.tv_usec;
666             if (stop.tv_usec < 0)
667                 stop.tv_sec--, stop.tv_usec += 1000000;
668             seconds = stop.tv_sec + (stop.tv_usec / 1000000.0);
669             if (!seconds)
670                 seconds = 1;
671
672             if (seconds != last_msg) {
673                 last_msg = seconds;
674                 msgInfo("%10d bytes read from %s dist, chunk %2d of %2d @ %.1f KBytes/sec.",
675                         total, dist, chunk + 1, numchunks,
676                         (total / seconds) / 1000.0);
677             }
678         }
679         fclose(fp);
680
681         if (!chunksize || (realsize == chunksize)) {
682             /* No substitution necessary */
683             retval = write(fd2, buf, realsize);
684             if (retval != realsize) {
685                 fclose(fp);
686                 dialog_clear_norefresh();
687                 msgConfirm("Write failure on transfer! (wrote %d bytes of %d bytes)", retval, realsize);
688                     goto punt;
689             }
690         } else {
691             for (j = 0; j < realsize; j++) {
692                 /* On finding CRLF, skip the CR; don't exceed end of buffer. */
693                 if ((buf[j] != 0x0d) || (j == total - 1) || (buf[j + 1] != 0x0a)) {
694                     retval = write(fd2, buf + j, 1);
695                     if (retval != 1) {
696                         fclose(fp);
697                         dialog_clear_norefresh();
698                         msgConfirm("Write failure on transfer! (wrote %d bytes of %d bytes)", j, chunksize);
699                         goto punt;
700                     }
701                 }
702             }
703         }
704     }
705     goto done;
706
707 punt:
708     status = FALSE;
709 done:
710     properties_free(dist_attr);
711     close(fd2);
712     if (status != FALSE)
713         status = mediaExtractDistEnd(zpid, cpid);
714     else
715         (void)mediaExtractDistEnd(zpid, cpid);
716
717     safe_free(buf);
718     return (status);
719 }
720
721 static Boolean
722 distExtract(char *parent, Distribution *me)
723 {
724     int i, status;
725     char *path, *dist;
726     WINDOW *w = savescr();
727     struct sigaction old, new;
728
729     status = TRUE;
730     if (isDebug())
731         msgDebug("distExtract: parent: %s, me: %s\n", parent ? parent : "(none)", me->my_name);
732
733     /* Make ^C fake a sudden timeout */
734     new.sa_handler = handle_intr;
735     new.sa_flags = 0;
736     (void)sigemptyset(&new.sa_mask);
737     dialog_clear_norefresh();
738     dialog_msgbox("Please Wait", "Extracting all requested distributions...", -1, -1, 0);
739     sigaction(SIGINT, &new, &old);
740
741     /* Loop through to see if we're in our parent's plans */
742     for (i = 0; me[i].my_name; i++) {
743         dist = me[i].my_name;
744         path = parent ? parent : dist;
745
746         /* If our bit isn't set, go to the next */
747         if (!(me[i].my_bit & *(me[i].my_mask)))
748             continue;
749
750         switch (me[i].my_type) {
751         case DT_SUBDIST:
752             /* Recurse if we actually have a sub-distribution */
753             status = distExtract(dist, me[i].my_data.my_dist);
754             if (!status) {
755                 dialog_clear_norefresh();
756                 msgConfirm("Unable to transfer all components of the %s distribution.\n"
757                     "You may wish to switch media types and try again.\n",
758                     me[i].my_name);
759             }
760             break;
761         case DT_PACKAGE:
762             dialog_clear_norefresh();
763             msgNotify("Installing %s distribution...", dist);
764             status = (package_add(me[i].my_data.my_string) == DITEM_SUCCESS);
765             if (!status)
766                 dialog_clear_norefresh();
767             break;
768         case DT_TARBALL:
769             status = distExtractTarball(path, dist, me[i].my_data.my_string,
770                 &me[i] == BASE_DIST);
771             if (!status) {
772                 dialog_clear_norefresh();
773                 if (me[i].my_bit != DIST_LOCAL) {
774                     status = msgYesNo("Unable to transfer the %s distribution from\n%s.\n\n"
775                                       "Do you want to try to retrieve it again?",
776                                       me[i].my_name, mediaDevice->name);
777                     if (!status)
778                         --i;
779                     status = FALSE;
780                 }
781             }
782             break;
783         }
784         
785         /*
786          * If extract was successful, remove ourselves from further
787          * consideration.
788          */
789         if (status)
790             *(me[i].my_mask) &= ~(me[i].my_bit);
791     }
792     sigaction(SIGINT, &old, NULL);      /* Restore signal handler */
793     restorescr(w);
794     return status;
795 }
796
797 static void
798 printSelected(char *buf, int selected, Distribution *me, int *col)
799 {
800     int i;
801
802     /* Loop through to see if we're in our parent's plans */
803     for (i = 0; me[i].my_name; i++) {
804
805         /* If our bit isn't set, go to the next */
806         if (!(me[i].my_bit & selected))
807             continue;
808
809         *col += strlen(me[i].my_name);
810         if (*col > 50) {
811             *col = 0;
812             strcat(buf, "\n");
813         }
814         sprintf(&buf[strlen(buf)], " %s", me[i].my_name);
815
816         /* Recurse if have a sub-distribution */
817         if (me[i].my_type == DT_SUBDIST)
818             printSelected(buf, *(me[i].my_mask), me[i].my_data.my_dist, col);
819     }
820 }
821
822 int
823 distExtractAll(dialogMenuItem *self)
824 {
825     int old_dists, old_kernel, retries = 0, status = DITEM_SUCCESS;
826     char buf[512];
827     WINDOW *w;
828
829     /* paranoia */
830     if (!Dists) {
831         if (!dmenuOpenSimple(&MenuSubDistributions, FALSE) || !Dists)
832             return DITEM_FAILURE;
833     }
834
835     if (!mediaVerify() || !DEVICE_INIT(mediaDevice))
836         return DITEM_FAILURE;
837
838     old_dists = Dists;
839     old_kernel = KernelDists;
840     distVerifyFlags();
841
842     dialog_clear_norefresh();
843     w = savescr();
844     msgNotify("Attempting to install all selected distributions..");
845
846     /* Try for 3 times around the loop, then give up. */
847     while (Dists && ++retries < 3)
848         distExtract(NULL, DistTable);
849
850     dialog_clear_norefresh();
851     /* Only do base fixup if base dist was successfully extracted */
852     if ((old_dists & DIST_BASE) && !(Dists & DIST_BASE))
853         status |= installFixupBase(self);
854     /* Only do kernel fixup if kernel dist was successfully extracted */
855     if ((old_dists & DIST_KERNEL) && !(Dists & DIST_KERNEL))
856         status |= installFixupKernel(self, old_kernel);
857
858     /* Clear any local dist flags now */
859     Dists &= ~DIST_LOCAL;
860
861     if (Dists) {
862         int col = 0;
863
864         buf[0] = '\0';
865         dialog_clear_norefresh();
866         printSelected(buf, Dists, DistTable, &col);
867         dialog_clear_norefresh();
868         if (col) {
869             msgConfirm("Couldn't extract the following distributions.  This may\n"
870                        "be because they were not available on the installation\n"
871                        "media you've chosen:\n\n\t%s", buf);
872         }
873     }
874     restorescr(w);
875     return status;
876 }