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