xref: /illumos-gate/usr/src/cmd/svr4pkg/libinst/pkgops.c (revision 5422785d352a2bb398daceab3d1898a8aa64d006)
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 (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 
27 #include <stdio.h>
28 #include <limits.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <fcntl.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <signal.h>
36 #include <errno.h>
37 #include <assert.h>
38 #include <pkgdev.h>
39 #include <pkginfo.h>
40 #include <pkglocs.h>
41 #include <locale.h>
42 #include <libintl.h>
43 #include <instzones_api.h>
44 #include <pkglib.h>
45 #include <install.h>
46 #include <libinst.h>
47 #include <libadm.h>
48 #include <messages.h>
49 
50 /* commands to execute */
51 
52 #define	PKGINFO_CMD	"/usr/bin/pkginfo"
53 
54 #define	GLOBALZONE_ONLY_PACKAGE_FILE_PATH	\
55 					"/var/sadm/install/gz-only-packages"
56 
57 #if	!defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
58 #define	TEXT_DOMAIN	"SYS_TEST"
59 #endif
60 
61 /*
62  * forward declarations
63  */
64 
65 static void		_pkginfoInit(struct pkginfo *a_info);
66 static struct pkginfo	*_pkginfoFactory(void);
67 static char		**thisZonePackages;
68 static int		numThisZonePackages;
69 
70 /*
71  * *****************************************************************************
72  * global external (public) functions
73  * *****************************************************************************
74  */
75 
76 /*
77  * Name:	pkginfoFree
78  * Description:	free pkginfo structure returned from various functions
79  * Arguments:	r_info - pointer to pointer to pkginfo structure to free
80  * Returns:	void
81  */
82 
83 void
84 pkginfoFree(struct pkginfo **r_info)
85 {
86 	struct pkginfo	*pinfo;
87 
88 	/* entry assertions */
89 
90 	assert(r_info != (struct pkginfo **)NULL);
91 
92 	/* localize reference to info structure to free */
93 
94 	pinfo = *r_info;
95 
96 	/* reset callers handle to info structure */
97 
98 	*r_info = (struct pkginfo *)NULL;
99 
100 	assert(pinfo != (struct pkginfo *)NULL);
101 
102 	/* free up contents of the structure */
103 
104 	_pkginfoInit(pinfo);
105 
106 	/* free up structure itself */
107 
108 	(void) free(pinfo);
109 }
110 
111 /*
112  * Name:	pkginfoIsPkgInstalled
113  * Description:	determine if specified package is installed, return pkginfo
114  *		structure describing package if package is installed
115  * Arguments:	r_pinfo - pointer to pointer to pkginfo structure
116  *			If this pointer is NOT null:
117  *			-On success, this handle is filled in with a pointer
118  *			--to a newly allocated pkginfo structure describing
119  *			--the package discovered
120  *			-On failure, this handle is filled with NULL
121  *			If this pointer is NULL:
122  *			-no pkginfo structure is returned on success.
123  *		a_pkgInst - package instance (name) to lookup
124  * Returns:	boolean_t
125  *			B_TRUE - package installed, pkginfo returned
126  *			B_FALSE - package not installed, no pkginfo returned
127  * NOTE:	This function returns the first instance of package that
128  *		is installed - see pkginfo() function for details
129  * NOTE:    	Any pkginfo structure returned is placed in new storage for the
130  *		calling function. The caller must use 'pkginfoFree' to dispose
131  *		of the storage once the pkginfo structure is no longer needed.
132  */
133 
134 boolean_t
135 pkginfoIsPkgInstalled(struct pkginfo **r_pinfo, char *a_pkgInst)
136 {
137 	int		r;
138 	struct pkginfo	*pinf;
139 
140 	/* entry assertions */
141 
142 	assert(a_pkgInst != (char *)NULL);
143 	assert(*a_pkgInst != '\0');
144 
145 	/* reset returned pkginfo structure handle */
146 
147 	if (r_pinfo != (struct pkginfo **)NULL) {
148 		*r_pinfo = (struct pkginfo *)NULL;
149 	}
150 
151 	/* allocate a new pinfo structure for use in the call to pkginfo */
152 
153 	pinf = _pkginfoFactory();
154 
155 	/* lookup the specified package */
156 
157 	/* NOTE: required 'pkgdir' set to spool directory or NULL */
158 	r = pkginfo(pinf, a_pkgInst, NULL, NULL);
159 	echoDebug(DBG_PKGOPS_PKGINFO_RETURNED, a_pkgInst, r);
160 
161 	if (r_pinfo != (struct pkginfo **)NULL) {
162 		*r_pinfo = pinf;
163 	} else {
164 		/* free pkginfo structure */
165 		pkginfoFree(&pinf);
166 	}
167 
168 	return (r == 0 ? B_TRUE : B_FALSE);
169 }
170 
171 /*
172  * Name:	pkgOpenInGzOnlyFile
173  * Description:	Open the global zone only package list file
174  * Arguments:	a_rootPath - pointer to string representing the root path
175  *			where the global zone only package list file is
176  *			located - NULL is the same as "/"
177  * Returns:	FILE *
178  *			== NULL - failure - file not open
179  *			!= NULL - success - file pointer returned
180  * NOTE:	This function will create the file if it does not exist.
181  */
182 
183 FILE *
184 pkgOpenInGzOnlyFile(char *a_rootPath)
185 {
186 	FILE	*pkgingzonlyFP;
187 	char	pkgingzonlyPath[PATH_MAX];
188 	int	len;
189 
190 	/* normalize root path */
191 
192 	if (a_rootPath == (char *)NULL) {
193 		a_rootPath = "";
194 	}
195 
196 	/* generate path to glocal zone only list file */
197 
198 	len = snprintf(pkgingzonlyPath, sizeof (pkgingzonlyPath), "%s/%s",
199 		a_rootPath, GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
200 	if (len > sizeof (pkgingzonlyPath)) {
201 		progerr(ERR_CREATE_PATH_2, a_rootPath,
202 				GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
203 		return ((FILE *)NULL);
204 	}
205 
206 	/* open global zone only list file */
207 
208 	pkgingzonlyFP = fopen(pkgingzonlyPath, "r+");
209 	if ((pkgingzonlyFP == (FILE *)NULL) && (errno == ENOENT)) {
210 		pkgingzonlyFP = fopen(pkgingzonlyPath, "w+");
211 	}
212 
213 	if ((pkgingzonlyFP == (FILE *)NULL) && (errno != ENOENT)) {
214 		progerr(ERR_PKGOPS_OPEN_GZONLY, pkgingzonlyPath,
215 				strerror(errno));
216 		return ((FILE *)NULL);
217 	}
218 
219 	/* success - return FILE pointer open on global zone only list file */
220 
221 	return (pkgingzonlyFP);
222 }
223 
224 /*
225  * Name:	pkgIsPkgInGzOnly
226  * Description:	determine if package is recorded as "in global zone only"
227  *		by opening the appropriate files and searching for the
228  *		specified package
229  * Arguments:	a_rootPath - pointer to string representing the root path
230  *			where the global zone only package list file is
231  *			located - NULL is the same as "/"
232  *		a_pkgInst - pointer to string representing the package instance
233  *			(name) of the package to lookup
234  * Returns:	boolean_t
235  *			B_TRUE - package is recorded as "in global zone only"
236  *			B_FALSE - package is NOT recorded as "in gz only"
237  * NOTE:	This function will create the file if it does not exist.
238  */
239 
240 boolean_t
241 pkgIsPkgInGzOnly(char *a_rootPath, char *a_pkgInst)
242 {
243 	FILE		*fp;
244 	boolean_t	in_gz_only;
245 
246 	/* normalize root path */
247 
248 	if (a_rootPath == (char *)NULL) {
249 		a_rootPath = "";
250 	}
251 
252 	/* open the global zone only package list file */
253 
254 	fp = pkgOpenInGzOnlyFile(a_rootPath);
255 	if (fp == (FILE *)NULL) {
256 		echoDebug(ERR_PKGOPS_CANNOT_OPEN_GZONLY,
257 				a_rootPath ? a_rootPath : "/");
258 		return (B_FALSE);
259 	}
260 
261 	/* is the package recorded as "in global zone only" ? */
262 
263 	in_gz_only = pkgIsPkgInGzOnlyFP(fp, a_pkgInst);
264 
265 	/* close the global zone only package list file */
266 
267 	(void) fclose(fp);
268 
269 	/* return results */
270 
271 	return (in_gz_only);
272 }
273 
274 /*
275  * Name:	pkgIsPkgInGzOnly
276  * Description:	determine if package is recorded as "in global zone only"
277  *		by searching the specified open FILE for the specified package
278  * Arguments:	a_fp - pointer to FILE handle open on file to search
279  *		a_pkgInst - pointer to string representing the package instance
280  *			(name) of the package to lookup
281  * Returns:	boolean_t
282  *			B_TRUE - package is recorded as "in global zone only"
283  *			B_FALSE - package is NOT recorded as "in gz only"
284  */
285 
286 boolean_t
287 pkgIsPkgInGzOnlyFP(FILE *a_fp, char *a_pkgInst)
288 {
289 	char	line[PATH_MAX+1];
290 
291 	/* entry assertions */
292 
293 	assert(a_fp != (FILE *)NULL);
294 	assert(a_pkgInst != (char *)NULL);
295 	assert(*a_pkgInst != '\0');
296 
297 	/* rewind the file to the beginning */
298 
299 	rewind(a_fp);
300 
301 	/* read the file line by line searching for the specified package */
302 
303 	while (fgets(line, sizeof (line), a_fp) != (char *)NULL) {
304 		int	len;
305 
306 		/* strip off trailing newlines */
307 		len = strlen(line);
308 		while ((len > 0) && (line[len-1] == '\n')) {
309 			line[--len] = '\0';
310 		}
311 
312 		/* ignore blank and comment lines */
313 		if ((line[0] == '#') || (line[0] == '\0')) {
314 			continue;
315 		}
316 
317 		/* return true if this is the package we are looking for */
318 		if (strcmp(a_pkgInst, line) == 0) {
319 			echoDebug(DBG_PKGOPS_PKG_IS_GZONLY, a_pkgInst);
320 			return (B_TRUE);
321 		}
322 	}
323 
324 	/* end of file - package not found */
325 
326 	echoDebug(DBG_PKGOPS_PKG_NOT_GZONLY, a_pkgInst);
327 
328 	return (B_FALSE);
329 }
330 
331 /*
332  * Name:	pkgRemovePackageFromGzonlyList
333  * Description:	Remove specified package from the global zone only package list
334  *		file located at a specified root path
335  * Arguments:	a_rootPath - pointer to string representing the root path
336  *			where the global zone only package list file is
337  *			located - NULL is the same as "/"
338  *		a_pkgInst - pointer to string representing the package instance
339  *			(name) of the package to remove
340  * Returns:	boolean_t
341  *			B_TRUE - package is successfully removed
342  *			B_FALSE - failed to remove package from file
343  * NOTE:	This function will create the file if it does not exist.
344  */
345 
346 boolean_t
347 pkgRemovePackageFromGzonlyList(char *a_rootPath, char *a_pkgInst)
348 {
349 	FILE		*destFP;
350 	FILE		*srcFP;
351 	boolean_t	pkgremoved = B_FALSE;
352 	char		destPath[PATH_MAX];
353 	char		line[PATH_MAX+1];
354 	char		savePath[PATH_MAX];
355 	char		srcPath[PATH_MAX];
356 	char		timeb[BUFSIZ];
357 	int		len;
358 	struct tm	*timep;
359 	time_t		clock;
360 
361 	/* entry assertions */
362 
363 	assert(a_pkgInst != (char *)NULL);
364 	assert(*a_pkgInst != '\0');
365 
366 	/* normalize root path */
367 
368 	if (a_rootPath == (char *)NULL) {
369 		a_rootPath = "";
370 	}
371 
372 	/*
373 	 * calculate paths to various objects
374 	 */
375 
376 	/* path to current "source" ingzonly file */
377 
378 	len = snprintf(srcPath, sizeof (srcPath), "%s/%s",
379 		a_rootPath, GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
380 	if (len > sizeof (srcPath)) {
381 		progerr(ERR_CREATE_PATH_2, a_rootPath,
382 				GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
383 		return (B_FALSE);
384 	}
385 
386 	/* path to new "destination" ingzonly file */
387 
388 	len = snprintf(destPath, sizeof (destPath), "%s/%s.tmp",
389 		a_rootPath, GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
390 	if (len > sizeof (srcPath)) {
391 		progerr(ERR_CREATE_PATH_2, a_rootPath,
392 				GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
393 		return (B_FALSE);
394 	}
395 
396 	/* path to temporary "saved" ingzonly file */
397 
398 	len = snprintf(savePath, sizeof (savePath), "%s/%s.save",
399 		a_rootPath, GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
400 	if (len > sizeof (srcPath)) {
401 		progerr(ERR_CREATE_PATH_2, a_rootPath,
402 				GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
403 		return (B_FALSE);
404 	}
405 
406 	/* open source file, creating if necessary */
407 
408 	srcFP = fopen(srcPath, "r+");
409 	if ((srcFP == (FILE *)NULL) && (errno == ENOENT)) {
410 		srcFP = fopen(srcPath, "w+");
411 	}
412 
413 	/* error if could not open/create file */
414 
415 	if (srcFP == (FILE *)NULL) {
416 		progerr(ERR_PKGOPS_OPEN_GZONLY, srcPath, strerror(errno));
417 		return (B_FALSE);
418 	}
419 
420 	/* open/create new destination file */
421 
422 	(void) remove(destPath);
423 	destFP = fopen(destPath, "w");
424 	if (destFP == (FILE *)NULL) {
425 		progerr(ERR_PKGOPS_TMPOPEN, destPath, strerror(errno));
426 		if (srcFP != (FILE *)NULL) {
427 			(void) fclose(srcFP);
428 		}
429 		return (B_FALSE);
430 	}
431 
432 	/* add standard comment to beginning of file */
433 
434 	(void) time(&clock);
435 	timep = localtime(&clock);
436 
437 	(void) strftime(timeb, sizeof (timeb), "%c\n", timep);
438 
439 	/* put standard header at the beginning of the file */
440 
441 	(void) fprintf(destFP, MSG_GZONLY_FILE_HEADER,
442 			get_prog_name(), "remove", a_pkgInst, timeb);
443 
444 	/* read source/write destination - removing specified package */
445 
446 	while (fgets(line, sizeof (line), srcFP) != (char *)NULL) {
447 		int	len;
448 
449 		/* strip off trailing newlines */
450 		len = strlen(line);
451 		while ((len > 0) && (line[len-1] == '\n')) {
452 			line[--len] = '\0';
453 		}
454 
455 		/* ignore blank and comment lines */
456 		if ((line[0] == '#') || (line[0] == '\0')) {
457 			continue;
458 		}
459 
460 		/* add pkg if yet to add and pkg <= line */
461 		if ((pkgremoved == B_FALSE) && (strcmp(a_pkgInst, line) == 0)) {
462 			pkgremoved = B_TRUE;
463 		} else {
464 			(void) fprintf(destFP, "%s\n", line);
465 		}
466 	}
467 
468 	/* close both files */
469 
470 	(void) fclose(srcFP);
471 
472 	(void) fclose(destFP);
473 
474 	/*
475 	 * if package not found there is no need to update the original file
476 	 */
477 
478 	if (pkgremoved == B_FALSE) {
479 		(void) unlink(destPath);
480 		return (B_TRUE);
481 	}
482 
483 	/*
484 	 * Now we want to make a copy of the old gzonly file as a
485 	 * fail-safe.
486 	 */
487 
488 	if ((access(savePath, F_OK) == 0) && remove(savePath)) {
489 		progerr(ERR_REMOVE, savePath, strerror(errno));
490 		(void) remove(destPath);
491 		return (B_FALSE);
492 	}
493 
494 	if (link(srcPath, savePath) != 0) {
495 		progerr(ERR_LINK, savePath, srcPath, strerror(errno));
496 		(void) remove(destPath);
497 		return (B_FALSE);
498 	}
499 
500 	if (rename(destPath, srcPath) != 0) {
501 		progerr(ERR_RENAME, destPath, srcPath, strerror(errno));
502 		if (rename(savePath, srcPath)) {
503 			progerr(ERR_RENAME, savePath, srcPath, strerror(errno));
504 		}
505 		(void) remove(destPath);
506 		return (B_FALSE);
507 	}
508 
509 	if (remove(savePath) != 0) {
510 		progerr(ERR_REMOVE, savePath, strerror(errno));
511 	}
512 
513 	/* successfully removed package */
514 
515 	echoDebug(DBG_PKGOPS_REMOVED_GZPKG, a_pkgInst);
516 
517 	return (B_TRUE);
518 }
519 
520 /*
521  * Name:	pkgAddPackageFromGzonlyList
522  * Description:	Add specified package to the global zone only package list
523  *		file located at a specified root path
524  * Arguments:	a_rootPath - pointer to string representing the root path
525  *			where the global zone only package list file is
526  *			located - NULL is the same as "/"
527  *		a_pkgInst - pointer to string representing the package instance
528  *			(name) of the package to add
529  * Returns:	boolean_t
530  *			B_TRUE - package is successfully added
531  *			B_FALSE - failed to add package to the file
532  * NOTE:	This function will create the file if it does not exist.
533  */
534 
535 boolean_t
536 pkgAddPackageToGzonlyList(char *a_pkgInst, char *a_rootPath)
537 {
538 	FILE		*destFP;
539 	FILE		*srcFP;
540 	boolean_t	pkgadded = B_FALSE;
541 	char		destPath[PATH_MAX];
542 	char		line[PATH_MAX+1];
543 	char		savePath[PATH_MAX];
544 	char		srcPath[PATH_MAX];
545 	char		timeb[BUFSIZ];
546 	int		len;
547 	struct tm	*timep;
548 	time_t		clock;
549 
550 	/* entry assertions */
551 
552 	assert(a_pkgInst != (char *)NULL);
553 	assert(*a_pkgInst != '\0');
554 
555 	/* normalize root path */
556 
557 	if (a_rootPath == (char *)NULL) {
558 		a_rootPath = "";
559 	}
560 
561 	/* entry debugging info */
562 
563 	echoDebug(DBG_PKGOPS_ADDGZPKG, a_pkgInst, a_rootPath);
564 
565 	/*
566 	 * calculate paths to various objects
567 	 */
568 
569 	/* path to current "source" ingzonly file */
570 
571 	len = snprintf(srcPath, sizeof (srcPath), "%s/%s",
572 		a_rootPath, GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
573 	if (len > sizeof (srcPath)) {
574 		progerr(ERR_CREATE_PATH_2, a_rootPath,
575 				GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
576 		return (B_FALSE);
577 	}
578 
579 	/* path to new "destination" ingzonly file */
580 
581 	len = snprintf(destPath, sizeof (destPath), "%s/%s.tmp",
582 		a_rootPath, GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
583 	if (len > sizeof (srcPath)) {
584 		progerr(ERR_CREATE_PATH_2, a_rootPath,
585 				GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
586 		return (B_FALSE);
587 	}
588 
589 	/* path to temporary "saved" ingzonly file */
590 
591 	len = snprintf(savePath, sizeof (savePath), "%s/%s.save",
592 		a_rootPath, GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
593 	if (len > sizeof (srcPath)) {
594 		progerr(ERR_CREATE_PATH_2, a_rootPath,
595 				GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
596 		return (B_FALSE);
597 	}
598 
599 	/* open source file, creating if necessary */
600 
601 	srcFP = fopen(srcPath, "r+");
602 	if ((srcFP == (FILE *)NULL) && (errno == ENOENT)) {
603 		srcFP = fopen(srcPath, "w+");
604 	}
605 
606 	/* error if could not open/create file */
607 
608 	if (srcFP == (FILE *)NULL) {
609 		progerr(ERR_PKGOPS_OPEN_GZONLY, srcPath, strerror(errno));
610 		return (B_FALSE);
611 	}
612 
613 	/* open/create new destination file */
614 
615 	(void) remove(destPath);
616 	destFP = fopen(destPath, "w");
617 	if (destFP == (FILE *)NULL) {
618 		progerr(ERR_PKGOPS_TMPOPEN, destPath, strerror(errno));
619 		if (srcFP != (FILE *)NULL) {
620 			(void) fclose(srcFP);
621 		}
622 		return (B_FALSE);
623 	}
624 
625 	/* add standard comment to beginning of file */
626 
627 	(void) time(&clock);
628 	timep = localtime(&clock);
629 
630 	(void) strftime(timeb, sizeof (timeb), "%c\n", timep);
631 
632 	/* put standard header at the beginning of the file */
633 
634 	(void) fprintf(destFP, MSG_GZONLY_FILE_HEADER,
635 			get_prog_name(), "add", a_pkgInst, timeb);
636 
637 	/* read source/write destination; add package at appropriate location */
638 
639 	while (fgets(line, sizeof (line), srcFP) != (char *)NULL) {
640 		int	len;
641 
642 		/* strip off trailing newlines */
643 		len = strlen(line);
644 		while ((len > 0) && (line[len-1] == '\n')) {
645 			line[--len] = '\0';
646 		}
647 
648 		/* ignore blank and comment lines */
649 		if ((line[0] == '#') || (line[0] == '\0')) {
650 			continue;
651 		}
652 
653 		/* add pkg if yet to add and pkg <= line */
654 		if ((pkgadded == B_FALSE) && (strcmp(a_pkgInst, line) <= 0)) {
655 			if (strcmp(a_pkgInst, line) != 0) {
656 				(void) fprintf(destFP, "%s\n", a_pkgInst);
657 			}
658 			pkgadded = B_TRUE;
659 		}
660 
661 		(void) fprintf(destFP, "%s\n", line);
662 	}
663 
664 	/* if package not added yet, add to end of the file */
665 
666 	if (pkgadded == B_FALSE) {
667 		(void) fprintf(destFP, "%s\n", a_pkgInst);
668 	}
669 
670 	/* close both files */
671 
672 	(void) fclose(srcFP);
673 
674 	(void) fclose(destFP);
675 
676 	/*
677 	 * Now we want to make a copy of the old gzonly file as a
678 	 * fail-safe.
679 	 */
680 
681 	if ((access(savePath, F_OK) == 0) && remove(savePath)) {
682 		progerr(ERR_REMOVE, savePath, strerror(errno));
683 		(void) remove(destPath);
684 		return (B_FALSE);
685 	}
686 
687 	if (link(srcPath, savePath) != 0) {
688 		progerr(ERR_LINK, savePath, srcPath, strerror(errno));
689 		(void) remove(destPath);
690 		return (B_FALSE);
691 	}
692 
693 	if (rename(destPath, srcPath) != 0) {
694 		progerr(ERR_RENAME, destPath, srcPath, strerror(errno));
695 		if (rename(savePath, srcPath)) {
696 			progerr(ERR_RENAME, savePath, srcPath, strerror(errno));
697 		}
698 		(void) remove(destPath);
699 		return (B_FALSE);
700 	}
701 
702 	if (remove(savePath) != 0) {
703 		progerr(ERR_REMOVE, savePath, strerror(errno));
704 	}
705 
706 	/* successfully added package */
707 
708 	echoDebug(DBG_PKGOPS_ADDED_GZPKG, a_pkgInst);
709 
710 	return (B_TRUE);
711 }
712 
713 /*
714  * Name:	pkginfoParamTruth
715  * Description:	Search pkginfo file for specified parameter/value pair
716  * Arguments:	a_fp - Pointer to FILE handle open on pkginfo file to search
717  *		a_param - Pointer to string representing the parameter name
718  *			to search for
719  *		a_value - Pointer to string representing the "success" value
720  *			being searched for
721  *		a_default - determine results if parameter NOT found
722  *			B_TRUE - parameter is TRUE if not found
723  *			B_FALSE - parameter is FALSE if not found
724  * Returns:	boolean_t
725  *		B_TRUE - the parameter was found and matched the specified value
726  *			OR the paramter was not found and a_default == B_TRUE
727  *		B_FALSE - the parameter was found and did NOT match the value
728  *			OR the paramter was not found and a_default == B_FALSE
729  */
730 
731 boolean_t
732 pkginfoParamTruth(FILE *a_fp, char *a_param, char *a_value, boolean_t a_default)
733 {
734 	char		*param;
735 	boolean_t	result;
736 
737 	/* entry assertions */
738 
739 	assert(a_fp != (FILE *)NULL);
740 	assert(a_param != (char *)NULL);
741 	assert(*a_param != '\0');
742 	assert(a_value != (char *)NULL);
743 	assert(*a_value != '\0');
744 
745 	/* rewind the file to the beginning */
746 
747 	rewind(a_fp);
748 
749 	/* search pkginfo file for the specified parameter */
750 
751 	param = fpkgparam(a_fp, a_param);
752 
753 	if (param == (char *)NULL) {
754 		/* parameter not found - return default */
755 		result = a_default;
756 	} else if (*param == '\0') {
757 		/* parameter found but no value - return default */
758 		result = a_default;
759 	} else if (strcasecmp(param, a_value) == 0) {
760 		/* paramter found - matches value */
761 		result = B_TRUE;
762 	} else {
763 		/* parameter found - does not match value */
764 		result = B_FALSE;
765 	}
766 
767 	/* exit debugging info */
768 
769 	echoDebug(DBG_PKGOPS_PARAMTRUTH_RESULTS,
770 		a_param, a_value, a_default == B_TRUE ? "true" : "false",
771 		param ? param : "?", result == B_TRUE ? "true" : "false");
772 
773 	/* if parameter value found, free results */
774 
775 	if (param != (char *)NULL) {
776 		(void) free(param);
777 	}
778 
779 	/* return results of search */
780 
781 	return (result);
782 }
783 
784 /*
785  * Name:	pkgGetPackageList
786  * Description:	Determine list of packages based on list of packages that are
787  *		available, category of packages to select, and list of packages
788  *		to select.
789  * Arguments:	r_pkgList - pointer to pointer to string array where the list
790  *			of selected packages will be returned
791  *		a_argv - pointer to string array containing list of packages
792  *			to select
793  *		a_optind - index into string array of first package to select
794  *		a_categories - pointer to string representing the categories of
795  *			packages to select
796  *		a_categoryList - pointer to string array representing a list
797  *			of categories to select
798  *		a_pkgdev - package dev containing packages that can be selected
799  * Returns:	int
800  *	== 0  - packages found r_pkgList contains results package list retrieved
801  *	== -1 - no packages found (errno == ENOPKG)
802  *	!= 0 - "quit" value entered by user
803  * NOTE:	If both a category and a list of packages to select are provided
804  *		the category is used over the list of packages provided
805  * NOTE:	If neither a category nor a list of packages to select are
806  *		provided, an error is returned
807  */
808 
809 int
810 pkgGetPackageList(char ***r_pkgList, char **a_argv, int a_optind,
811 	char *a_categories, char **a_categoryList, struct pkgdev *a_pkgdev)
812 {
813 	char	*all_pkgs[4] = {"all", NULL};
814 
815 	/* entry assertions */
816 
817 	assert(a_pkgdev != (struct pkgdev *)NULL);
818 	assert(a_pkgdev->dirname != (char *)NULL);
819 	assert(*a_pkgdev->dirname != '\0');
820 	assert(r_pkgList != (char ***)NULL);
821 	assert(a_argv != (char **)NULL);
822 
823 	/* entry debugging info */
824 
825 	echoDebug(DBG_PKGOPS_GETPKGLIST_ENTRY);
826 	echoDebug(DBG_PKGOPS_GETPKGLIST_ARGS, a_pkgdev->dirname,
827 			a_categories ? a_categories : "?");
828 
829 	/* reset returned package list handle */
830 
831 	*r_pkgList = (char **)NULL;
832 
833 	/*
834 	 * generate list of packages to be removed: if removing by category,
835 	 * then generate package list based on all packages by category,
836 	 * else generate package list based on all packages specified.
837 	 */
838 
839 	if (a_categories != NULL) {
840 		/* generate package list from all packages in given category */
841 
842 		*r_pkgList = gpkglist(a_pkgdev->dirname, &all_pkgs[0],
843 					a_categoryList);
844 
845 		if (*r_pkgList == NULL) {
846 			echoDebug(DBG_PKGOPS_GPKGLIST_CATFAILED, a_categories);
847 			progerr(ERR_CAT_FND, a_categories);
848 			return (1);
849 		}
850 
851 		echoDebug(DBG_PKGOPS_GPKGLIST_CATOK, a_categories);
852 
853 		return (0);
854 	}
855 
856 	/* generate package list from specified packages */
857 
858 	*r_pkgList = gpkglist(a_pkgdev->dirname, &a_argv[a_optind], NULL);
859 
860 	/* if list generated return results */
861 
862 	if (*r_pkgList != NULL) {
863 		echoDebug(DBG_PKGOPS_GPKGLIST_OK);
864 		return (0);
865 	}
866 
867 	/* handle error from gpkglist */
868 
869 	switch (errno) {
870 	    case ENOPKG:	/* no packages */
871 		echoDebug(DBG_PKGOPS_GPKGLIST_ENOPKG);
872 		return (-1);
873 
874 	    case ESRCH:
875 		echoDebug(DBG_PKGOPS_GPKGLIST_ESRCH);
876 		return (1);
877 
878 	    case EINTR:
879 		echoDebug(DBG_PKGOPS_GPKGLIST_EINTR);
880 		return (3);
881 
882 	    default:
883 		echoDebug(DBG_PKGOPS_GPKGLIST_UNKNOWN, errno);
884 		progerr(ERR_GPKGLIST_ERROR);
885 		return (99);
886 	}
887 }
888 
889 /*
890  * return string representing path to "global zone only file"
891  */
892 
893 char *
894 pkgGetGzOnlyPath(void)
895 {
896 	return (GLOBALZONE_ONLY_PACKAGE_FILE_PATH);
897 }
898 
899 /*
900  * Name:	pkgAddThisZonePackage
901  * Description:	Add specified package to internal list of "this zone only" pkgs
902  * Arguments:	a_pkgInst - name of package to add to list
903  * Returns:	void
904  */
905 
906 void
907 pkgAddThisZonePackage(char *a_pkgInst)
908 {
909 	/* entry assertions */
910 
911 	assert(a_pkgInst != (char *)NULL);
912 	assert(*a_pkgInst != '\0');
913 
914 	/* do not duplicate entries */
915 
916 	if (pkgPackageIsThisZone(a_pkgInst) == B_TRUE) {
917 		return;
918 	}
919 
920 	/* add package name to internal list */
921 
922 	if (thisZonePackages == (char **)NULL) {
923 		thisZonePackages =
924 				(char **)calloc(2, sizeof (char **));
925 	} else {
926 		thisZonePackages =
927 				(char **)realloc(thisZonePackages,
928 				sizeof (char **)*(numThisZonePackages+2));
929 	}
930 
931 	/* handle out of memory error */
932 
933 	if (thisZonePackages == (char **)NULL) {
934 		progerr(ERR_MEMORY, errno);
935 		quit(99);
936 	}
937 
938 	/* add this entry to the end of the list */
939 
940 	thisZonePackages[numThisZonePackages] = strdup(a_pkgInst);
941 	if (thisZonePackages[numThisZonePackages] == (char *)NULL) {
942 		progerr(ERR_MEMORY, errno);
943 		quit(99);
944 	}
945 
946 	numThisZonePackages++;
947 
948 	/* make sure end of the list is properly terminated */
949 
950 	thisZonePackages[numThisZonePackages] = (char *)NULL;
951 
952 	/* exit debugging info */
953 
954 	echoDebug(DBG_PKGOPS_ADD_TZP, numThisZonePackages,
955 			thisZonePackages[numThisZonePackages-1]);
956 }
957 
958 /*
959  * Name:	pkgPackageIsThisZone
960  * Description:	Determine if the specified package is marked to be installed
961  *		in this zone only
962  * Arguments:	a_pkgInst - pointer to string representing package name to check
963  * Returns:	boolean_t
964  *			B_TRUE - the package IS "this zone only"
965  *			B_FALSE - the paackage is NOT "this zone only"
966  */
967 
968 boolean_t
969 pkgPackageIsThisZone(char *a_pkgInst)
970 {
971 	int		n;
972 
973 	/* entry assertions */
974 
975 	assert(a_pkgInst != (char *)NULL);
976 	assert(*a_pkgInst != '\0');
977 
978 	/*
979 	 * see if this package is in the "this zone only" list
980 	 */
981 
982 	for (n = 0; n < numThisZonePackages; n++) {
983 		if (strcmp(a_pkgInst, thisZonePackages[n]) == 0) {
984 			echoDebug(DBG_PKGOPS_IS_THISZONE, a_pkgInst);
985 			return (B_TRUE);
986 		}
987 	}
988 
989 	/* path is not in "this zone only" list */
990 
991 	echoDebug(DBG_PKGOPS_IS_NOT_THISZONE, a_pkgInst);
992 
993 	return (B_FALSE);
994 }
995 
996 /*
997  * Name:	pkgLocateHighestInst
998  * Description:	Locate the highest installed instance of a package
999  * Arguments:	r_path - [RO, *RW] - (char *)
1000  *			Pointer to buffer where the full path to the top level
1001  *			directory containing the latest instance of the
1002  *			specified package is located is placed.
1003  *		r_pathLen - [RO, *RO] - (int)
1004  *			Integer representing the size of r_path in bytes.
1005  *		r_pkgInst - [RO, *RW] - (char *)
1006  *			Pointer to buffer where the package instance name of the
1007  *			latest instance of the specified package is placed.
1008  *		r_pkgInstLen - [RO, *RO] - (int)
1009  *			Integer representing the size of r_pkgInst in bytes.
1010  *		a_rootPath - [RO, *RO] - (char *)
1011  *			Pointer to string representing the root path to look
1012  *			for the latest instance of the specified package.
1013  *		a_pkgInst - [RO, *RO] - (char *)
1014  *			Pointer to string representing the name of the package
1015  *			to locate the latest installed instance of.
1016  */
1017 
1018 void
1019 pkgLocateHighestInst(char *r_path, int r_pathLen, char *r_pkgInst,
1020 	int r_pkgInstLen, char *a_rootPath, char *a_pkgInst)
1021 {
1022 	char		pkgInstPath[PATH_MAX] = {'\0'};
1023 	char		pkgWild[PKGSIZ+1] = {'\0'};
1024 	char		pkgName[PKGSIZ+1] = {'\0'};
1025 	int		npkgs;
1026 	struct pkginfo	*pinf = (struct pkginfo *)NULL;
1027 
1028 	/* entry assertions */
1029 
1030 	assert(r_path != (char *)NULL);
1031 	assert(r_pathLen > 0);
1032 	assert(r_pkgInst != (char *)NULL);
1033 	assert(r_pkgInstLen > 0);
1034 	assert(a_pkgInst != (char *)NULL);
1035 	assert(*a_pkgInst != '\0');
1036 
1037 	/* normalize root path */
1038 
1039 	if ((a_rootPath == (char *)NULL) || (strcmp(a_rootPath, "/") == 0)) {
1040 		a_rootPath = "";
1041 	}
1042 
1043 	/* construct path to package repository directory (eg. /var/sadm/pkg) */
1044 
1045 	(void) snprintf(pkgInstPath, sizeof (pkgInstPath), "%s%s", a_rootPath,
1046 		PKGLOC);
1047 
1048 	/* entry debugging info */
1049 
1050 	echoDebug(DBG_PKGOPS_LOCHIGH_ENTRY);
1051 	echoDebug(DBG_PKGOPS_LOCHIGH_ARGS, pkgInstPath, a_pkgInst);
1052 
1053 	/* reset returned path/package instance so both ares empty */
1054 
1055 	*r_path = '\0';
1056 	*r_pkgInst = '\0';
1057 
1058 	/* remove any architecture extension */
1059 
1060 	pkgstrGetToken_r((char *)NULL, a_pkgInst, 0, ".",
1061 		pkgName, sizeof (pkgName));
1062 
1063 	/* make sure that the package name is valid and can be wild carded */
1064 
1065 	if (pkgnmchk(pkgName, NULL, 0) || strchr(pkgName, '.')) {
1066 		progerr(ERR_PKGOPS_LOCHIGH_BAD_PKGNAME, pkgName);
1067 		quit(99);
1068 	}
1069 
1070 	/* create wild card specification for this package instance */
1071 
1072 	(void) snprintf(pkgWild, sizeof (pkgWild), "%s.*", pkgName);
1073 
1074 	echoDebug(DBG_PKGOPS_LOCHIGH_WILDCARD, pkgName, pkgWild);
1075 
1076 	/*
1077 	 * inspect the system to determine if any instances of the
1078 	 * package being installed already exist on the system
1079 	 */
1080 
1081 	for (npkgs = 0; ; npkgs++) {
1082 		char	*savePkgdir;
1083 		int	r;
1084 
1085 		/* allocate new pinfo structure for use in the pkginfo call */
1086 
1087 		pinf = _pkginfoFactory();
1088 
1089 		/*
1090 		 * lookup the specified package; the first call will cause the
1091 		 * pkgdir directory to be opened - it will be closed when the
1092 		 * end of directory is read and pkginfo() returns != 0. You must
1093 		 * cycle through all instances until pkginfo() returns != 0.
1094 		 * NOTE: pkginfo() requires the global variable 'pkgdir' be set
1095 		 * to the package installed directory (<root>/var/sadm/pkg).
1096 		 */
1097 
1098 		savePkgdir = pkgdir;
1099 		pkgdir = pkgInstPath;
1100 
1101 		r = pkginfo(pinf, pkgWild, NULL, NULL);
1102 
1103 		pkgdir = savePkgdir;
1104 
1105 		echoDebug(DBG_PKGOPS_PKGINFO_RETURNED, pkgName, r);
1106 
1107 		/* break out of loop of no package found */
1108 
1109 		if (r != 0) {
1110 			pkginfoFree(&pinf);
1111 			break;
1112 		}
1113 
1114 		echoDebug(DBG_PKGOPS_LOCHIGH_INSTANCE, npkgs,
1115 			pinf->pkginst ? pinf->pkginst : "",
1116 			pinf->name ? pinf->name : "",
1117 			pinf->arch ? pinf->arch : "",
1118 			pinf->version ? pinf->version : "",
1119 			pinf->vendor ? pinf->vendor : "",
1120 			pinf->basedir ? pinf->basedir : "",
1121 			pinf->catg ? pinf->catg : "",
1122 			pinf->status);
1123 
1124 		/* save path/instance name for this instance found */
1125 
1126 		(void) strlcpy(r_pkgInst, pinf->pkginst, r_pkgInstLen);
1127 		pkgstrPrintf_r(r_path, r_pathLen, "%s%s/%s", a_rootPath,
1128 			PKGLOC, pinf->pkginst);
1129 
1130 		pkginfoFree(&pinf);
1131 	}
1132 
1133 	echoDebug(DBG_PKGOPS_LOCHIGH_RETURN, npkgs, r_pkgInst, r_path);
1134 }
1135 
1136 /*
1137  * Name:	pkgTestInstalled
1138  * Description:	determine if package is installed at specified root path
1139  * Arguments:	a_packageName - name of package to test
1140  * 		a_rootPath - root path of alternative root to test
1141  * Returns:	B_TRUE - package is installed
1142  *		B_FALSE - package is not installed
1143  */
1144 
1145 boolean_t
1146 pkgTestInstalled(char *a_packageName, char *a_rootPath)
1147 {
1148 	char	cmd[MAXPATHLEN+1];
1149 	int	rc;
1150 
1151 	/* entry assertions */
1152 
1153 	assert(a_packageName != (char *)NULL);
1154 	assert(*a_packageName != '\0');
1155 	assert(a_rootPath != (char *)NULL);
1156 	assert(a_rootPath != '\0');
1157 
1158 	/* entry debugging info */
1159 
1160 	echoDebug(DBG_PKG_TEST_EXISTENCE, a_packageName, a_rootPath);
1161 
1162 	/*
1163 	 * create pkginfo command to execute:
1164 	 * /usr/bin/pkginfo -q <packageName>
1165 	 */
1166 	(void) snprintf(cmd, sizeof (cmd),
1167 		"%s -q %s", PKGINFO_CMD, a_packageName);
1168 
1169 	/* execute command */
1170 
1171 	rc = system(cmd);
1172 
1173 	/* return success if pkginfo returns "0" */
1174 
1175 	if (rc == 0) {
1176 		echoDebug(DBG_PKG_INSTALLED, a_packageName, a_rootPath);
1177 		return (B_TRUE);
1178 	}
1179 
1180 	/* package not installed */
1181 
1182 	echoDebug(DBG_PKG_NOT_INSTALLED, a_packageName, a_rootPath);
1183 
1184 	return (B_FALSE);
1185 }
1186 
1187 /*
1188  * *****************************************************************************
1189  * static internal (private) functions
1190  * *****************************************************************************
1191  */
1192 
1193 static void
1194 _pkginfoInit(struct pkginfo *a_info)
1195 {
1196 	/* entry assertions */
1197 
1198 	assert(a_info != (struct pkginfo *)NULL);
1199 
1200 	/* free previously allocated space */
1201 
1202 	if (a_info->pkginst) {
1203 		free(a_info->pkginst);
1204 		if (a_info->arch)
1205 			free(a_info->arch);
1206 		if (a_info->version)
1207 			free(a_info->version);
1208 		if (a_info->basedir)
1209 			free(a_info->basedir);
1210 		if (a_info->name)
1211 			free(a_info->name);
1212 		if (a_info->vendor)
1213 			free(a_info->vendor);
1214 		if (a_info->catg)
1215 			free(a_info->catg);
1216 	}
1217 
1218 	a_info->pkginst = NULL;
1219 	a_info->arch = a_info->version = NULL;
1220 	a_info->basedir = a_info->name = NULL;
1221 	a_info->vendor = a_info->catg = NULL;
1222 	a_info->status = PI_UNKNOWN;
1223 }
1224 
1225 static struct pkginfo *
1226 _pkginfoFactory(void)
1227 {
1228 	struct pkginfo *pinf;
1229 
1230 	pinf = (struct pkginfo *)calloc(1, sizeof (struct pkginfo));
1231 	if (pinf == (struct pkginfo *)NULL) {
1232 		progerr(ERR_MEM);
1233 		exit(1);
1234 	}
1235 
1236 	_pkginfoInit(pinf);
1237 	return (pinf);
1238 }
1239