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