xref: /illumos-gate/usr/src/cmd/svr4pkg/pkginstall/merginfo.c (revision 93a18d6d401e844455263f926578e9d2aa6b47ec)
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 2007 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 <string.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <dirent.h>
36 #include <locale.h>
37 #include <libintl.h>
38 #include <errno.h>
39 #include "pkglib.h"
40 #include "install.h"
41 #include "libadm.h"
42 #include "libinst.h"
43 #include "pkginstall.h"
44 #include "messages.h"
45 
46 extern char	instdir[], pkgbin[], pkgloc[], savlog[], *pkginst, **environ;
47 extern char	saveSpoolInstallDir[];
48 extern char	pkgsav[];	/* pkginstall/main.c */
49 static char 	*infoloc;
50 
51 /*
52  * flag definitions for each entry in table
53  */
54 
55 typedef unsigned int TBL_FLAG_T;
56 
57 /* no flag set */
58 #define	FLAG_NONE	((TBL_FLAG_T)0x0000)
59 
60 /* exclude this attribute if found */
61 #define	FLAG_EXCLUDE	((TBL_FLAG_T)0x0001)
62 
63 /* this attribute must not change if found */
64 #define	FLAG_IDENTICAL	((TBL_FLAG_T)0x0002)
65 
66 /*
67  * macro to generate an entry in the table:
68  *	TBL_ENTRY("PKGINFO_ATTRIBUTE=", FLAG_XXX)
69  * where:
70  *	"PKGINFO_ATTRIBUTE=" is the attribute to look for
71  *	FLAG_XXX is the action to perform when the attribute is found
72  */
73 
74 #define	TBL_ENTRY(_Y_, _F_)	{ (_Y_), ((sizeof ((_Y_)))-1), (_F_) }
75 
76 /*
77  * table containing attributes that require special handling
78  */
79 
80 struct _namelist {
81 	char		*_nlName;	/* attribute name */
82 	int		_nlLen;		/* attribute length */
83 	TBL_FLAG_T	_nlFlag;	/* attribute disposition flag */
84 };
85 
86 typedef struct _namelist NAMELIST_T;
87 
88 /*
89  * These are attributes to be acted on in some way when a pkginfo file is
90  * merged. This table MUST be in alphabetical order because it is searched
91  * using a binary search algorithm.
92  */
93 
94 static NAMELIST_T attrTbl[] = {
95 	TBL_ENTRY("BASEDIR=",			FLAG_EXCLUDE),
96 	TBL_ENTRY("CLASSES=",			FLAG_EXCLUDE),
97 	TBL_ENTRY("CLIENT_BASEDIR=",		FLAG_EXCLUDE),
98 	TBL_ENTRY("INST_DATADIR=",		FLAG_EXCLUDE),
99 	TBL_ENTRY("PKG_CAS_PASSRELATIVE=",	FLAG_EXCLUDE),
100 	TBL_ENTRY("PKG_DST_QKVERIFY=",		FLAG_EXCLUDE),
101 	TBL_ENTRY("PKG_INIT_INSTALL=",		FLAG_EXCLUDE),
102 	TBL_ENTRY("PKG_INSTALL_ROOT=",		FLAG_EXCLUDE),
103 	TBL_ENTRY("PKG_SRC_NOVERIFY=",		FLAG_EXCLUDE),
104 	TBL_ENTRY("SUNW_PKGCOND_GLOBAL_DATA=",	FLAG_EXCLUDE),
105 	TBL_ENTRY("SUNW_PKG_ALLZONES=",		FLAG_IDENTICAL),
106 	TBL_ENTRY("SUNW_PKG_DIR=",		FLAG_EXCLUDE),
107 	TBL_ENTRY("SUNW_PKG_HOLLOW=",		FLAG_IDENTICAL),
108 	TBL_ENTRY("SUNW_PKG_INSTALL_ZONENAME=",	FLAG_EXCLUDE),
109 	TBL_ENTRY("SUNW_PKG_THISZONE=",		FLAG_IDENTICAL),
110 };
111 
112 #define	ATTRTBL_SIZE	(sizeof (attrTbl) / sizeof (NAMELIST_T))
113 
114 /*
115  * While pkgsav has to be set up with reference to the server for package
116  * scripts, it has to be client-relative in the pkginfo file. This function
117  * is used to set the client-relative value for use in the pkginfo file.
118  */
119 void
120 set_infoloc(char *path)
121 {
122 	if (path && *path) {
123 		if (is_an_inst_root()) {
124 			/* Strip the server portion of the path. */
125 			infoloc = orig_path(path);
126 		} else {
127 			infoloc = strdup(path);
128 		}
129 	}
130 }
131 
132 void
133 merginfo(struct cl_attr **pclass, int install_from_pspool)
134 {
135 	DIR		*pdirfp;
136 	FILE		*fp;
137 	FILE		*pkginfoFP;
138 	char		path[PATH_MAX];
139 	char		cmd[PATH_MAX];
140 	char		pkginfoPath[PATH_MAX];
141 	char		temp[PATH_MAX];
142 	int		i;
143 	int		nc;
144 	int		out;
145 
146 	/* remove savelog from previous attempts */
147 
148 	(void) unlink(savlog);
149 
150 	/*
151 	 * create path to appropriate pkginfo file for the package that is
152 	 * already installed - is_spool_create() will be set (!= 0) if the
153 	 * -t option is presented to pkginstall - the -t option is used to
154 	 * disable save spool area creation; do not spool any partial package
155 	 * contents, that is, suppress the creation and population of the
156 	 * package save spool area (var/sadm/pkg/PKG/save/pspool/PKG). This
157 	 * option is set only when a non-global zone is being created.
158 	 */
159 
160 	if (is_spool_create() == 0) {
161 		/*
162 		 * normal package install (not a non-global zone install);
163 		 * use the standard installed pkginfo file for this package:
164 		 * --> /var/sadm/pkg/PKGINST/pkginfo
165 		 * as the source pkginfo file to scan.
166 		 */
167 		i = snprintf(pkginfoPath, sizeof (pkginfoPath),
168 			"%s/var/sadm/pkg/%s/%s",
169 			((get_inst_root()) &&
170 			(strcmp(get_inst_root(), "/") != 0)) ?
171 			get_inst_root() : "", pkginst,
172 			PKGINFO);
173 		if (i > sizeof (pkginfoPath)) {
174 			progerr(ERR_CREATE_PATH_2,
175 				((get_inst_root()) &&
176 				(strcmp(get_inst_root(), "/") != 0)) ?
177 				get_inst_root() : "/",
178 				pkginst);
179 			quit(1);
180 		}
181 	} else {
182 		/*
183 		 * non-global zone installation - use the "saved" pspool
184 		 * pkginfo file in the global zone for this package:
185 		 * --> /var/sadm/install/PKG/save/pspool/PKG/pkginfo
186 		 * as the source pkginfo file to scan.
187 		 */
188 		i = snprintf(pkginfoPath, sizeof (pkginfoPath), "%s/%s",
189 			saveSpoolInstallDir, PKGINFO);
190 		if (i > sizeof (pkginfoPath)) {
191 			progerr(ERR_CREATE_PATH_2,
192 				saveSpoolInstallDir, PKGINFO);
193 			quit(1);
194 		}
195 	}
196 
197 	i = snprintf(path, PATH_MAX, "%s/%s", pkgloc, PKGINFO);
198 	if (i > PATH_MAX) {
199 		progerr(ERR_CREATE_PATH_2, pkgloc, PKGINFO);
200 		quit(1);
201 	}
202 
203 	/* entry debugging info */
204 
205 	echoDebug(DBG_MERGINFO_ENTRY,
206 		instdir ? instdir : "??",
207 		((get_inst_root()) &&
208 		(strcmp(get_inst_root(), "/") != 0)) ?
209 		get_inst_root() : "??",
210 		saveSpoolInstallDir ? saveSpoolInstallDir : "??",
211 		pkgloc ? pkgloc : "??",	is_spool_create(),
212 		get_info_basedir() ? get_info_basedir() : "??",
213 		pkginfoPath, path);
214 
215 	/*
216 	 * open the pkginfo file:
217 	 * if the source pkginfo file to check is the same as the merged one
218 	 * (e.g. /var/sadm/pkg/PKGINST/pkginfo) then do not open the source
219 	 * pkginfo file to "verify"
220 	 */
221 
222 	if (strcmp(pkginfoPath, path) == 0) {
223 		pkginfoFP = (FILE *)NULL;
224 		echoDebug(DBG_MERGINFO_SAME, path);
225 	} else {
226 		echoDebug(DBG_MERGINFO_DIFFERENT, pkginfoPath, path);
227 		pkginfoFP = fopen(pkginfoPath, "r");
228 
229 		if (pkginfoFP == (FILE *)NULL) {
230 			echoDebug(ERR_NO_PKG_INFOFILE, pkginst, pkginfoPath,
231 				strerror(errno));
232 		}
233 	}
234 
235 	/*
236 	 * output packaging environment to create a pkginfo file in pkgloc[]
237 	 */
238 
239 	if ((fp = fopen(path, "w")) == NULL) {
240 		progerr(ERR_CANNOT_OPEN_FOR_WRITING, path, strerror(errno));
241 		quit(99);
242 	}
243 
244 	/*
245 	 * output CLASSES attribute
246 	 */
247 
248 	out = 0;
249 	(void) fputs("CLASSES=", fp);
250 	if (pclass) {
251 		(void) fputs(pclass[0]->name, fp);
252 		out++;
253 		for (i = 1; pclass[i]; i++) {
254 			(void) putc(' ', fp);
255 			(void) fputs(pclass[i]->name, fp);
256 			out++;
257 		}
258 	}
259 	nc = cl_getn();
260 	for (i = 0; i < nc; i++) {
261 		int found = 0;
262 
263 		if (pclass) {
264 			int	j;
265 
266 			for (j = 0; pclass[j]; ++j) {
267 				if (cl_nam(i) != NULL &&
268 					strcmp(cl_nam(i),
269 					pclass[j]->name) == 0) {
270 					found++;
271 					break;
272 				}
273 			}
274 		}
275 		if (!found) {
276 			if (out > 0) {
277 				(void) putc(' ', fp);
278 			}
279 			(void) fputs(cl_nam(i), fp);
280 			out++;
281 		}
282 	}
283 	(void) putc('\n', fp);
284 
285 	/*
286 	 * NOTE : BASEDIR below is relative to the machine that
287 	 * *runs* the package. If there's an install root, this
288 	 * is actually the CLIENT_BASEDIR wrt the machine
289 	 * doing the pkgadd'ing here. -- JST
290 	 */
291 
292 	if (is_a_basedir()) {
293 		static char	*txs1 = "BASEDIR=";
294 
295 		(void) fputs(txs1, fp);
296 		(void) fputs(get_info_basedir(), fp);
297 		(void) putc('\n', fp);
298 	} else {
299 		(void) fputs("BASEDIR=/", fp);
300 		(void) putc('\n', fp);
301 	}
302 
303 	/*
304 	 * output all other environment attributes except those which
305 	 * are relevant only to install.
306 	 */
307 
308 	for (i = 0; environ[i] != (char *)NULL; i++) {
309 		char	*ep = environ[i];
310 		int	attrPos = -1;
311 		int	incr = (ATTRTBL_SIZE >> 1)+1;	/* searches possible */
312 		int	pos = ATTRTBL_SIZE >> 1;	/* start in middle */
313 		NAMELIST_T	*pp = (NAMELIST_T *)NULL;
314 
315 		/*
316 		 * find this attribute in the table - accept the attribute if it
317 		 * is outside of the bounds of the table; otherwise, do a binary
318 		 * search looking for this attribute.
319 		 */
320 
321 		if (strncmp(ep, attrTbl[0]._nlName, attrTbl[0]._nlLen) < 0) {
322 
323 			/* entry < first entry in attribute table */
324 
325 			echoDebug(DBG_MERGINFO_LESS_THAN, ep,
326 				attrTbl[0]._nlName);
327 
328 		} else if (strncmp(ep, attrTbl[ATTRTBL_SIZE-1]._nlName,
329 				attrTbl[ATTRTBL_SIZE-1]._nlLen) > 0) {
330 
331 			/* entry > last entry in attribute table */
332 
333 			echoDebug(DBG_MERGINFO_GREATER_THAN, ep,
334 				attrTbl[ATTRTBL_SIZE-1]._nlName);
335 
336 		} else {
337 			/* first entry < entry < last entry in table: search */
338 
339 			echoDebug(DBG_MERGINFO_SEARCHING, ep,
340 				attrTbl[0]._nlName,
341 				attrTbl[ATTRTBL_SIZE-1]._nlName);
342 
343 			while (incr > 0) {	/* while possible to divide */
344 				int	r;
345 
346 				pp = &attrTbl[pos];
347 
348 				/* compare current attr with this table entry */
349 				r = strncmp(pp->_nlName, ep, pp->_nlLen);
350 
351 				/* break out of loop if match */
352 				if (r == 0) {
353 					/* save location/break if match found */
354 					attrPos = pos;
355 					break;
356 				}
357 
358 				/* no match search to next/prev half */
359 				incr = incr >> 1;
360 				pos += (r < 0) ? incr : -incr;
361 				continue;
362 			}
363 		}
364 
365 		/* handle excluded attribute found */
366 
367 		if ((attrPos >= 0) && (pp->_nlFlag == FLAG_EXCLUDE)) {
368 			/* attribute is excluded */
369 			echoDebug(DBG_MERGINFO_EXCLUDING, ep);
370 			continue;
371 		}
372 
373 		/* handle fixed attribute found */
374 
375 		if ((pkginfoFP != (FILE *)NULL) && (attrPos >= 0) &&
376 			(pp->_nlFlag == FLAG_IDENTICAL)) {
377 			/* attribute must not change */
378 
379 			char	*src = ep+pp->_nlLen;
380 			char	*trg;
381 			char	theAttr[PATH_MAX+1];
382 
383 			/* isolate attribute name only without '=' at end */
384 
385 			(void) strncpy(theAttr, pp->_nlName, pp->_nlLen-1);
386 			theAttr[pp->_nlLen-1] = '\0';
387 
388 			/* lookup attribute in installed package pkginfo file */
389 
390 			rewind(pkginfoFP);
391 			trg = fpkgparam(pkginfoFP, theAttr);
392 
393 			echoDebug(DBG_MERGINFO_ATTRCOMP, theAttr,
394 				trg ? trg : "");
395 
396 			/* if target not found attribute is being added */
397 
398 			if (trg == (char *)NULL) {
399 				progerr(ERR_PKGINFO_ATTR_ADDED, pkginst, ep);
400 				quit(1);
401 			}
402 
403 			/* error if two values are not the same */
404 
405 			if (strcmp(src, trg) != 0) {
406 				progerr(ERR_PKGINFO_ATTR_CHANGED, pkginst,
407 					theAttr, src, trg);
408 				quit(1);
409 			}
410 		}
411 
412 		/* attribute not excluded/has not changed - process */
413 
414 		if ((strncmp(ep, "PKGSAV=", 7) == 0)) {
415 			(void) fputs("PKGSAV=", fp);
416 			(void) fputs(infoloc, fp);
417 			(void) putc('/', fp);
418 			(void) fputs(pkginst, fp);
419 			(void) fputs("/save\n", fp);
420 			continue;
421 		}
422 
423 		if ((strncmp(ep, "UPDATE=", 7) == 0) &&
424 				install_from_pspool != 0 &&
425 				!isPatchUpdate() &&
426 				!isUpdate()) {
427 			continue;
428 		}
429 
430 		echoDebug(DBG_MERGINFO_FINAL, ep);
431 
432 		(void) fputs(ep, fp);
433 		(void) putc('\n', fp);
434 	}
435 
436 	(void) fclose(fp);
437 	(void) fclose(pkginfoFP);
438 
439 	/*
440 	 * copy all packaging scripts to appropriate directory
441 	 */
442 
443 	i = snprintf(path, PATH_MAX, "%s/install", instdir);
444 	if (i > PATH_MAX) {
445 		progerr(ERR_CREATE_PATH_2, instdir, "/install");
446 		quit(1);
447 	}
448 
449 	if ((pdirfp = opendir(path)) != NULL) {
450 		struct dirent	*dp;
451 
452 		while ((dp = readdir(pdirfp)) != NULL) {
453 			if (dp->d_name[0] == '.')
454 				continue;
455 
456 			i = snprintf(path, PATH_MAX, "%s/install/%s",
457 					instdir, dp->d_name);
458 			if (i > PATH_MAX) {
459 				progerr(ERR_CREATE_PATH_3, instdir, "/install/",
460 					dp->d_name);
461 				quit(1);
462 			}
463 
464 			i = snprintf(temp, PATH_MAX, "%s/%s", pkgbin,
465 					dp->d_name);
466 			if (i > PATH_MAX) {
467 				progerr(ERR_CREATE_PATH_2, pkgbin, dp->d_name);
468 				quit(1);
469 			}
470 
471 			if (cppath(MODE_SRC|DIR_DISPLAY, path, temp, 0644)) {
472 			    progerr(ERR_CANNOT_COPY, dp->d_name, pkgbin);
473 				quit(99);
474 			}
475 		}
476 		(void) closedir(pdirfp);
477 	}
478 
479 	/*
480 	 * copy all packaging scripts to the partial spool directory
481 	 */
482 
483 	if (!is_spool_create()) {
484 		/* packages are being spooled to ../save/pspool/.. */
485 		i = snprintf(path, PATH_MAX, "%s/install", instdir);
486 		if (i > PATH_MAX) {
487 			progerr(ERR_CREATE_PATH_2, instdir, "/install");
488 			quit(1);
489 		}
490 
491 		if (((pdirfp = opendir(path)) != NULL) &&
492 			!isPatchUpdate()) {
493 			struct dirent	*dp;
494 
495 
496 			while ((dp = readdir(pdirfp)) != NULL) {
497 				if (dp->d_name[0] == '.')
498 					continue;
499 				/*
500 				 * Don't copy i.none since if it exists it
501 				 * contains Class Archive Format procedure
502 				 * for installing archives. Only Directory
503 				 * Format packages can exist
504 				 * in a global spooled area.
505 				 */
506 				if (strcmp(dp->d_name, "i.none") == 0)
507 					continue;
508 
509 				i = snprintf(path, PATH_MAX, "%s/install/%s",
510 						instdir, dp->d_name);
511 
512 				if (i > PATH_MAX) {
513 					progerr(ERR_CREATE_PATH_3, instdir,
514 						"/install/", dp->d_name);
515 					quit(1);
516 				}
517 
518 				i = snprintf(temp, PATH_MAX, "%s/install/%s",
519 						saveSpoolInstallDir,
520 						dp->d_name);
521 
522 				if (i > PATH_MAX) {
523 					progerr(ERR_CREATE_PATH_3,
524 						saveSpoolInstallDir,
525 						"/install/", dp->d_name);
526 					quit(1);
527 				}
528 
529 				if (cppath(MODE_SRC, path, temp, 0644)) {
530 					progerr(ERR_CANNOT_COPY, path, temp);
531 					(void) closedir(pdirfp);
532 					quit(99);
533 				}
534 			}
535 			(void) closedir(pdirfp);
536 		}
537 
538 		/*
539 		 * Now copy the original pkginfo and pkgmap files from the
540 		 * installing package to the spooled directory.
541 		 */
542 
543 		i = snprintf(path, sizeof (path), "%s/%s", instdir, PKGINFO);
544 		if (i > sizeof (path)) {
545 			progerr(ERR_CREATE_PATH_2, instdir, PKGINFO);
546 			quit(1);
547 		}
548 
549 		i = snprintf(temp, sizeof (temp), "%s/%s",
550 				saveSpoolInstallDir, PKGINFO);
551 		if (i > sizeof (temp)) {
552 			progerr(ERR_CREATE_PATH_2, saveSpoolInstallDir,
553 				PKGINFO);
554 			quit(1);
555 		}
556 
557 		if (cppath(MODE_SRC, path, temp, 0644)) {
558 			progerr(ERR_CANNOT_COPY, path, temp);
559 			quit(99);
560 		}
561 
562 		/*
563 		 * Only want to copy the FCS pkgmap if this is not a
564 		 * patch installation.
565 		 */
566 
567 		if (!isPatchUpdate()) {
568 			i = snprintf(path, sizeof (path), "%s/pkgmap", instdir);
569 			if (i > sizeof (path)) {
570 				progerr(ERR_CREATE_PATH_2, instdir, "pkgmap");
571 				quit(1);
572 			}
573 
574 			i = snprintf(temp, sizeof (temp), "%s/pkgmap",
575 				saveSpoolInstallDir);
576 			if (i > sizeof (path)) {
577 				progerr(ERR_CREATE_PATH_2, saveSpoolInstallDir,
578 					"pkgmap");
579 				quit(1);
580 			}
581 
582 			if (cppath(MODE_SRC, path, temp, 0644)) {
583 				progerr(ERR_CANNOT_COPY, path, temp);
584 				quit(99);
585 			}
586 		}
587 	}
588 
589 	/*
590 	 * If we are installing from a spool directory
591 	 * copy the save directory from it, it may have
592 	 * been patched. Duplicate it only if this
593 	 * installation isn't an update and is not to
594 	 * an alternate root.
595 	 */
596 	if (strstr(instdir, "pspool") != NULL) {
597 		struct stat status;
598 
599 		i = snprintf(path, sizeof (path), "%s/save", instdir);
600 		if (i > sizeof (path)) {
601 			progerr(ERR_CREATE_PATH_2, instdir, "save");
602 			quit(1);
603 		}
604 
605 		if ((stat(path, &status) == 0) &&
606 				(status.st_mode & S_IFDIR) &&
607 				!isPatchUpdate()) {
608 			i = snprintf(cmd, sizeof (cmd), "cp -pr %s/* %s",
609 					path, pkgsav);
610 			if (i > sizeof (cmd)) {
611 				progerr(ERR_SNPRINTF, "cp -pr %s/* %s");
612 				quit(1);
613 			}
614 
615 			if (system(cmd)) {
616 				progerr(ERR_PKGBINCP, path, pkgsav);
617 				quit(99);
618 			}
619 		}
620 	}
621 }
622