xref: /illumos-gate/usr/src/cmd/svr4pkg/pkgmk/main.c (revision 598f4ceed9327d2d6c2325dd67cae3aa06f7fea6)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29 
30 
31 #include <stdio.h>
32 #include <string.h>
33 #include <signal.h>
34 #include <errno.h>
35 #include <malloc.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <time.h>
39 #include <fcntl.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <sys/param.h>
43 #include <ctype.h>
44 #include <sys/mman.h>
45 #include <sys/sysmacros.h>
46 #include <strings.h>
47 #include <pkgstrct.h>
48 #include <pkgdev.h>
49 #include <pkginfo.h>
50 #include <pkglocs.h>
51 #include <locale.h>
52 #include <libintl.h>
53 #include <sys/statvfs.h>
54 #include <sys/utsname.h>
55 #include <instzones_api.h>
56 #include <pkglib.h>
57 #include <libadm.h>
58 #include <libinst.h>
59 
60 extern char	**environ, *pkgdir;
61 
62 /* mkpkgmap.c */
63 extern int	mkpkgmap(char *outfile, char *protofile, char **cmdparam);
64 /* splpkgmap.c */
65 extern int	splpkgmap(struct cfent **eptlist, unsigned int eptnum,
66     char *order[], ulong_t bsize, ulong_t frsize, fsblkcnt_t *plimit,
67     fsfilcnt_t *pilimit, fsblkcnt_t *pllimit);
68 /* scriptvfy.c */
69 extern int	checkscripts(char *inst_dir, int silent);
70 
71 /* libpkg/gpkgmap.c */
72 extern void	setmapmode(int mode_no);
73 
74 static boolean_t valid_zone_attr(struct cfent **eptlist);
75 
76 #define	MALSIZ	16
77 #define	NROOT	8
78 #define	SPOOLDEV	"spool"
79 
80 #define	MSG_PROTOTYPE	"## Building pkgmap from package prototype file.\n"
81 #define	MSG_PKGINFO	"## Processing pkginfo file.\n"
82 #define	MSG_VOLUMIZE	"## Attempting to volumize %d entries in pkgmap.\n"
83 #define	MSG_PACKAGE1	"## Packaging one part.\n"
84 #define	MSG_PACKAGEM	"## Packaging %d parts.\n"
85 #define	MSG_VALSCRIPTS	"## Validating control scripts.\n"
86 
87 /* Other problems */
88 #define	ERR_MEMORY	"memory allocation failure, errno=%d"
89 #define	ERR_NROOT	"too many paths listed with -r option, limit is %d"
90 #define	ERR_PKGINST	"invalid package instance identifier <%s>"
91 #define	ERR_PKGABRV	"invalid package abbreviation <%s>"
92 #define	ERR_BADDEV	"unknown or invalid device specified <%s>"
93 #define	ERR_TEMP	"unable to obtain temporary file resources, errno=%d"
94 #define	ERR_DSTREAM	"invalid device specified (datastream) <%s>"
95 #define	ERR_SPLIT	"unable to volumize package"
96 #define	ERR_MKDIR	"unable to make directory <%s>"
97 #define	ERR_SYMLINK	"unable to create symbolic link for <%s>"
98 #define	ERR_OVERWRITE	"must use -o option to overwrite <%s>"
99 #define	ERR_UMOUNT	"unable to unmount device <%s>"
100 #define	ERR_NOPKGINFO	"required pkginfo file is not specified in prototype " \
101 			"file"
102 #define	ERR_RDPKGINFO	"unable to process pkginfo file <%s>"
103 #define	ERR_PROTOTYPE	"unable to locate prototype file"
104 #define	ERR_STATVFS	"unable to stat filesystem <%s>"
105 #define	ERR_WHATVFS	"unable to determine or access output filesystem for " \
106 			"device <%s>"
107 #define	ERR_DEVICE	"unable to find info for device <%s>"
108 #define	ERR_BUILD	"unable to build pkgmap from prototype file"
109 #define	ERR_ONEVOL	"other packages found - package must fit on a single " \
110 			"volume"
111 #define	ERR_NOPARAM	"parameter <%s> is not defined in <%s>"
112 #define	ERR_PKGMTCH	"PKG parameter <%s> does not match instance <%s>"
113 #define	ERR_NO_PKG_INFOFILE	"unable to open pkginfo file <%s>: %s"
114 #define	ERR_ALLZONES_AND_THISZONE	"The package <%s> has <%s> = true " \
115 					"and <%s> = true: the package may " \
116 					"set either parameter to true, but " \
117 					"may not set both parameters to " \
118 					"true. NOTE: if the package " \
119 					"contains a request script, it is " \
120 					"treated as though it has " \
121 					"<SUNW_PKG_THISZONE> = true"
122 #define	ERR_NO_ALLZONES_AND_HOLLOW	"The package <%s> has <%s> = false " \
123 					"and <%s> = true: a hollow package " \
124 					"must also be set to install in all " \
125 					"zones"
126 #define	ERR_PKGINFO_INVALID_OPTION_COMB	"Invalid combinations of zone " \
127 					"parameters in pkginfo file"
128 
129 #define	ERR_USAGE	"usage: %s [options] [VAR=value [VAR=value]] " \
130 			"[pkginst]\n" \
131 			"   where options may include:\n" \
132 			"\t-o\n" \
133 			"\t-a arch\n" \
134 			"\t-v version\n" \
135 			"\t-p pstamp\n" \
136 			"\t-l limit\n" \
137 			"\t-r rootpath\n" \
138 			"\t-b basedir\n" \
139 			"\t-d device\n" \
140 			"\t-f protofile\n"
141 #define	WRN_MISSINGDIR	"WARNING: missing directory entry for <%s>"
142 #define	WRN_SETPARAM	"WARNING: parameter <%s> set to \"%s\""
143 #define	WRN_CLASSES	"WARNING: unreferenced class <%s> in prototype file"
144 
145 #define	LINK    1
146 
147 struct pkgdev pkgdev; 	/* holds info about the installation device */
148 int	started;
149 char	pkgloc[PATH_MAX];
150 char	*basedir;
151 char	*root;
152 char	*rootlist[NROOT];
153 char	*t_pkgmap;
154 char	*t_pkginfo;
155 
156 static struct cfent *svept;
157 static char	*protofile,
158 		*device;
159 static fsblkcnt_t limit = 0;
160 static fsblkcnt_t llimit = 0;
161 static fsfilcnt_t ilimit = 0;
162 static int	overwrite,
163 		nflag,
164 		sflag;
165 static void	ckmissing(char *path, char type);
166 static void	outvol(struct cfent **eptlist, unsigned int eptnum, int part,
167 			int nparts);
168 static void	trap(int n);
169 static void	usage(void);
170 
171 static int	slinkf(char *from, char *to);
172 
173 int
174 main(int argc, char *argv[])
175 {
176 	struct utsname utsbuf;
177 	struct statvfs64 svfsb;
178 	struct cfent	**eptlist;
179 	FILE	*fp;
180 	VFP_T	*vfp;
181 	int	c, n, found;
182 	int	part, nparts, npkgs, objects;
183 	char	buf[MAX_PKG_PARAM_LENGTH];
184 	char	temp[MAX_PKG_PARAM_LENGTH];
185 	char	param[MAX_PKG_PARAM_LENGTH];
186 	char	*pt, *value, *pkginst, *tmpdir, *abi_sym_ptr,
187 		**cmdparam;
188 	char	*pkgname;
189 	char	*pkgvers;
190 	char	*pkgarch;
191 	char	*pkgcat;
192 	void	(*func)();
193 	time_t	clock;
194 	ulong_t	bsize = 0;
195 	ulong_t	frsize = 0;
196 	struct cl_attr	**allclass = NULL;
197 	struct cl_attr	**order;
198 	unsigned int eptnum, i;
199 
200 	/* initialize locale environment */
201 
202 	(void) setlocale(LC_ALL, "");
203 
204 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
205 #define	TEXT_DOMAIN "SYS_TEST"
206 #endif
207 	(void) textdomain(TEXT_DOMAIN);
208 
209 	/* initialize program name */
210 
211 	(void) set_prog_name(argv[0]);
212 
213 	/* tell spmi zones interface how to access package output functions */
214 
215 	z_set_output_functions(echo, echoDebug, progerr);
216 
217 	func = sigset(SIGINT, trap);
218 	if (func != SIG_DFL)
219 		func = sigset(SIGINT, func);
220 	func = sigset(SIGHUP, trap);
221 	setmapmode(MAPBUILD);	/* variable binding */
222 	if (func != SIG_DFL)
223 		func = sigset(SIGHUP, func);
224 
225 	environ = NULL;
226 	while ((c = getopt(argc, argv, "osnp:l:r:b:d:f:a:v:?")) != EOF) {
227 		switch (c) {
228 		    case 'n':
229 			nflag++;
230 			break;
231 
232 		    case 's':
233 			sflag++;
234 			break;
235 
236 		    case 'o':
237 			overwrite++;
238 			break;
239 
240 		    case 'p':
241 			putparam("PSTAMP", optarg);
242 			break;
243 
244 		    case 'l':
245 			llimit = strtoull(optarg, NULL, 10);
246 			break;
247 
248 		    case 'r':
249 			pt = strtok(optarg, " \t\n, ");
250 			n = 0;
251 			do {
252 				rootlist[n++] = flex_device(pt, 0);
253 				if (n >= NROOT) {
254 					progerr(gettext(ERR_NROOT), NROOT);
255 					quit(1);
256 				}
257 			} while (pt = strtok(NULL, " \t\n, "));
258 			rootlist[n] = NULL;
259 			break;
260 
261 		    case 'b':
262 			basedir = optarg;
263 			break;
264 
265 		    case 'f':
266 			protofile = optarg;
267 			break;
268 
269 		    case 'd':
270 			device = flex_device(optarg, 1);
271 			break;
272 
273 		    case 'a':
274 			putparam("ARCH", optarg);
275 			break;
276 
277 		    case 'v':
278 			putparam("VERSION", optarg);
279 			break;
280 
281 		    default:
282 			usage();
283 			/*NOTREACHED*/
284 			/*
285 			 * Although usage() calls a noreturn function,
286 			 * needed to add return (1);  so that main() would
287 			 * pass compilation checks. The statement below
288 			 * should never be executed.
289 			 */
290 			return (1);
291 		}
292 	}
293 
294 	/*
295 	 * Store command line variable assignments for later
296 	 * incorporation into the environment.
297 	 */
298 	cmdparam = &argv[optind];
299 
300 	/* Skip past equates. */
301 	while (argv[optind] && strchr(argv[optind], '='))
302 		optind++;
303 
304 	/* Confirm that the instance name is valid */
305 	if ((pkginst = argv[optind]) != NULL) {
306 		if (pkgnmchk(pkginst, "all", 0)) {
307 			progerr(gettext(ERR_PKGINST), pkginst);
308 			quit(1);
309 		}
310 		argv[optind++] = NULL;
311 	}
312 	if (optind != argc)
313 		usage();
314 
315 	tmpdir = getenv("TMPDIR");
316 	if (tmpdir == NULL)
317 		tmpdir = P_tmpdir;
318 
319 	/* bug id 4244631, not ABI compliant */
320 	abi_sym_ptr = getenv("PKG_NONABI_SYMLINKS");
321 	if (abi_sym_ptr && (strncasecmp(abi_sym_ptr, "TRUE", 4) == 0)) {
322 		set_nonABI_symlinks();
323 	}
324 
325 	if (device == NULL) {
326 		device = devattr(SPOOLDEV, "pathname");
327 		if (device == NULL) {
328 			progerr(gettext(ERR_DEVICE), SPOOLDEV);
329 			exit(99);
330 		}
331 	}
332 
333 	if (protofile == NULL) {
334 		if (access("prototype", 0) == 0)
335 			protofile = "prototype";
336 		else if (access("Prototype", 0) == 0)
337 			protofile = "Prototype";
338 		else {
339 			progerr(gettext(ERR_PROTOTYPE));
340 			quit(1);
341 		}
342 	}
343 
344 	if (devtype(device, &pkgdev)) {
345 		progerr(gettext(ERR_BADDEV), device);
346 		quit(1);
347 	}
348 	if (pkgdev.norewind) {
349 		/* initialize datastream */
350 		progerr(gettext(ERR_DSTREAM), device);
351 		quit(1);
352 	}
353 	if (pkgdev.mount) {
354 		if (n = pkgmount(&pkgdev, NULL, 0, 0, 1))
355 			quit(n);
356 	}
357 
358 	/*
359 	 * convert prototype file to a pkgmap, while locating
360 	 * package objects in the current environment
361 	 */
362 	t_pkgmap = tempnam(tmpdir, "tmpmap");
363 	if (t_pkgmap == NULL) {
364 		progerr(gettext(ERR_TEMP), errno);
365 		exit(99);
366 	}
367 
368 	(void) fprintf(stderr, gettext(MSG_PROTOTYPE));
369 	if (n = mkpkgmap(t_pkgmap, protofile, cmdparam)) {
370 		progerr(gettext(ERR_BUILD));
371 		quit(1);
372 	}
373 
374 	setmapmode(MAPNONE);	/* All appropriate variables are now bound */
375 
376 	if (vfpOpen(&vfp, t_pkgmap, "r", VFP_NEEDNOW) != 0) {
377 		progerr(gettext(ERR_TEMP), errno);
378 		quit(99);
379 	}
380 
381 	eptlist = procmap(vfp, 0, NULL);
382 
383 	if (eptlist == NULL) {
384 		quit(1);
385 	}
386 
387 	(void) vfpClose(&vfp);
388 
389 	/* Validate the zone attributes in pkginfo, before creation */
390 	if (!valid_zone_attr(eptlist)) {
391 		progerr(ERR_PKGINFO_INVALID_OPTION_COMB);
392 		quit(1);
393 	}
394 
395 	(void) fprintf(stderr, gettext(MSG_PKGINFO));
396 	pt = NULL;
397 	for (i = 0; eptlist[i]; i++) {
398 		ckmissing(eptlist[i]->path, eptlist[i]->ftype);
399 		if (eptlist[i]->ftype != 'i')
400 			continue;
401 		if (strcmp(eptlist[i]->path, "pkginfo") == 0)
402 			svept = eptlist[i];
403 	}
404 	if (svept == NULL) {
405 		progerr(gettext(ERR_NOPKGINFO));
406 		quit(99);
407 	}
408 	eptnum = i;
409 
410 	/*
411 	 * process all parameters from the pkginfo file
412 	 * and place them in the execution environment
413 	 */
414 
415 	if ((fp = fopen(svept->ainfo.local, "r")) == NULL) {
416 		progerr(gettext(ERR_RDPKGINFO), svept->ainfo.local);
417 		quit(99);
418 	}
419 	param[0] = '\0';
420 	while (value = fpkgparam(fp, param)) {
421 		if (getenv(param) == NULL)
422 			putparam(param, value);
423 		free((void *)value);
424 		param[0] = '\0';
425 	}
426 	(void) fclose(fp);
427 
428 	/* add command line variables */
429 	while (*cmdparam && (value = strchr(*cmdparam, '=')) != NULL) {
430 		*value = NULL;	/* terminate the parameter */
431 		value++;	/* value is now the value (not '=') */
432 		putparam(*cmdparam++, value);  /* store it in environ */
433 	}
434 
435 	/* make sure parameters are valid */
436 	(void) time(&clock);
437 	if (pt = getenv("PKG")) {
438 		if (pkgnmchk(pt, NULL, 0) || strchr(pt, '.')) {
439 			progerr(gettext(ERR_PKGABRV), pt);
440 			quit(1);
441 		}
442 		if (pkginst == NULL)
443 			pkginst = pt;
444 	} else {
445 		progerr(gettext(ERR_NOPARAM), "PKG", svept->path);
446 		quit(1);
447 	}
448 	/*
449 	 * verify consistency between PKG parameter and pkginst
450 	 */
451 	(void) snprintf(param, sizeof (param), "%s.*", pt);
452 	if (pkgnmchk(pkginst, param, 0)) {
453 		progerr(gettext(ERR_PKGMTCH), pt, pkginst);
454 		quit(1);
455 	}
456 
457 	if ((pkgname = getenv("NAME")) == NULL) {
458 		progerr(gettext(ERR_NOPARAM), "NAME", svept->path);
459 		quit(1);
460 	}
461 	if (ckparam("NAME", pkgname))
462 		quit(1);
463 	if ((pkgvers = getenv("VERSION")) == NULL) {
464 		/* XXX - I18n */
465 		/* LINTED do not use cftime(); use strftime instead */
466 		(void) cftime(buf, "\045m/\045d/\045Y", &clock);
467 		(void) snprintf(temp, sizeof (temp),
468 			gettext("Dev Release %s"), buf);
469 		putparam("VERSION", temp);
470 		pkgvers = getenv("VERSION");
471 		logerr(gettext(WRN_SETPARAM), "VERSION", temp);
472 	}
473 	if (ckparam("VERSION", pkgvers))
474 		quit(1);
475 	if ((pkgarch = getenv("ARCH")) == NULL) {
476 		(void) uname(&utsbuf);
477 		putparam("ARCH", utsbuf.machine);
478 		pkgarch = getenv("ARCH");
479 		logerr(gettext(WRN_SETPARAM), "ARCH", utsbuf.machine);
480 	}
481 	if (ckparam("ARCH", pkgarch))
482 		quit(1);
483 	if (getenv("PSTAMP") == NULL) {
484 		/* use octal value of '%' to fight sccs expansion */
485 		/* XXX - I18n */
486 		/* LINTED do not use cftime(); use strftime instead */
487 		(void) cftime(buf, "\045Y\045m\045d\045H\045M\045S", &clock);
488 		(void) uname(&utsbuf);
489 		(void) snprintf(temp, sizeof (temp), "%s%s",
490 			utsbuf.nodename, buf);
491 		putparam("PSTAMP", temp);
492 		logerr(gettext(WRN_SETPARAM), "PSTAMP", temp);
493 	}
494 	if ((pkgcat = getenv("CATEGORY")) == NULL) {
495 		progerr(gettext(ERR_NOPARAM), "CATEGORY", svept->path);
496 		quit(1);
497 	}
498 	if (ckparam("CATEGORY", pkgcat))
499 		quit(1);
500 
501 	/*
502 	 * warn user of classes listed in package which do
503 	 * not appear in CLASSES variable in pkginfo file
504 	 */
505 	objects = 0;
506 	for (i = 0; eptlist[i]; i++) {
507 		if (eptlist[i]->ftype != 'i') {
508 			objects++;
509 			addlist(&allclass, eptlist[i]->pkg_class);
510 		}
511 	}
512 
513 	if ((pt = getenv("CLASSES")) == NULL) {
514 		if (allclass && *allclass) {
515 			cl_setl(allclass);
516 			cl_putl("CLASSES", allclass);
517 			logerr(gettext(WRN_SETPARAM), "CLASSES",
518 			    getenv("CLASSES"));
519 		}
520 	} else {
521 		cl_sets(qstrdup(pt));
522 		if (allclass && *allclass) {
523 			for (i = 0; allclass[i]; i++) {
524 				found = 0;
525 				if (cl_idx(allclass[i]->name) != -1) {
526 					found++;
527 					break;
528 				}
529 				if (!found) {
530 					logerr(gettext(WRN_CLASSES),
531 					    (char *)allclass[i]);
532 				}
533 			}
534 		}
535 	}
536 
537 	(void) fprintf(stderr, gettext(MSG_VOLUMIZE), objects);
538 	order = (struct cl_attr **)0;
539 	if (pt = getenv("ORDER")) {
540 		pt = qstrdup(pt);
541 		(void) setlist(&order, pt);
542 		cl_putl("ORDER", order);
543 	}
544 
545 	/* stat the intended output filesystem to get blocking information */
546 	if (pkgdev.dirname == NULL) {
547 		progerr(gettext(ERR_WHATVFS), device);
548 		quit(99);
549 	}
550 	if (statvfs64(pkgdev.dirname, &svfsb)) {
551 		progerr(gettext(ERR_STATVFS), pkgdev.dirname);
552 		quit(99);
553 	}
554 
555 	if (bsize == 0) {
556 		bsize = svfsb.f_bsize;
557 	}
558 	if (frsize == 0) {
559 		frsize = svfsb.f_frsize;
560 	}
561 
562 	if (limit == 0)
563 		/*
564 		 * bavail is in terms of fragment size blocks - change
565 		 * to 512 byte blocks
566 		 */
567 		limit = (fsblkcnt_t)(((fsblkcnt_t)frsize > 0) ?
568 			howmany(frsize, DEV_BSIZE) :
569 			howmany(bsize, DEV_BSIZE)) * svfsb.f_bavail;
570 
571 	if (ilimit == 0) {
572 		ilimit = (svfsb.f_favail > 0) ?
573 		    svfsb.f_favail : svfsb.f_ffree;
574 	}
575 
576 	nparts = splpkgmap(eptlist, eptnum, (char **)order, bsize, frsize,
577 	    &limit, &ilimit, &llimit);
578 
579 	if (nparts <= 0) {
580 		progerr(gettext(ERR_SPLIT));
581 		quit(1);
582 	}
583 
584 	if (nflag) {
585 		for (i = 0; eptlist[i]; i++)
586 			(void) ppkgmap(eptlist[i], stdout);
587 		exit(0);
588 		/*NOTREACHED*/
589 	}
590 
591 	(void) snprintf(pkgloc, sizeof (pkgloc), "%s/%s",
592 			pkgdev.dirname, pkginst);
593 	if (!isdir(pkgloc) && !overwrite) {
594 		progerr(gettext(ERR_OVERWRITE), pkgloc);
595 		quit(1);
596 	}
597 
598 	/* output all environment install parameters */
599 	t_pkginfo = tempnam(tmpdir, "pkginfo");
600 	if ((fp = fopen(t_pkginfo, "w")) == NULL) {
601 		progerr(gettext(ERR_TEMP), errno);
602 		exit(99);
603 	}
604 	for (i = 0; environ[i]; i++) {
605 		if (isupper(*environ[i])) {
606 			(void) fputs(environ[i], fp);
607 			(void) fputc('\n', fp);
608 		}
609 	}
610 	(void) fclose(fp);
611 
612 	started++;
613 	(void) rrmdir(pkgloc);
614 	if (mkdir(pkgloc, 0755)) {
615 		progerr(gettext(ERR_MKDIR), pkgloc);
616 		quit(1);
617 	}
618 
619 	/* determine how many packages already reside on the medium */
620 	pkgdir = pkgdev.dirname;
621 	npkgs = 0;
622 	while (pt = fpkginst("all", NULL, NULL))
623 		npkgs++;
624 	(void) fpkginst(NULL); /* free resource usage */
625 
626 	if (nparts > 1) {
627 		if (pkgdev.mount && npkgs) {
628 			progerr(gettext(ERR_ONEVOL));
629 			quit(1);
630 		}
631 	}
632 
633 	/*
634 	 *  update pkgmap entry for pkginfo file, since it may
635 	 *  have changed due to command line or failure to
636 	 *  specify all neccessary parameters
637 	 */
638 	for (i = 0; eptlist[i]; i++) {
639 		if (eptlist[i]->ftype != 'i')
640 			continue;
641 		if (strcmp(eptlist[i]->path, "pkginfo") == 0) {
642 			svept = eptlist[i];
643 			svept->ftype = '?';
644 			svept->ainfo.local = t_pkginfo;
645 			(void) cverify(0, &svept->ftype, t_pkginfo,
646 				&svept->cinfo, 1);
647 			svept->ftype = 'i';
648 			break;
649 		}
650 	}
651 
652 	if (nparts > 1)
653 		(void) fprintf(stderr, gettext(MSG_PACKAGEM), nparts);
654 	else
655 		(void) fprintf(stderr, gettext(MSG_PACKAGE1));
656 
657 	for (part = 1; part <= nparts; part++) {
658 		if ((part > 1) && pkgdev.mount) {
659 			if (pkgumount(&pkgdev)) {
660 				progerr(gettext(ERR_UMOUNT), pkgdev.mount);
661 				quit(99);
662 			}
663 			if (n = pkgmount(&pkgdev, NULL, part, nparts, 1))
664 				quit(n);
665 			(void) rrmdir(pkgloc);
666 			if (mkdir(pkgloc, 0555)) {
667 				progerr(gettext(ERR_MKDIR), pkgloc);
668 				quit(99);
669 			}
670 		}
671 		outvol(eptlist, eptnum, part, nparts);
672 
673 		/* Validate (as much as possible) the control scripts. */
674 		if (part == 1) {
675 			char inst_path[PATH_MAX];
676 
677 			(void) fprintf(stderr, gettext(MSG_VALSCRIPTS));
678 			(void) snprintf(inst_path, sizeof (inst_path),
679 					"%s/install", pkgloc);
680 			checkscripts(inst_path, 0);
681 		}
682 	}
683 
684 	quit(0);
685 	/* LINTED: no return */
686 }
687 
688 static void
689 trap(int n)
690 {
691 	(void) signal(SIGINT, SIG_IGN);
692 	(void) signal(SIGHUP, SIG_IGN);
693 
694 	if (n == SIGINT)
695 		quit(3);
696 	else {
697 		(void) fprintf(stderr, gettext("%s terminated (signal %d).\n"),
698 				get_prog_name(), n);
699 		quit(99);
700 	}
701 }
702 
703 static void
704 outvol(struct cfent **eptlist, unsigned int eptnum, int part, int nparts)
705 {
706 	FILE	*fp;
707 	char	*svpt, *path, temp[PATH_MAX];
708 	unsigned int	i;
709 
710 
711 	if (nparts > 1)
712 		(void) fprintf(stderr, gettext(" -- part %2d:\n"), part);
713 	if (part == 1) {
714 		/* re-write pkgmap, but exclude local pathnames */
715 		(void) snprintf(temp, sizeof (temp), "%s/pkgmap", pkgloc);
716 		if ((fp = fopen(temp, "w")) == NULL) {
717 			progerr(gettext(ERR_TEMP), errno);
718 			quit(99);
719 		}
720 		(void) fprintf(fp, ": %d %llu\n", nparts, limit);
721 		for (i = 0; eptlist[i]; i++) {
722 			svpt = eptlist[i]->ainfo.local;
723 			if (!strchr("sl", eptlist[i]->ftype))
724 				eptlist[i]->ainfo.local = NULL;
725 			if (ppkgmap(eptlist[i], fp)) {
726 				progerr(gettext(ERR_TEMP), errno);
727 				quit(99);
728 			}
729 			eptlist[i]->ainfo.local = svpt;
730 		}
731 		(void) fclose(fp);
732 		(void) fprintf(stderr, "%s\n", temp);
733 	}
734 
735 	(void) snprintf(temp, sizeof (temp), "%s/pkginfo", pkgloc);
736 	if (copyf(svept->ainfo.local, temp, svept->cinfo.modtime))
737 		quit(1);
738 	(void) fprintf(stderr, "%s\n", temp);
739 
740 	for (i = 0; i < eptnum; i++) {
741 		if (eptlist[i]->volno != part)
742 			continue;
743 		if (strchr("dxslcbp", eptlist[i]->ftype))
744 			continue;
745 		if (eptlist[i]->ftype == 'i') {
746 			if (eptlist[i] == svept)
747 				continue; /* don't copy pkginfo file */
748 			(void) snprintf(temp, sizeof (temp),
749 				"%s/install/%s", pkgloc,
750 				eptlist[i]->path);
751 			path = temp;
752 		} else
753 			path = srcpath(pkgloc, eptlist[i]->path, part, nparts);
754 		if (sflag) {
755 			if (slinkf(eptlist[i]->ainfo.local, path))
756 				quit(1);
757 		} else if (copyf(eptlist[i]->ainfo.local, path,
758 				eptlist[i]->cinfo.modtime)) {
759 			quit(1);
760 		}
761 
762 		/*
763 		 * If the package file attributes can be sync'd up with
764 		 * the pkgmap, we fix the attributes here.
765 		 */
766 		if (*(eptlist[i]->ainfo.owner) != '$' &&
767 		    *(eptlist[i]->ainfo.group) != '$') {
768 			/* Clear dangerous bits. */
769 			eptlist[i]->ainfo.mode=
770 			    (eptlist[i]->ainfo.mode & S_IAMB);
771 			/*
772 			 * Make sure it can be read by the world and written
773 			 * by the owner.
774 			 */
775 			eptlist[i]->ainfo.mode |= 0644;
776 			if (!strchr("in", eptlist[i]->ftype)) {
777 				/* Set the safe attributes. */
778 				averify(1, &(eptlist[i]->ftype),
779 				    path, &(eptlist[i]->ainfo));
780 			}
781 		}
782 
783 		(void) fprintf(stderr, "%s\n", path);
784 	}
785 }
786 
787 static void
788 ckmissing(char *path, char type)
789 {
790 	static char	**dir;
791 	static int	ndir;
792 	char	*pt;
793 	int	i, found;
794 
795 	if (dir == NULL) {
796 		dir = (char **)calloc(MALSIZ, sizeof (char *));
797 		if (dir == NULL) {
798 			progerr(gettext(ERR_MEMORY), errno);
799 			quit(99);
800 		}
801 	}
802 
803 	if (strchr("dx", type)) {
804 		dir[ndir] = path;
805 		if ((++ndir % MALSIZ) == 0) {
806 			dir = (char **)realloc((void *)dir,
807 				(ndir+MALSIZ)*sizeof (char *));
808 			if (dir == NULL) {
809 				progerr(gettext(ERR_MEMORY), errno);
810 				quit(99);
811 			}
812 		}
813 		dir[ndir] = (char *)NULL;
814 	}
815 
816 	pt = path;
817 	if (*pt == '/')
818 		pt++;
819 	while (pt = strchr(pt, '/')) {
820 		*pt = '\0';
821 		found = 0;
822 		for (i = 0; i < ndir; i++) {
823 			if (strcmp(path, dir[i]) == 0) {
824 				found++;
825 				break;
826 			}
827 		}
828 		if (!found) {
829 			logerr(gettext(WRN_MISSINGDIR), path);
830 			ckmissing(qstrdup(path), 'd');
831 		}
832 		*pt++ = '/';
833 	}
834 }
835 
836 static int
837 slinkf(char *from, char *to)
838 {
839 	char	*pt;
840 
841 	pt = to;
842 	while (pt = strchr(pt+1, '/')) {
843 		*pt = '\0';
844 		if (isdir(to) && mkdir(to, 0755)) {
845 			progerr(gettext(ERR_MKDIR), to);
846 			*pt = '/';
847 			return (-1);
848 		}
849 		*pt = '/';
850 	}
851 	if (symlink(from, to)) {
852 		progerr(gettext(ERR_SYMLINK), to);
853 		return (-1);
854 	}
855 	return (0);
856 }
857 
858 static void
859 usage(void)
860 {
861 	(void) fprintf(stderr, gettext(ERR_USAGE), get_prog_name());
862 	exit(1);
863 	/*NOTREACHED*/
864 }
865 
866 /*
867  * valid_zone_attr:	Validates the zone attributes specified in
868  *			pkginfo file for this package. The package
869  *			can not be created with certain combinations
870  *			of the attributes.
871  */
872 static boolean_t
873 valid_zone_attr(struct cfent **eptlist)
874 {
875 	FILE		*pkginfoFP;
876 	boolean_t	all_zones;	/* pkg is "all zones" only */
877 	boolean_t	is_hollow;	/* pkg is "hollow" */
878 	boolean_t	this_zone;	/* pkg is "this zone" only */
879 	char 		pkginfoPath[PATH_MAX];	/* pkginfo file path */
880 	char		*pkgInst;
881 	int i;
882 
883 	/* Path to pkginfo file within the package to be installed */
884 
885 	this_zone = B_FALSE;
886 	for (i = 0; eptlist[i]; i++) {
887 		if (eptlist[i]->ftype != 'i')
888 			continue;
889 		if (strcmp(eptlist[i]->path, "pkginfo") == 0)
890 			(void) strcpy(pkginfoPath, eptlist[i]->ainfo.local);
891 
892 		/*
893 		 * Check to see if this package has a request script. If this
894 		 * package does have a request script, then mark the package
895 		 * for installation in this zone only. Any package with a
896 		 * request script cannot be installed outside of the zone the
897 		 * pkgadd command is being run in, nor can such a package be
898 		 * installed as part of a new zone install. A new zone install
899 		 * must be non-interactive, which is required by all packages
900 		 * integrated into the Solaris WOS.
901 		 * If request file is set in prototype, then this_zone is TRUE.
902 		 */
903 		if (strcmp(eptlist[i]->path, "request") == 0)
904 			this_zone = B_TRUE;
905 	}
906 
907 	/* Gather information from the pkginfo file */
908 
909 	pkginfoFP = fopen(pkginfoPath, "r");
910 
911 	if (pkginfoFP == NULL) {
912 		progerr(ERR_NO_PKG_INFOFILE, pkginfoPath, strerror(errno));
913 		return (B_FALSE);
914 	}
915 
916 	if ((pkgInst = fpkgparam(pkginfoFP, "PKG")) == NULL) {
917 		progerr(gettext(ERR_NOPARAM), "PKG", pkginfoPath);
918 		return (B_FALSE);
919 	}
920 
921 
922 	/* Determine "HOLLOW" setting for this package */
923 	is_hollow = pkginfoParamTruth(pkginfoFP, PKG_HOLLOW_VARIABLE,
924 			"true", B_FALSE);
925 
926 	/* Determine "ALLZONES" setting for this package */
927 	all_zones = pkginfoParamTruth(pkginfoFP, PKG_ALLZONES_VARIABLE,
928 			"true", B_FALSE);
929 
930 	/* Determine "THISZONE" setting for this package, if no request file */
931 	if (!this_zone)
932 		this_zone = pkginfoParamTruth(pkginfoFP, PKG_THISZONE_VARIABLE,
933 			"true", B_FALSE);
934 
935 	/* Close pkginfo file */
936 	(void) fclose(pkginfoFP);
937 
938 	/*
939 	 * Validate zone attributes based on information gathered,
940 	 * and validate the three SUNW_PKG_ options:
941 	 *
942 	 * -----------------------------|---------------|
943 	 * <ALLZONES><HOLLOW><THISZONE> |  If Allowed   |
944 	 * ----1------------------------|---------------|
945 	 *		F F F		|	OK	|
946 	 *		F F T		|	OK	|
947 	 *		F T *		|	NO	|
948 	 * ----2------------------------|---------------|
949 	 *		T F F		|	OK	|
950 	 *		T T F		|	OK	|
951 	 *		T * T		|	NO	|
952 	 * -----------------------------|---------------|
953 	 */
954 
955 	/* pkg "all zones" && "this zone" (#2) */
956 
957 	if (all_zones && this_zone) {
958 		progerr(ERR_ALLZONES_AND_THISZONE, pkgInst,
959 		    PKG_ALLZONES_VARIABLE, PKG_THISZONE_VARIABLE);
960 		return (B_FALSE);
961 	}
962 
963 	/* pkg "!all zones" && "hollow" (#1) */
964 
965 	if ((!all_zones) && is_hollow) {
966 		progerr(ERR_NO_ALLZONES_AND_HOLLOW, pkgInst,
967 		    PKG_ALLZONES_VARIABLE, PKG_HOLLOW_VARIABLE);
968 		return (B_FALSE);
969 	}
970 
971 	return (B_TRUE);
972 }
973