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