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