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