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