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