xref: /illumos-gate/usr/src/cmd/svr4pkg/pkgchk/main.c (revision 4c28a617e3922d92a58e813a5b955eb526b9c386)
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 pathtype;
158 
159 	/* initialize locale mechanism */
160 
161 	(void) setlocale(LC_ALL, "");
162 
163 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
164 #define	TEXT_DOMAIN "SYS_TEST"
165 #endif
166 	(void) textdomain(TEXT_DOMAIN);
167 
168 	/* determine program name */
169 
170 	prog = set_prog_name(argv[0]);
171 
172 	/* establish installation root directory */
173 
174 	if (!set_inst_root(getenv("PKG_INSTALL_ROOT"))) {
175 		progerr(gettext(ERR_ROOT_SET));
176 		quit(1);
177 	}
178 
179 	/* check if not ABI compliant mode */
180 	abi_sym_ptr = getenv("PKG_NONABI_SYMLINKS");
181 	if (abi_sym_ptr && strncasecmp(abi_sym_ptr, "TRUE", 4) == 0) {
182 		set_nonABI_symlinks();
183 	}
184 
185 	/* bugId 4012147 */
186 	if ((uniTmp = getenv("PKG_NO_UNIFIED")) != NULL)
187 		map_client = 0;
188 
189 	while ((c = getopt(argc, argv, "Y:R:e:p:d:nLli:vaV:Mm:cqxfP:?"))
190 	    != EOF) {
191 		switch (c) {
192 		case 'p':
193 			pathlist[npaths] = strtok(optarg, " , ");
194 			if (pathlist[npaths++] == NULL) {
195 				progerr(gettext(ERR_POPTION));
196 				quit(1);
197 			}
198 			while (pathlist[npaths] = strtok(NULL, " , ")) {
199 				if (npaths++ >= MAXPATHS) {
200 					progerr(gettext(ERR_MAXPATHS),
201 					    MAXPATHS);
202 					quit(1);
203 				}
204 			}
205 			break;
206 
207 		case 'd':
208 			dvalue = optarg;
209 			dflag = 1;
210 			break;
211 
212 		case 'n':
213 			nflag++;
214 			break;
215 
216 		case 'M':
217 			map_client = 0;
218 			break;
219 
220 		/*
221 		 * Allow admin to establish the client filesystem using a
222 		 * vfstab-like file of stable format.
223 		 */
224 		case 'V':
225 			vfstab_file = flex_device(optarg, 2);
226 			map_client = 1;
227 			break;
228 
229 		case 'f':
230 			if (getuid()) {
231 				progerr(gettext(ERR_NOTROOT), prog);
232 				quit(1);
233 			}
234 			fflag++;
235 			break;
236 
237 		case 'i':
238 			setpathlist(optarg);
239 			break;
240 
241 		case 'v':
242 			vflag++;
243 			break;
244 
245 		case 'l':
246 			lflag++;
247 			break;
248 
249 		case 'L':
250 			Lflag++;
251 			break;
252 
253 		case 'x':
254 			if (aflag < 0)
255 				aflag = 0;
256 			if (cflag < 0)
257 				cflag = 0;
258 			xflag++;
259 			break;
260 
261 		case 'q':
262 			qflag++;
263 			break;
264 
265 		case 'a':
266 			if (cflag < 0)
267 				cflag = 0;
268 			aflag = 1;
269 			break;
270 
271 		case 'c':
272 			if (aflag < 0)
273 				aflag = 0;
274 			cflag = 1;
275 			break;
276 
277 		case 'e':
278 			envfile = optarg;
279 			break;
280 
281 		case 'm':
282 			mapfile = optarg;
283 			break;
284 
285 		case 'R':
286 			Rvalue = optarg;
287 			Rflag = 1;
288 			break;
289 
290 		case 'Y':
291 			catg_arg = strdup(optarg);
292 
293 			if ((category = get_categories(catg_arg)) == NULL) {
294 				progerr(gettext(ERR_CAT_INV), catg_arg);
295 				quit(1);
296 			} else if (is_not_valid_length(category)) {
297 				progerr(gettext(ERR_CAT_LNGTH));
298 				quit(1);
299 			}
300 			break;
301 
302 		case 'P':
303 			ppathlist[npaths] = strtok(optarg, " , ");
304 			if ((ppathlist[npaths] == NULL) ||
305 			    (ppathlist[npaths][0] == '-')) {
306 				progerr(gettext(ERR_PARTIAL_POPTION));
307 				quit(1);
308 			}
309 			npaths++;
310 			while (ppathlist[npaths] = strtok(NULL, " , ")) {
311 				if (npaths++ >= MAXPATHS) {
312 					progerr(gettext(ERR_MAXPATHS),
313 					    MAXPATHS);
314 					quit(1);
315 				}
316 			}
317 			break;
318 
319 		default:
320 			usage();
321 			/*NOTREACHED*/
322 			/*
323 			 * Although usage() calls a noreturn function,
324 			 * needed to add return (1);  so that main() would
325 			 * pass compilation checks. The statement below
326 			 * should never be executed.
327 			 */
328 			return (1);
329 		}
330 	}
331 
332 	/* Check for incompatible options */
333 	if (dflag && Rflag)
334 		usage();
335 
336 	/* Check for root dir and device dir if set */
337 	if (Rflag) {
338 		if (!set_inst_root(Rvalue)) {
339 			progerr(gettext(ERR_ROOT_CMD));
340 			quit(1);
341 		}
342 	}
343 
344 	if (dflag)
345 		device = flex_device(dvalue, 1);
346 
347 	if (lflag || Lflag) {
348 		/* we're only supposed to list information */
349 		if ((cflag >= 0) || (aflag >= 0) ||
350 		    qflag || xflag || fflag || nflag || vflag)
351 			usage();
352 	}
353 
354 	set_PKGpaths(get_inst_root());
355 
356 	if (catg_arg != NULL && device == NULL) {
357 		if (argc - optind) {
358 			usage();
359 		}
360 		pkg = gpkglist(pkgdir, all_pkgs, category);
361 		if (pkg == NULL) {
362 			progerr(gettext(ERR_CAT_FND), catg_arg);
363 			quit(1);
364 		} else {
365 			for (pkgcnt = 0; pkg[pkgcnt] != NULL; pkgcnt++)
366 				;
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 				quit(n);
417 			if (catg_arg != NULL)
418 				pkg = gpkglist(spooldir, all_pkgs, category);
419 			else
420 				pkg = gpkglist(spooldir, all_pkgs, NULL);
421 			pkgfmt = 0;
422 		} else {
423 			if (catg_arg != NULL)
424 				pkg = gpkglist(spooldir,
425 				    pkgcnt ? pkg : all_pkgs, category);
426 			else
427 				pkg = gpkglist(spooldir,
428 				    pkgcnt ? pkg : all_pkgs, NULL);
429 			pkgfmt = 1;
430 		}
431 
432 		/*
433 		 * At this point pkg[] is the list of packages to check. They
434 		 * are in directory format in spooldir.
435 		 */
436 		if (pkg == NULL) {
437 			if (catg_arg != NULL) {
438 				progerr(gettext(ERR_CAT_FND), catg_arg);
439 				quit(1);
440 			} else {
441 				progerr(gettext(ERR_SEL_PKG));
442 				quit(1);
443 			}
444 		}
445 
446 		aflag = 0;
447 
448 		for (n = 0; pkg[n]; n++) {
449 			char locenv[PATH_MAX];
450 
451 			if (pkgfmt)
452 				(void) printf(
453 				    gettext(MSG_CHK_DIR), pkg[n], device);
454 			else
455 				(void) printf(
456 				    gettext(MSG_CHK_STRM), pkg[n], device);
457 
458 			(void) snprintf(pkgspool, sizeof (pkgspool),
459 			    "%s/%s", spooldir, pkg[n]);
460 			(void) snprintf(file, sizeof (file),
461 			    "%s/install", pkgspool);
462 			/* Here we check the install scripts. */
463 			(void) printf(
464 			    gettext("## Checking control scripts.\n"));
465 			(void) checkscripts(file, 0);
466 			/* Verify consistency with the pkgmap. */
467 			(void) printf(
468 			    gettext("## Checking package objects.\n"));
469 			(void) snprintf(file, sizeof (file),
470 			    "%s/pkgmap", pkgspool);
471 			(void) snprintf(locenv, sizeof (locenv),
472 			    "%s/pkginfo", pkgspool);
473 			envfile = locenv;
474 
475 			/*
476 			 * NOTE : checkmap() frees the environ data and
477 			 * pointer when it's through with them.
478 			 */
479 			if (checkmap(0, (device != NULL), file, envfile,
480 			    pkg[n], NULL, 0))
481 				errflg++;
482 			(void) printf(
483 			    gettext("## Checking is complete.\n"));
484 		}
485 	} else {
486 		if (envfile)
487 			usage();
488 
489 		put_path_params();	/* Restore what's needed. */
490 
491 		/*
492 		 * If this is a check of a client of some sort, we'll need to
493 		 * mount up the client's filesystems. If the caller isn't
494 		 * root, this may not be possible.
495 		 */
496 		if (is_an_inst_root()) {
497 			if (getuid()) {
498 				logerr(gettext(MSG_NOTROOT));
499 				logerr(gettext(MSG_CONT));
500 			} else {
501 				if (get_mntinfo(map_client, vfstab_file))
502 					map_client = 0;
503 				if (map_client)
504 					mount_client();
505 			}
506 		}
507 
508 		(void) snprintf(file, sizeof (file),
509 		    "%s/contents", get_PKGADM());
510 		if (ppathlist[0] != NULL) {
511 			for (n = 0; ppathlist[n]; n++) {
512 				if (checkmap(1, (device != NULL), file, NULL,
513 				    NULL, ppathlist[n], 1))
514 					errflg++;
515 			}
516 		} else if (pkg[0] != NULL) {
517 				if (checkmap(1, (device != NULL), file, NULL,
518 				    pkg[0], NULL, 0)) {
519 					errflg++;
520 				}
521 		} else {
522 			if (checkmap(1, (device != NULL), file, NULL,
523 			    NULL, NULL, 0)) {
524 				errflg++;
525 			}
526 		}
527 
528 		if (map_client) {
529 			unmount_client();
530 		}
531 	}
532 	quit(errflg ? 1 : 0);
533 	/* LINTED: no return */
534 }
535 
536 static void
537 setpathlist(char *file)
538 {
539 	int fd;
540 	struct stat st;
541 	FILE *fplist;
542 	char pathname[PATH_MAX];
543 	/*
544 	 * This trap laid to catch a mismatch between the declaration above and
545 	 * the hard-coded constant in the fscanf below
546 	 */
547 #if PATH_MAX != 1024
548 #error "PATH_MAX changed, so we have a bug to fix"
549 #endif
550 
551 	if (strcmp(file, "-") == 0) {
552 		fplist = stdin;
553 	} else {
554 		if ((fd = open(file, O_RDONLY)) == -1) {
555 			progerr(gettext(ERR_IOPEN), file);
556 			quit(1);
557 		}
558 		if (fstat(fd, &st) == -1) {
559 			progerr(gettext(ERR_IOPEN), file);
560 			quit(1);
561 		}
562 		if (S_ISDIR(st.st_mode) || S_ISBLK(st.st_mode)) {
563 			progerr(gettext(ERR_PATHS_INVALID), file);
564 			quit(1);
565 		}
566 		if ((fplist = fdopen(fd, "r")) == NULL) {
567 			progerr(gettext(ERR_IOPEN), file);
568 			quit(1);
569 		}
570 	}
571 	while (fscanf(fplist, "%1024s", pathname) == 1) {
572 		if (*pathname == '\0') {
573 			progerr(gettext(ERR_PATHS_INVALID), file);
574 			quit(1);
575 		}
576 		pathlist[npaths] = qstrdup(pathname);
577 		if (npaths++ > MAXPATHS) {
578 			progerr(gettext(ERR_TOO_MANY), MAXPATHS);
579 			quit(1);
580 		}
581 	}
582 	if (npaths == 0) {
583 		progerr(gettext(ERR_IEMPTY));
584 		quit(1);
585 	}
586 	(void) fclose(fplist);
587 }
588 
589 void
590 quit(int n)
591 {
592 	/* cleanup any temporary directories */
593 	(void) chdir("/");
594 	if (tmpdir != NULL) {
595 		(void) rrmdir(tmpdir);
596 		free(tmpdir);
597 		tmpdir = NULL;
598 	}
599 	(void) pkghead(NULL);
600 	exit(n);
601 	/*NOTREACHED*/
602 }
603 
604 static void
605 usage(void)
606 {
607 	char *prog = get_prog_name();
608 
609 	(void) fprintf(stderr, gettext(ERR_USAGE), prog, prog);
610 	quit(1);
611 	/*NOTREACHED*/
612 }
613