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