xref: /illumos-gate/usr/src/cmd/svr4pkg/installf/main.c (revision 9b2055cc)
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	nosetuid;
120 int	nocnflct;
121 int	warnflag = 0;
122 
123 /* libadm/pkgparam.c */
124 extern void	set_PKGADM(char *newpath);
125 extern void	set_PKGLOC(char *newpath);
126 
127 extern void set_limit(void);
128 
129 int
130 main(int argc, char **argv)
131 {
132 	VFP_T		*cfTmpVfp;
133 	PKGserver	pkgserver = NULL;
134 	char		*tp;
135 	char		*prog;
136 	char		*pt;
137 	char		*vfstab_file = NULL;
138 	char		*temp_cl_basedir;
139 	char		outbuf[PATH_MAX];
140 	int		c;
141 	int		dbchg;
142 	int		err;
143 	int		fflag = 0;
144 	int		map_client = 1;
145 	int		n;
146 	int		pkgrmremote = 0;	/* don't remove remote files */
147 	struct cfent	*ept;
148 
149 	/* hookup signals */
150 
151 	(void) signal(SIGHUP, exit);
152 	(void) signal(SIGINT, exit);
153 	(void) signal(SIGQUIT, exit);
154 
155 	/* initialize locale mechanism */
156 
157 	(void) setlocale(LC_ALL, "");
158 
159 #if	!defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
160 #define	TEXT_DOMAIN "SYS_TEST"
161 #endif	/* !defined(TEXT_DOMAIN) */
162 
163 	(void) textdomain(TEXT_DOMAIN);
164 
165 	/* determine program name */
166 
167 	prog = set_prog_name(argv[0]);
168 
169 	/* tell instzones interface how to access package output functions */
170 
171 	z_set_output_functions(echo, echoDebug, progerr);
172 
173 	/* only allow root to run this program */
174 
175 	if (getuid() != 0) {
176 		progerr(gettext(ERR_NOTROOT), prog);
177 		exit(1);
178 	}
179 
180 	ulim = getenv("PKG_ULIMIT");
181 	script = getenv("PKG_PROC_SCRIPT");
182 
183 	if (ulim && script) {
184 		set_limit();
185 		clr_ulimit();
186 	}
187 
188 	/* bug id 4244631, not ABI compliant */
189 	abi_sym_ptr = getenv("PKG_NONABI_SYMLINKS");
190 	if (abi_sym_ptr && strncasecmp(abi_sym_ptr, "TRUE", 4) == 0)
191 		set_nonABI_symlinks();
192 
193 	/* bugId 4012147 */
194 	if ((uniTmp = getenv("PKG_NO_UNIFIED")) != NULL)
195 		map_client = 0;
196 	if (!set_inst_root(getenv("PKG_INSTALL_ROOT"))) {
197 		progerr(gettext(ERR_ROOT_SET));
198 		exit(1);
199 	}
200 
201 	while ((c = getopt(argc, argv, "c:V:fAMR:?")) != EOF) {
202 		switch (c) {
203 			case 'f':
204 			fflag++;
205 			break;
206 
207 			case 'c':
208 			classname = optarg;
209 			/* validate that classname is acceptable */
210 			if (strlen(classname) > (size_t)CLSSIZ) {
211 				progerr(gettext(ERR_CLASSLONG));
212 				exit(1);
213 			}
214 			for (pt = classname; *pt; pt++) {
215 				if (!isalpha(*pt) && !isdigit(*pt)) {
216 					progerr(gettext(ERR_CLASSCHAR));
217 					exit(1);
218 				}
219 			}
220 			break;
221 
222 		/*
223 		 * Don't map the client filesystem onto the server's. Assume
224 		 * the mounts have been made for us.
225 		 */
226 			case 'M':
227 			map_client = 0;
228 			break;
229 
230 		/*
231 		 * Allow admin to establish the client filesystem using a
232 		 * vfstab-like file of stable format.
233 		 */
234 			case 'V':
235 			vfstab_file = flex_device(optarg, 2);
236 			map_client = 1;
237 			break;
238 
239 			case 'A':
240 			pkgrmremote++;
241 			break;
242 
243 			case 'R':	/* added for newroot option */
244 			if (!set_inst_root(optarg)) {
245 				progerr(gettext(ERR_ROOT_CMD));
246 				exit(1);
247 			}
248 			break;
249 
250 			default:
251 			usage();
252 			/*NOTREACHED*/
253 			/*
254 			 * Although usage() calls a noreturn function,
255 			 * needed to add return (1);  so that main() would
256 			 * pass compilation checks. The statement below
257 			 * should never be executed.
258 			 */
259 			return (1);
260 		}
261 	}
262 
263 	if (pkgrmremote && (!is_an_inst_root() || fflag || INSTALF)) {
264 		usage();
265 		/*NOTREACHED*/
266 	}
267 
268 	/*
269 	 * Get the mount table info and store internally.
270 	 */
271 	if (get_mntinfo(map_client, vfstab_file))
272 		exit(1);
273 
274 	/*
275 	 * This function defines the standard /var/... directories used later
276 	 * to construct the paths to the various databases.
277 	 */
278 	(void) set_PKGpaths(get_inst_root());
279 
280 	/*
281 	 * If this is being installed on a client whose /var filesystem is
282 	 * mounted in some odd way, remap the administrative paths to the
283 	 * real filesystem. This could be avoided by simply mounting up the
284 	 * client now; but we aren't yet to the point in the process where
285 	 * modification of the filesystem is permitted.
286 	 */
287 	if (is_an_inst_root()) {
288 		int fsys_value;
289 
290 		fsys_value = fsys(get_PKGLOC());
291 		if (use_srvr_map_n(fsys_value))
292 			set_PKGLOC(server_map(get_PKGLOC(), fsys_value));
293 
294 		fsys_value = fsys(get_PKGADM());
295 		if (use_srvr_map_n(fsys_value))
296 			set_PKGADM(server_map(get_PKGADM(), fsys_value));
297 	}
298 
299 	/*
300 	 * get the package name and verify length is not too long
301 	 */
302 
303 	pkginst = argv[optind++];
304 	if (pkginst == NULL) {
305 		usage();
306 		/*NOTREACHED*/
307 
308 	}
309 
310 	n = strlen(pkginst);
311 	if (n > PKGSIZ) {
312 		progerr(gettext(ERR_PKGNAME_TOO_LONG), PKGSIZ, n, n-PKGSIZ,
313 		    PKGSIZ);
314 		usage();
315 		/*NOTREACHED*/
316 	}
317 
318 	/*
319 	 * The following is used to setup the environment. Note that the
320 	 * variable 'BASEDIR' is only meaningful for this utility if there
321 	 * is an install root, recorded in PKG_INSTALL_ROOT. Otherwise, this
322 	 * utility can create a file or directory anywhere unfettered by
323 	 * the basedir associated with the package instance.
324 	 */
325 	if ((err = set_basedirs(0, NULL, pkginst, 1)) != 0)
326 		exit(err);
327 
328 	if (INSTALF)
329 		mkbasedir(0, get_basedir());
330 
331 	if (fflag) {
332 		/* installf and removef must only have pkginst */
333 		if (optind != argc) {
334 			usage();
335 			/*NOTREACHED*/
336 		}
337 	} else {
338 		/*
339 		 * installf and removef must have at minimum
340 		 * pkginst & pathname specified on command line
341 		 */
342 		if (optind >= argc) {
343 			usage();
344 			/*NOTREACHED*/
345 		}
346 	}
347 	if (REMOVEF) {
348 		if (classname) {
349 			usage();
350 		}
351 	}
352 	if (pkgnmchk(pkginst, "all", 0)) {
353 		progerr(gettext(ERR_INVAL), pkginst);
354 		exit(1);
355 	}
356 	if (fpkginst(pkginst, NULL, NULL) == NULL) {
357 		progerr(gettext(ERR_NOTINST), pkginst);
358 		exit(1);
359 	}
360 
361 #ifdef	ALLOW_EXCEPTION_PKG_LIST
362 	/*
363 	 * *********************************************************************
364 	 * this feature is removed starting with Solaris 10 - there is no built
365 	 * in list of packages that should be run "the old way"
366 	 * *********************************************************************
367 	 */
368 	/* Until 2.9, set it from the execption list */
369 	if (pkginst && exception_pkg(pkginst, LINK))
370 		set_nonABI_symlinks();
371 #endif
372 	/*
373 	 * This maps the client filesystems into the server's space.
374 	 */
375 	if (map_client && !mount_client())
376 		logerr(gettext(MSG_MANMOUNT));
377 
378 	/* open the package database (contents) file */
379 
380 	if (!ocfile(&pkgserver, &cfTmpVfp, 0L)) {
381 		quit(1);
382 	}
383 
384 	if (fflag) {
385 		dbchg = dofinal(pkgserver, cfTmpVfp, REMOVEF, classname, prog);
386 	} else {
387 		if (INSTALF) {
388 			dbst = INST_RDY;
389 			if (installf(argc-optind, &argv[optind]))
390 				quit(1);
391 		} else {
392 			dbst = RM_RDY;
393 			removef(argc-optind, &argv[optind]);
394 		}
395 
396 		dbchg = pkgdbmerg(pkgserver, cfTmpVfp, extlist);
397 		if (dbchg < 0) {
398 			progerr(gettext(ERR_MERG));
399 			quit(99);
400 		}
401 	}
402 
403 	if (dbchg) {
404 		if ((n = swapcfile(pkgserver, &cfTmpVfp, pkginst, 1))
405 		    == RESULT_WRN) {
406 			warnflag++;
407 		} else if (n == RESULT_ERR) {
408 			quit(99);
409 		}
410 	}
411 
412 	relslock();
413 
414 	if (REMOVEF && !fflag) {
415 		for (n = 0; extlist[n]; n++) {
416 			ept = &(extlist[n]->cf_ent);
417 
418 			/* Skip duplicated paths */
419 			if ((n > 0) && (strncmp(ept->path,
420 			    extlist[n-1]->cf_ent.path, PATH_MAX) == 0)) {
421 				continue;
422 			}
423 
424 			if (!extlist[n]->mstat.shared) {
425 				/*
426 				 * Only output paths that can be deleted.
427 				 * so need to skip if the object is owned
428 				 * by a remote server and removal is not
429 				 * being forced.
430 				 */
431 				if (ept->pinfo &&
432 				    (ept->pinfo->status == SERVED_FILE) &&
433 				    !pkgrmremote)
434 					continue;
435 
436 				c = 0;
437 				if (is_a_cl_basedir() && !is_an_inst_root()) {
438 					/*
439 					 * A path in contents db might have
440 					 * other prefix than BASEDIR of the
441 					 * package
442 					 */
443 					temp_cl_basedir = get_client_basedir();
444 					if (strncmp(ept->path, temp_cl_basedir,
445 					    strlen(temp_cl_basedir)) == 0) {
446 						c = strlen(temp_cl_basedir);
447 						(void) snprintf(outbuf,
448 						    sizeof (outbuf), "%s/%s\n",
449 						    get_basedir(),
450 						    &(ept->path[c]));
451 					} else {
452 						(void) snprintf(outbuf,
453 						    sizeof (outbuf),
454 						    "%s\n", &(ept->path[c]));
455 					}
456 				} else if (is_an_inst_root()) {
457 					(void) snprintf(outbuf, sizeof (outbuf),
458 					    "%s/%s\n", get_inst_root(),
459 					    &(ept->path[c]));
460 				} else {
461 					(void) snprintf(outbuf, sizeof (outbuf),
462 					    "%s\n", &(ept->path[c]));
463 				}
464 				canonize(outbuf);
465 				(void) printf("%s", outbuf);
466 			}
467 		}
468 	} else if (INSTALF && !fflag) {
469 		for (n = 0; extlist[n]; n++) {
470 			ept = &(extlist[n]->cf_ent);
471 
472 			if (strchr("dxcbp", ept->ftype)) {
473 				tp = fixpath(ept->path);
474 				(void) averify(1, &ept->ftype, tp, &ept->ainfo);
475 			}
476 		}
477 	}
478 
479 	pkgcloseserver(pkgserver);
480 
481 	z_destroyMountTable();
482 
483 	quit(warnflag ? 1 : 0);
484 	/* LINTED: no return */
485 }
486 
487 void
488 quit(int n)
489 {
490 	char *prog = get_prog_name();
491 
492 	unmount_client();
493 
494 	if (ulim && script) {
495 		if (REMOVEF) {
496 			set_ulimit(script, gettext(ERR_R_FAIL));
497 		} else {
498 			set_ulimit(script, gettext(ERR_I_FAIL));
499 		}
500 	}
501 
502 	exit(n);
503 }
504 
505 void
506 usage(void)
507 {
508 	char *prog = get_prog_name();
509 
510 	if (REMOVEF) {
511 		(void) fprintf(stderr, gettext(ERR_USAGE0), prog, prog);
512 	} else {
513 		(void) fprintf(stderr, gettext(ERR_USAGE1), prog, prog, prog);
514 	}
515 	exit(1);
516 }
517