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