xref: /illumos-gate/usr/src/cmd/svr4pkg/installf/main.c (revision cb7ea99db394f3bd5f4a6c6bf58c8c52df3508f0)
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		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 #ifdef	ALLOW_EXCEPTION_PKG_LIST
361 	/*
362 	 * *********************************************************************
363 	 * this feature is removed starting with Solaris 10 - there is no built
364 	 * in list of packages that should be run "the old way"
365 	 * *********************************************************************
366 	 */
367 	/* Until 2.9, set it from the execption list */
368 	if (pkginst && exception_pkg(pkginst, LINK))
369 		set_nonABI_symlinks();
370 #endif
371 	/*
372 	 * This maps the client filesystems into the server's space.
373 	 */
374 	if (map_client && !mount_client())
375 		logerr(gettext(MSG_MANMOUNT));
376 
377 	/* open the package database (contents) file */
378 
379 	if (!ocfile(&pkgserver, &cfTmpVfp, 0L)) {
380 		quit(1);
381 	}
382 
383 	if (fflag) {
384 		dbchg = dofinal(pkgserver, cfTmpVfp, REMOVEF, classname, prog);
385 	} else {
386 		if (INSTALF) {
387 			dbst = INST_RDY;
388 			if (installf(argc-optind, &argv[optind]))
389 				quit(1);
390 		} else {
391 			dbst = RM_RDY;
392 			removef(argc-optind, &argv[optind]);
393 		}
394 
395 		dbchg = pkgdbmerg(pkgserver, cfTmpVfp, extlist);
396 		if (dbchg < 0) {
397 			progerr(gettext(ERR_MERG));
398 			quit(99);
399 		}
400 	}
401 
402 	if (dbchg) {
403 		if ((n = swapcfile(pkgserver, &cfTmpVfp, pkginst, 1))
404 		    == RESULT_WRN) {
405 			warnflag++;
406 		} else if (n == RESULT_ERR) {
407 			quit(99);
408 		}
409 	}
410 
411 	relslock();
412 
413 	if (REMOVEF && !fflag) {
414 		for (n = 0; extlist[n]; n++) {
415 			ept = &(extlist[n]->cf_ent);
416 
417 			/* Skip duplicated paths */
418 			if ((n > 0) && (strncmp(ept->path,
419 			    extlist[n-1]->cf_ent.path, PATH_MAX) == 0)) {
420 				continue;
421 			}
422 
423 			if (!extlist[n]->mstat.shared) {
424 				/*
425 				 * Only output paths that can be deleted.
426 				 * so need to skip if the object is owned
427 				 * by a remote server and removal is not
428 				 * being forced.
429 				 */
430 				if (ept->pinfo &&
431 				    (ept->pinfo->status == SERVED_FILE) &&
432 				    !pkgrmremote)
433 					continue;
434 
435 				c = 0;
436 				if (is_a_cl_basedir() && !is_an_inst_root()) {
437 					c = strlen(get_client_basedir());
438 					(void) snprintf(outbuf, sizeof (outbuf),
439 					    "%s/%s\n", get_basedir(),
440 					    &(ept->path[c]));
441 				} else if (is_an_inst_root()) {
442 					(void) snprintf(outbuf, sizeof (outbuf),
443 					    "%s/%s\n", get_inst_root(),
444 					    &(ept->path[c]));
445 				} else {
446 					(void) snprintf(outbuf, sizeof (outbuf),
447 					    "%s\n", &(ept->path[c]));
448 				}
449 				canonize(outbuf);
450 				(void) printf("%s", outbuf);
451 			}
452 		}
453 	} else if (INSTALF && !fflag) {
454 		for (n = 0; extlist[n]; n++) {
455 			ept = &(extlist[n]->cf_ent);
456 
457 			if (strchr("dxcbp", ept->ftype)) {
458 				tp = fixpath(ept->path);
459 				(void) averify(1, &ept->ftype, tp, &ept->ainfo);
460 			}
461 		}
462 	}
463 
464 	pkgcloseserver(pkgserver);
465 
466 	z_destroyMountTable();
467 
468 	quit(warnflag ? 1 : 0);
469 	/* LINTED: no return */
470 }
471 
472 void
473 quit(int n)
474 {
475 	char *prog = get_prog_name();
476 
477 	unmount_client();
478 
479 	if (ulim && script) {
480 		if (REMOVEF) {
481 			set_ulimit(script, gettext(ERR_R_FAIL));
482 		} else {
483 			set_ulimit(script, gettext(ERR_I_FAIL));
484 		}
485 	}
486 
487 	exit(n);
488 }
489 
490 void
491 usage(void)
492 {
493 	char *prog = get_prog_name();
494 
495 	if (REMOVEF) {
496 		(void) fprintf(stderr, gettext(ERR_USAGE0), prog, prog);
497 	} else {
498 		(void) fprintf(stderr, gettext(ERR_USAGE1), prog, prog, prog);
499 	}
500 	exit(1);
501 }
502