xref: /illumos-gate/usr/src/cmd/svr4pkg/pkgchk/main.c (revision 24fe0b3bf671e123467ce1df0b67cadd3614c8e4)
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 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29 
30 
31 #include <stdio.h>
32 #include <limits.h>
33 #include <unistd.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <dirent.h>
38 #include <sys/stat.h>
39 #include <pkginfo.h>
40 #include <pkglocs.h>
41 #include <sys/types.h>
42 #include <pkgstrct.h>
43 #include <pkgtrans.h>
44 #include <locale.h>
45 #include <libintl.h>
46 #include <pkglib.h>
47 #include <libadm.h>
48 #include <libinst.h>
49 
50 #define	MAXPATHS	1024
51 
52 #define	MSG_CHK_STRM	"Checking uninstalled stream format package " \
53 				"<%s> from <%s>\n"
54 #define	MSG_CHK_DIR	"Checking uninstalled directory format package " \
55 				"<%s> from <%s>\n"
56 #define	MSG_NOTROOT	"NOTE: \"root\" permission may be required to " \
57 				"validate all objects in the client filesystem."
58 #define	MSG_CONT	"Continuing."
59 
60 #define	WRN_F_SPOOL	"WARNING: %s is spooled. Ignoring \"f\" argument"
61 
62 #define	ERR_ROOT_SET	"Could not set install root from the environment."
63 #define	ERR_ROOT_CMD	"Command line install root contends with environment."
64 #define	ERR_IOPEN	"unable to open input file <%s>"
65 #define	ERR_IEMPTY	"no pathnames in file specified by -i option"
66 #define	ERR_POPTION	"no pathname included with -p option"
67 #define	ERR_PARTIAL_POPTION	"no pathname included with -P option"
68 #define	ERR_MAXPATHS	"too many pathnames in option list (limit is %d)"
69 #define	ERR_NOTROOT	"You must be \"root\" for \"%s -f\" to" \
70 					"execute properly."
71 #define	ERR_SEL_PKG "No packages selected for verification."
72 #define	ERR_CAT_LNGTH "The category argument exceeds the SVr4 ABI\n" \
73 		"        defined maximum supported length of 16 characters."
74 #define	ERR_CAT_FND "Category argument <%s> cannot be found."
75 #define	ERR_CAT_INV "Category argument <%s> is invalid."
76 #define	ERR_TOO_MANY "too many pathnames in list, limit is %d"
77 #define	ERR_PATHS_INVALID "Pathnames in %s are not valid."
78 #define	ERR_MKDIR "unable to make directory <%s>"
79 #define	ERR_USAGE	"usage:\n" \
80 		"\t%s [-l|vqacnxf] [-R rootdir] [-p path[, ...] | " \
81 		"-P path[, ...]]\n" \
82 		"\t\t[-i file] [options]\n" \
83 		"\t%s -d device [-f][-l|v] [-p path[, ...] | " \
84 		"-P path[, ...]]\n" \
85 		"\t\t[-V ...] [-M] [-i file] [-Y category[, ...] | " \
86 		"pkginst [...]]\n" \
87 		"\twhere options may include ONE of the " \
88 		"following:\n " \
89 		"\t\t-m pkgmap [-e envfile]\n" \
90 		"\t\tpkginst [...]\n" \
91 		"\t\t-Y category[, ...]\n"
92 
93 #define	LINK	1
94 
95 char	**pkg = NULL;
96 int	pkgcnt = 0;
97 char	*basedir;
98 char	*pathlist[MAXPATHS], *ppathlist[MAXPATHS], pkgspool[PATH_MAX];
99 short	used[MAXPATHS];
100 short	npaths;
101 struct cfent **eptlist;
102 
103 int	aflag = (-1);
104 int	cflag = (-1);
105 int	vflag = 0;
106 int	nflag = 0;
107 int	lflag = 0;
108 int	Lflag = 0;
109 int	fflag = 0;
110 int	xflag = 0;
111 int	qflag = 0;
112 int	Rflag = 0;
113 int	dflag = 0;
114 char 	*device;
115 
116 char	*uniTmp;
117 
118 static char	*mapfile,
119 		*spooldir,
120 		*tmpdir,
121 		*envfile;
122 static int	errflg = 0;
123 static int	map_client = 1;
124 
125 void	quit(int);
126 static void	setpathlist(char *);
127 static void	usage(void);
128 
129 extern	char	**environ;
130 extern	char	*pkgdir;
131 
132 /* checkmap.c */
133 extern int	checkmap(int, int, char *, char *, char *, char *, int);
134 /* scriptvfy.c */
135 extern int	checkscripts(char *inst_dir, int silent);
136 
137 int
138 main(int argc, char *argv[])
139 {
140 	int	pkgfmt = 0;	/* Makes more sense as a pointer, but */
141 				/*	18N is compromised. */
142 	char	file[PATH_MAX+1],
143 		*abi_sym_ptr,
144 		*vfstab_file = NULL;
145 	char *all_pkgs[4] = {"all", NULL};
146 	char **category = NULL;
147 	char *catg_arg = NULL;
148 	int	c;
149 	int	n = 0;
150 	char	*prog,
151 		*Rvalue,
152 		*dvalue;
153 	int dbcreate = 0;
154 	int pathtype;
155 
156 	/* initialize locale mechanism */
157 
158 	(void) setlocale(LC_ALL, "");
159 
160 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
161 #define	TEXT_DOMAIN "SYS_TEST"
162 #endif
163 	(void) textdomain(TEXT_DOMAIN);
164 
165 	/* determine program name */
166 
167 	prog = set_prog_name(argv[0]);
168 
169 	/* establish installation root directory */
170 
171 	if (!set_inst_root(getenv("PKG_INSTALL_ROOT"))) {
172 		progerr(gettext(ERR_ROOT_SET));
173 		quit(1);
174 	}
175 
176 	/* check if not ABI compliant mode */
177 	abi_sym_ptr = getenv("PKG_NONABI_SYMLINKS");
178 	if (abi_sym_ptr && strncasecmp(abi_sym_ptr, "TRUE", 4) == 0) {
179 		set_nonABI_symlinks();
180 	}
181 
182 	/* bugId 4012147 */
183 	if ((uniTmp = getenv("PKG_NO_UNIFIED")) != NULL)
184 		map_client = 0;
185 
186 	while ((c = getopt(argc, argv, "Y:R:e:p:d:nLli:vaV:Mm:cqxfQP:?"))
187 			!= EOF) {
188 		switch (c) {
189 		case 'p':
190 			pathlist[npaths] = strtok(optarg, " , ");
191 			if (pathlist[npaths++] == NULL) {
192 				progerr(gettext(ERR_POPTION));
193 				quit(1);
194 			}
195 			while (pathlist[npaths] = strtok(NULL, " , ")) {
196 				if (npaths++ >= MAXPATHS) {
197 					progerr(gettext(ERR_MAXPATHS),
198 						MAXPATHS);
199 					quit(1);
200 				}
201 			}
202 			break;
203 
204 		case 'd':
205 			dvalue = optarg;
206 			dflag = 1;
207 			break;
208 
209 		case 'n':
210 			nflag++;
211 			break;
212 
213 		case 'M':
214 			map_client = 0;
215 			break;
216 
217 		/*
218 		 * Allow admin to establish the client filesystem using a
219 		 * vfstab-like file of stable format.
220 		 */
221 		case 'V':
222 			vfstab_file = flex_device(optarg, 2);
223 			map_client = 1;
224 			break;
225 
226 		case 'f':
227 			if (getuid()) {
228 				progerr(gettext(ERR_NOTROOT), prog);
229 				quit(1);
230 			}
231 			fflag++;
232 			break;
233 
234 		case 'i':
235 			setpathlist(optarg);
236 			break;
237 
238 		case 'v':
239 			vflag++;
240 			break;
241 
242 		case 'l':
243 			lflag++;
244 			break;
245 
246 		case 'L':
247 			Lflag++;
248 			break;
249 
250 		case 'x':
251 			if (aflag < 0)
252 				aflag = 0;
253 			if (cflag < 0)
254 				cflag = 0;
255 			xflag++;
256 			break;
257 
258 		case 'q':
259 			qflag++;
260 			break;
261 
262 		case 'a':
263 			if (cflag < 0)
264 				cflag = 0;
265 			aflag = 1;
266 			break;
267 
268 		case 'c':
269 			if (aflag < 0)
270 				aflag = 0;
271 			cflag = 1;
272 			break;
273 
274 		case 'e':
275 			envfile = optarg;
276 			break;
277 
278 		case 'm':
279 			mapfile = optarg;
280 			break;
281 
282 		case 'R':
283 			Rvalue = optarg;
284 			Rflag = 1;
285 			break;
286 
287 		case 'Y':
288 			catg_arg = strdup(optarg);
289 
290 			if ((category = get_categories(catg_arg)) == NULL) {
291 				progerr(gettext(ERR_CAT_INV), catg_arg);
292 				quit(1);
293 			} else if (is_not_valid_length(category)) {
294 				progerr(gettext(ERR_CAT_LNGTH));
295 				quit(1);
296 			}
297 			break;
298 
299 		case 'Q':
300 			dbcreate++;
301 			break;
302 
303 		case 'P':
304 			ppathlist[npaths] = strtok(optarg, " , ");
305 			if ((ppathlist[npaths] == NULL) ||
306 			    (ppathlist[npaths][0] == '-')) {
307 				progerr(gettext(ERR_PARTIAL_POPTION));
308 				quit(1);
309 			}
310 			npaths++;
311 			while (ppathlist[npaths] = strtok(NULL, " , ")) {
312 				if (npaths++ >= MAXPATHS) {
313 					progerr(gettext(ERR_MAXPATHS),
314 						MAXPATHS);
315 					quit(1);
316 				}
317 			}
318 			break;
319 
320 		default:
321 			usage();
322 			/*NOTREACHED*/
323 			/*
324 			 * Although usage() calls a noreturn function,
325 			 * needed to add return (1);  so that main() would
326 			 * pass compilation checks. The statement below
327 			 * should never be executed.
328 			 */
329 			return (1);
330 		}
331 	}
332 
333 	/* Check for incompatible options */
334 	if (dflag && Rflag)
335 		usage();
336 
337 	/* Check for root dir and device dir if set */
338 	if (Rflag) {
339 		if (!set_inst_root(Rvalue)) {
340 			progerr(gettext(ERR_ROOT_CMD));
341 			quit(1);
342 		}
343 	}
344 
345 	if (dflag)
346 		device = flex_device(dvalue, 1);
347 
348 	if (lflag || Lflag) {
349 		/* we're only supposed to list information */
350 		if ((cflag >= 0) || (aflag >= 0) ||
351 		qflag || xflag || fflag || nflag || vflag)
352 			usage();
353 	}
354 
355 	set_PKGpaths(get_inst_root());
356 
357 	if (catg_arg != NULL && device == NULL) {
358 		if (argc - optind) {
359 			usage();
360 		}
361 		pkg = gpkglist(pkgdir, all_pkgs, category);
362 		if (pkg == NULL) {
363 			progerr(gettext(ERR_CAT_FND), catg_arg);
364 			quit(1);
365 		} else {
366 			for (pkgcnt = 0; pkg[pkgcnt] != NULL; pkgcnt++);
367 		}
368 	} else if (catg_arg != NULL && optind < argc) {
369 		usage();
370 	} else {
371 		pkg = &argv[optind];
372 		pkgcnt = (argc - optind);
373 	}
374 
375 	/* read the environment for the pkgserver */
376 	pkgserversetmode(DEFAULTMODE);
377 
378 	environ = NULL;		/* Sever the parent environment. */
379 
380 	if (vcfile() == 0) {
381 		quit(99);
382 	}
383 
384 	errflg = 0;
385 	if (mapfile) {
386 		/* check for incompatible options */
387 		if (device || pkgcnt)
388 			usage();
389 		put_path_params();	/* Restore what's needed. */
390 
391 		/* send pathtype if partial path */
392 		pathtype = (ppathlist[0] != NULL) ? 1 : 0;
393 		if (checkmap(0, (device != NULL), mapfile, envfile, NULL,
394 		    NULL, pathtype))
395 			errflg++;
396 	} else if (device) {
397 		/* check for incompatible options */
398 		if ((cflag >= 0) || (aflag >= 0))
399 			usage();
400 		if (qflag || xflag || nflag || envfile)
401 			usage();
402 		tmpdir = NULL;
403 		if ((spooldir = devattr(device, "pathname")) == NULL)
404 			spooldir = device;
405 		if (isdir(spooldir)) {
406 			tmpdir = spooldir = qstrdup(tmpnam(NULL));
407 			if (fflag) {
408 				logerr(gettext(WRN_F_SPOOL), *pkg);
409 				fflag = 0;
410 			}
411 			if (mkdir(spooldir, 0755)) {
412 				progerr(gettext(ERR_MKDIR), spooldir);
413 				quit(99);
414 			}
415 			if (n = pkgtrans(device, spooldir, pkg, PT_SILENT,
416 				NULL, NULL))
417 				quit(n);
418 			if (catg_arg != NULL)
419 				pkg = gpkglist(spooldir, all_pkgs, category);
420 			else
421 				pkg = gpkglist(spooldir, all_pkgs, NULL);
422 			pkgfmt = 0;
423 		} else {
424 			if (catg_arg != NULL)
425 				pkg = gpkglist(spooldir,
426 					pkgcnt ? pkg : all_pkgs, category);
427 			else
428 				pkg = gpkglist(spooldir,
429 					pkgcnt ? pkg : all_pkgs, NULL);
430 			pkgfmt = 1;
431 		}
432 
433 		/*
434 		 * At this point pkg[] is the list of packages to check. They
435 		 * are in directory format in spooldir.
436 		 */
437 		if (pkg == NULL) {
438 			if (catg_arg != NULL) {
439 				progerr(gettext(ERR_CAT_FND), catg_arg);
440 				quit(1);
441 			} else {
442 				progerr(gettext(ERR_SEL_PKG));
443 				quit(1);
444 			}
445 		}
446 
447 		aflag = 0;
448 
449 		for (n = 0; pkg[n]; n++) {
450 			char locenv[PATH_MAX];
451 
452 	/*
453 	 * *********************************************************************
454 	 * this feature is removed starting with Solaris 10 - there is no built
455 	 * in list of packages that should be run "the old way"
456 	 * *********************************************************************
457 	 */
458 #ifdef	ALLOW_EXCEPTION_PKG_LIST
459 			/* Until 2.9, set it from the execption list */
460 			if (exception_pkg(pkg[n], LINK))
461 				set_nonABI_symlinks();
462 #endif
463 
464 			if (pkgfmt)
465 				(void) printf(
466 					gettext(MSG_CHK_DIR), pkg[n], device);
467 			else
468 				(void) printf(
469 					gettext(MSG_CHK_STRM), pkg[n], device);
470 
471 			(void) snprintf(pkgspool, sizeof (pkgspool),
472 				"%s/%s", spooldir, pkg[n]);
473 			(void) snprintf(file, sizeof (file),
474 				"%s/install", pkgspool);
475 			/* Here we check the install scripts. */
476 			(void) printf(
477 				gettext("## Checking control scripts.\n"));
478 			(void) checkscripts(file, 0);
479 			/* Verify consistency with the pkgmap. */
480 			(void) printf(
481 				gettext("## Checking package objects.\n"));
482 			(void) snprintf(file, sizeof (file),
483 				"%s/pkgmap", pkgspool);
484 			(void) snprintf(locenv, sizeof (locenv),
485 				"%s/pkginfo", pkgspool);
486 			envfile = locenv;
487 
488 			/*
489 			 * NOTE : checkmap() frees the environ data and
490 			 * pointer when it's through with them.
491 			 */
492 			if (checkmap(0, (device != NULL), file, envfile,
493 					pkg[n], NULL, 0))
494 				errflg++;
495 			(void) printf(
496 				gettext("## Checking is complete.\n"));
497 		}
498 	} else {
499 		if (envfile)
500 			usage();
501 
502 		put_path_params();	/* Restore what's needed. */
503 
504 		/*
505 		 * If this is a check of a client of some sort, we'll need to
506 		 * mount up the client's filesystems. If the caller isn't
507 		 * root, this may not be possible.
508 		 */
509 		if (is_an_inst_root()) {
510 			if (getuid()) {
511 				logerr(gettext(MSG_NOTROOT));
512 				logerr(gettext(MSG_CONT));
513 			} else {
514 				if (get_mntinfo(map_client, vfstab_file))
515 					map_client = 0;
516 				if (map_client)
517 					mount_client();
518 			}
519 		}
520 
521 		(void) snprintf(file, sizeof (file),
522 			"%s/contents", get_PKGADM());
523 		if (ppathlist[0] != NULL) {
524 			for (n = 0; ppathlist[n]; n++) {
525 				if (checkmap(1, (device != NULL), file, NULL,
526 						NULL, ppathlist[n], 1))
527 					errflg++;
528 			}
529 		} else if (pkg[0] != NULL) {
530 				if (checkmap(1, (device != NULL), file, NULL,
531 					pkg[0], NULL, 0)) {
532 					errflg++;
533 				}
534 		} else {
535 			if (checkmap(1, (device != NULL), file, NULL,
536 					NULL, NULL, 0)) {
537 				errflg++;
538 			}
539 		}
540 
541 		if (map_client) {
542 			unmount_client();
543 		}
544 	}
545 	quit(errflg ? 1 : 0);
546 	/* LINTED: no return */
547 }
548 
549 static void
550 setpathlist(char *file)
551 {
552 	int fd;
553 	struct stat st;
554 	FILE *fplist;
555 	char pathname[PATH_MAX];
556 	/*
557 	 * This trap laid to catch a mismatch between the declaration above and
558 	 * the hard-coded constant in the fscanf below
559 	 */
560 #if PATH_MAX != 1024
561 #error "PATH_MAX changed, so we have a bug to fix"
562 #endif
563 
564 	if (strcmp(file, "-") == 0) {
565 		fplist = stdin;
566 	} else {
567 		if ((fd = open(file, O_RDONLY)) == -1) {
568 			progerr(gettext(ERR_IOPEN), file);
569 			quit(1);
570 		}
571 		if (fstat(fd, &st) == -1) {
572 			progerr(gettext(ERR_IOPEN), file);
573 			quit(1);
574 		}
575 		if (S_ISDIR(st.st_mode) || S_ISBLK(st.st_mode)) {
576 			progerr(gettext(ERR_PATHS_INVALID), file);
577 			quit(1);
578 		}
579 		if ((fplist = fdopen(fd, "r")) == NULL) {
580 			progerr(gettext(ERR_IOPEN), file);
581 			quit(1);
582 		}
583 	}
584 	while (fscanf(fplist, "%1024s", pathname) == 1) {
585 		if (*pathname == '\0') {
586 			progerr(gettext(ERR_PATHS_INVALID), file);
587 			quit(1);
588 		}
589 		pathlist[npaths] = qstrdup(pathname);
590 		if (npaths++ > MAXPATHS) {
591 			progerr(gettext(ERR_TOO_MANY), MAXPATHS);
592 			quit(1);
593 		}
594 	}
595 	if (npaths == 0) {
596 		progerr(gettext(ERR_IEMPTY));
597 		quit(1);
598 	}
599 	(void) fclose(fplist);
600 }
601 
602 void
603 quit(int n)
604 {
605 	/* cleanup any temporary directories */
606 	(void) chdir("/");
607 	if (tmpdir != NULL) {
608 		(void) rrmdir(tmpdir);
609 		free(tmpdir);
610 		tmpdir = NULL;
611 	}
612 	(void) pkghead(NULL);
613 	exit(n);
614 	/*NOTREACHED*/
615 }
616 
617 static void
618 usage(void)
619 {
620 	char *prog = get_prog_name();
621 
622 	(void) fprintf(stderr, gettext(ERR_USAGE), prog, prog);
623 	quit(1);
624 	/*NOTREACHED*/
625 }
626