xref: /illumos-gate/usr/src/cmd/svr4pkg/installf/main.c (revision 5c51f124)
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 <fcntl.h>
33 #include <ctype.h>
34 #include <errno.h>
35 #include <string.h>
36 #include <signal.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <pkginfo.h>
40 #include <pkgstrct.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 <libadm.h>
48 #include <libinst.h>
49 #include "installf.h"
50 
51 #define	BASEDIR	"/BASEDIR/"
52 
53 #define	INSTALF	(*prog == 'i')
54 #define	REMOVEF	(*prog == 'r')
55 
56 #define	MSG_MANMOUNT	"Assuming mounts were provided."
57 
58 #define	ERR_PKGNAME_TOO_LONG	\
59 "The package name specified on the command line\n" \
60 "exceeds the maximum package name length: a package name may contain a\n" \
61 "maximum of <%d> characters; however, the package name specified on\n" \
62 "the command line contains <%d> characters, which exceeds the maximum\n" \
63 "package name length by <%d> characters. Please specify a package name\n" \
64 "that contains no more than <%d> characters."
65 
66 #define	ERR_DB_GET "unable to retrieve entries from the database."
67 #define	ERR_DB_PUT "unable to update the package database."
68 #define	ERR_ROOT_SET	"Could not set install root from the environment."
69 #define	ERR_ROOT_CMD	"Command line install root contends with environment."
70 #define	ERR_CLASSLONG	"classname argument too long"
71 #define	ERR_CLASSCHAR	"bad character in classname"
72 #define	ERR_INVAL	"package instance <%s> is invalid"
73 #define	ERR_NOTINST	"package instance <%s> is not installed"
74 #define	ERR_MERG	"unable to merge contents file"
75 #define	ERR_SORT	"unable to sort contents file"
76 #define	ERR_I_FAIL	"installf did not complete successfully"
77 #define	ERR_R_FAIL	"removef did not complete successfully"
78 #define	ERR_NOTROOT	"You must be \"root\" for %s to execute properly."
79 #define	ERR_USAGE0	"usage:\n" \
80 	"\t%s [[-M|-A] -R host_path] [-V ...] pkginst path " \
81 	"[path ...]\n" \
82 	"\t%s [[-M|-A] -R host_path] [-V ...] pkginst path\n"
83 
84 #define	ERR_USAGE1	"usage:\n" \
85 	"\t%s [[-M] -R host_path] [-V ...] [-c class] <pkginst> " \
86 	"<path>\n" \
87 	"\t%s [[-M] -R host_path] [-V ...] [-c class] <pkginst> " \
88 	"<path> <specs>\n" \
89 	"\t   where <specs> may be defined as:\n" \
90 	"\t\tf <mode> <owner> <group>\n" \
91 	"\t\tv <mode> <owner> <group>\n" \
92 	"\t\te <mode> <owner> <group>\n" \
93 	"\t\td <mode> <owner> <group>\n" \
94 	"\t\tx <mode> <owner> <group>\n" \
95 	"\t\tp <mode> <owner> <group>\n" \
96 	"\t\tc <major> <minor> <mode> <owner> <group>\n" \
97 	"\t\tb <major> <minor> <mode> <owner> <group>\n" \
98 	"\t\ts <path>=<srcpath>\n" \
99 	"\t\tl <path>=<srcpath>\n" \
100 	"\t%s [[-M] -R host_path] [-V ...] [-c class] -f pkginst\n"
101 
102 #define	CMD_SORT	"sort +0 -1"
103 
104 #define	LINK	1
105 
106 extern char	dbst; 			/* libinst/pkgdbmerg.c */
107 
108 struct cfextra **extlist;
109 struct pinfo **eptlist;
110 
111 char	*classname = NULL;
112 char	*pkginst;
113 char	*uniTmp;
114 char 	*abi_sym_ptr;
115 char 	*ulim;
116 char 	*script;
117 
118 int	eptnum;
119 int	sortflag;
120 int	nosetuid;
121 int	nocnflct;
122 int	warnflag = 0;
123 
124 /* libadm/pkgparam.c */
125 extern void	set_PKGADM(char *newpath);
126 extern void	set_PKGLOC(char *newpath);
127 
128 extern void set_limit(void);
129 
130 int
131 main(int argc, char **argv)
132 {
133 	FILE		*pp;
134 	VFP_T		*cfTmpVfp;
135 	VFP_T		*cfVfp;
136 	char		*cmd;
137 	char		*tp;
138 	char		*prog;
139 	char		*pt;
140 	char		*vfstab_file = NULL;
141 	char		line[1024];
142 	char		outbuf[PATH_MAX];
143 	int		c;
144 	int		dbchg;
145 	int		err;
146 	int		fflag = 0;
147 	int		map_client = 1;
148 	int		n;
149 	int		pkgrmremote = 0;	/* don't remove remote files */
150 	struct cfent	*ept;
151 
152 	/* hookup signals */
153 
154 	(void) signal(SIGHUP, exit);
155 	(void) signal(SIGINT, exit);
156 	(void) signal(SIGQUIT, exit);
157 
158 	/* initialize locale mechanism */
159 
160 	(void) setlocale(LC_ALL, "");
161 
162 #if	!defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
163 #define	TEXT_DOMAIN "SYS_TEST"
164 #endif	/* !defined(TEXT_DOMAIN) */
165 
166 	(void) textdomain(TEXT_DOMAIN);
167 
168 	/* determine program name */
169 
170 	prog = set_prog_name(argv[0]);
171 
172 	/* tell instzones interface how to access package output functions */
173 
174 	z_set_output_functions(echo, echoDebug, progerr);
175 
176 	/* only allow root to run this program */
177 
178 	if (getuid() != 0) {
179 		progerr(gettext(ERR_NOTROOT), prog);
180 		exit(1);
181 	}
182 
183 	ulim = getenv("PKG_ULIMIT");
184 	script = getenv("PKG_PROC_SCRIPT");
185 
186 	if (ulim && script) {
187 		set_limit();
188 		clr_ulimit();
189 	}
190 
191 	/* bug id 4244631, not ABI compliant */
192 	abi_sym_ptr = getenv("PKG_NONABI_SYMLINKS");
193 	if (abi_sym_ptr && strncasecmp(abi_sym_ptr, "TRUE", 4) == 0)
194 		set_nonABI_symlinks();
195 
196 	/* bugId 4012147 */
197 	if ((uniTmp = getenv("PKG_NO_UNIFIED")) != NULL)
198 		map_client = 0;
199 	if (!set_inst_root(getenv("PKG_INSTALL_ROOT"))) {
200 		progerr(gettext(ERR_ROOT_SET));
201 		exit(1);
202 	}
203 
204 	while ((c = getopt(argc, argv, "c:V:fAMR:?")) != EOF) {
205 		switch (c) {
206 			case 'f':
207 			fflag++;
208 			break;
209 
210 			case 'c':
211 			classname = optarg;
212 			/* validate that classname is acceptable */
213 			if (strlen(classname) > (size_t)CLSSIZ) {
214 				progerr(gettext(ERR_CLASSLONG));
215 				exit(1);
216 			}
217 			for (pt = classname; *pt; pt++) {
218 				if (!isalpha(*pt) && !isdigit(*pt)) {
219 					progerr(gettext(ERR_CLASSCHAR));
220 					exit(1);
221 				}
222 			}
223 			break;
224 
225 		/*
226 		 * Don't map the client filesystem onto the server's. Assume
227 		 * the mounts have been made for us.
228 		 */
229 			case 'M':
230 			map_client = 0;
231 			break;
232 
233 		/*
234 		 * Allow admin to establish the client filesystem using a
235 		 * vfstab-like file of stable format.
236 		 */
237 			case 'V':
238 			vfstab_file = flex_device(optarg, 2);
239 			map_client = 1;
240 			break;
241 
242 			case 'A':
243 			pkgrmremote++;
244 			break;
245 
246 			case 'R':	/* added for newroot option */
247 			if (!set_inst_root(optarg)) {
248 				progerr(gettext(ERR_ROOT_CMD));
249 				exit(1);
250 			}
251 			break;
252 
253 			default:
254 			usage();
255 			/*NOTREACHED*/
256 			/*
257 			 * Although usage() calls a noreturn function,
258 			 * needed to add return (1);  so that main() would
259 			 * pass compilation checks. The statement below
260 			 * should never be executed.
261 			 */
262 			return (1);
263 		}
264 	}
265 
266 	if (pkgrmremote && (!is_an_inst_root() || fflag || INSTALF)) {
267 		usage();
268 		/*NOTREACHED*/
269 	}
270 
271 	/*
272 	 * Get the mount table info and store internally.
273 	 */
274 	if (get_mntinfo(map_client, vfstab_file))
275 		exit(1);
276 
277 	/*
278 	 * This function defines the standard /var/... directories used later
279 	 * to construct the paths to the various databases.
280 	 */
281 	(void) set_PKGpaths(get_inst_root());
282 
283 	/*
284 	 * If this is being installed on a client whose /var filesystem is
285 	 * mounted in some odd way, remap the administrative paths to the
286 	 * real filesystem. This could be avoided by simply mounting up the
287 	 * client now; but we aren't yet to the point in the process where
288 	 * modification of the filesystem is permitted.
289 	 */
290 	if (is_an_inst_root()) {
291 		int fsys_value;
292 
293 		fsys_value = fsys(get_PKGLOC());
294 		if (use_srvr_map_n(fsys_value))
295 			set_PKGLOC(server_map(get_PKGLOC(), fsys_value));
296 
297 		fsys_value = fsys(get_PKGADM());
298 		if (use_srvr_map_n(fsys_value))
299 			set_PKGADM(server_map(get_PKGADM(), fsys_value));
300 	}
301 
302 	sortflag = 0;
303 
304 	/*
305 	 * get the package name and verify length is not too long
306 	 */
307 
308 	pkginst = argv[optind++];
309 	if (pkginst == NULL) {
310 		usage();
311 		/*NOTREACHED*/
312 
313 	}
314 
315 	n = strlen(pkginst);
316 	if (n > PKGSIZ) {
317 		progerr(gettext(ERR_PKGNAME_TOO_LONG), PKGSIZ, n, n-PKGSIZ,
318 		    PKGSIZ);
319 		usage();
320 		/*NOTREACHED*/
321 	}
322 
323 	/*
324 	 * The following is used to setup the environment. Note that the
325 	 * variable 'BASEDIR' is only meaningful for this utility if there
326 	 * is an install root, recorded in PKG_INSTALL_ROOT. Otherwise, this
327 	 * utility can create a file or directory anywhere unfettered by
328 	 * the basedir associated with the package instance.
329 	 */
330 	if ((err = set_basedirs(0, NULL, pkginst, 1)) != 0)
331 		exit(err);
332 
333 	if (INSTALF)
334 		mkbasedir(0, get_basedir());
335 
336 	if (fflag) {
337 		/* installf and removef must only have pkginst */
338 		if (optind != argc) {
339 			usage();
340 			/*NOTREACHED*/
341 		}
342 	} else {
343 		/*
344 		 * installf and removef must have at minimum
345 		 * pkginst & pathname specified on command line
346 		 */
347 		if (optind >= argc) {
348 			usage();
349 			/*NOTREACHED*/
350 		}
351 	}
352 	if (REMOVEF) {
353 		if (classname) {
354 			usage();
355 		}
356 	}
357 	if (pkgnmchk(pkginst, "all", 0)) {
358 		progerr(gettext(ERR_INVAL), pkginst);
359 		exit(1);
360 	}
361 	if (fpkginst(pkginst, NULL, NULL) == NULL) {
362 		progerr(gettext(ERR_NOTINST), pkginst);
363 		exit(1);
364 	}
365 
366 #ifdef	ALLOW_EXCEPTION_PKG_LIST
367 	/*
368 	 * *********************************************************************
369 	 * this feature is removed starting with Solaris 10 - there is no built
370 	 * in list of packages that should be run "the old way"
371 	 * *********************************************************************
372 	 */
373 	/* Until 2.9, set it from the execption list */
374 	if (pkginst && exception_pkg(pkginst, LINK))
375 		set_nonABI_symlinks();
376 #endif
377 	/*
378 	 * This maps the client filesystems into the server's space.
379 	 */
380 	if (map_client && !mount_client())
381 		logerr(gettext(MSG_MANMOUNT));
382 
383 	/* open the package database (contents) file */
384 
385 	if (!ocfile(&cfVfp, &cfTmpVfp, 0L)) {
386 		quit(1);
387 	}
388 
389 	if (fflag) {
390 		dbchg = dofinal(cfVfp, cfTmpVfp, REMOVEF, classname, prog);
391 	} else {
392 		if (INSTALF) {
393 			dbst = INST_RDY;
394 			if (installf(argc-optind, &argv[optind]))
395 				quit(1);
396 		} else {
397 			dbst = RM_RDY;
398 			removef(argc-optind, &argv[optind]);
399 		}
400 
401 		dbchg = pkgdbmerg(cfVfp, cfTmpVfp, extlist, 0);
402 		if (dbchg < 0) {
403 			progerr(gettext(ERR_MERG));
404 			quit(99);
405 		}
406 	}
407 
408 	if (dbchg) {
409 		if ((n = swapcfile(&cfVfp, &cfTmpVfp, pkginst, 1))
410 			== RESULT_WRN) {
411 		    warnflag++;
412 		} else if (n == RESULT_ERR) {
413 		    quit(99);
414 		}
415 	}
416 
417 	relslock();
418 
419 	if (REMOVEF && !fflag) {
420 		for (n = 0; extlist[n]; n++) {
421 			ept = &(extlist[n]->cf_ent);
422 
423 			/* Skip duplicated paths */
424 			if ((n > 0) && (strncmp(ept->path,
425 			    extlist[n-1]->cf_ent.path, PATH_MAX) == 0)) {
426 				continue;
427 			}
428 
429 			if (!extlist[n]->mstat.shared) {
430 				/*
431 				 * Only output paths that can be deleted.
432 				 * so need to skip if the object is owned
433 				 * by a remote server and removal is not
434 				 * being forced.
435 				 */
436 				if (ept->pinfo &&
437 				    (ept->pinfo->status == SERVED_FILE) &&
438 				    !pkgrmremote)
439 					continue;
440 
441 				c = 0;
442 				if (is_a_cl_basedir() && !is_an_inst_root()) {
443 					c = strlen(get_client_basedir());
444 					(void) snprintf(outbuf, sizeof (outbuf),
445 						"%s/%s\n", get_basedir(),
446 						&(ept->path[c]));
447 				} else if (is_an_inst_root()) {
448 					(void) snprintf(outbuf, sizeof (outbuf),
449 						"%s/%s\n", get_inst_root(),
450 						&(ept->path[c]));
451 				} else {
452 					(void) snprintf(outbuf, sizeof (outbuf),
453 						"%s\n", &(ept->path[c]));
454 				}
455 				canonize(outbuf);
456 				(void) printf("%s", outbuf);
457 			}
458 		}
459 	} else if (INSTALF && !fflag) {
460 		for (n = 0; extlist[n]; n++) {
461 			ept = &(extlist[n]->cf_ent);
462 
463 			if (strchr("dxcbp", ept->ftype)) {
464 				tp = fixpath(ept->path);
465 				(void) averify(1, &ept->ftype,
466 					tp, &ept->ainfo);
467 			}
468 		}
469 	}
470 
471 	/* Sort the contents files if needed */
472 	if (sortflag) {
473 		int n;
474 
475 		warnflag += (ocfile(&cfVfp, &cfTmpVfp, 0L)) ? 0 : 1;
476 		if (!warnflag) {
477 			size_t	len;
478 
479 			len = strlen(CMD_SORT) + strlen(get_PKGADM()) +
480 				strlen("/contents") + 5;
481 			cmd = (char *)malloc(len);
482 			(void) snprintf(cmd, len, "%s %s/contents",
483 				CMD_SORT, get_PKGADM());
484 			pp = popen(cmd, "r");
485 			if (pp == NULL) {
486 				(void) vfpClose(&cfVfp);
487 				(void) vfpClose(&cfTmpVfp);
488 				free(cmd);
489 				progerr(gettext(ERR_SORT));
490 				quit(1);
491 			}
492 			while (fgets(line, 1024, pp) != NULL) {
493 				if (line[0] != DUP_ENTRY) {
494 					vfpPuts(cfTmpVfp, line);
495 				}
496 			}
497 			free(cmd);
498 			(void) pclose(pp);
499 			n = swapcfile(&cfVfp, &cfTmpVfp, pkginst, 1);
500 			if (n == RESULT_WRN) {
501 				warnflag++;
502 			} else if (n == RESULT_ERR) {
503 				quit(99);
504 			}
505 
506 			relslock();	/* Unlock the database. */
507 		}
508 	}
509 
510 	z_destroyMountTable();
511 
512 	quit(warnflag ? 1 : 0);
513 	/* LINTED: no return */
514 }
515 
516 void
517 quit(int n)
518 {
519 	char *prog = get_prog_name();
520 
521 	unmount_client();
522 
523 	if (ulim && script) {
524 		if (REMOVEF) {
525 			set_ulimit(script, gettext(ERR_R_FAIL));
526 		} else {
527 			set_ulimit(script, gettext(ERR_I_FAIL));
528 		}
529 	}
530 
531 	exit(n);
532 }
533 
534 void
535 usage(void)
536 {
537 	char *prog = get_prog_name();
538 
539 	if (REMOVEF) {
540 		(void) fprintf(stderr, gettext(ERR_USAGE0), prog, prog);
541 	} else {
542 		(void) fprintf(stderr, gettext(ERR_USAGE1), prog, prog, prog);
543 	}
544 	exit(1);
545 }
546