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
106extern char	dbst; 			/* libinst/pkgdbmerg.c */
107
108struct cfextra **extlist;
109struct pinfo **eptlist;
110
111char	*classname = NULL;
112char	*pkginst;
113char	*uniTmp;
114char 	*abi_sym_ptr;
115char 	*ulim;
116char 	*script;
117
118int	eptnum;
119int	nosetuid;
120int	nocnflct;
121int	warnflag = 0;
122
123/* libadm/pkgparam.c */
124extern void	set_PKGADM(char *newpath);
125extern void	set_PKGLOC(char *newpath);
126
127extern void set_limit(void);
128
129int
130main(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
487void
488quit(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
505void
506usage(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