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