xref: /illumos-gate/usr/src/lib/libadm/common/pkgparam.c (revision a38ddfee9c8c6b6c5a2947ff52fd2338362a4444)
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 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
22 /*	  All Rights Reserved  	*/
23 
24 /*
25  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
26  * Use is subject to license terms.
27  */
28 
29 #pragma ident	"%Z%%M%	%I%	%E% SMI"	/* SVr4.0 1.1 */
30 /*LINTLIBRARY*/
31 
32 /*   5-20-92   newroot support added  */
33 
34 #include <stdio.h>
35 #include <limits.h>
36 #include <ctype.h>
37 #include <errno.h>
38 #include <string.h>
39 #include <sys/types.h>
40 #include <pkgstrct.h>
41 #include <pkginfo.h>
42 #include <pkglocs.h>
43 #include <stdlib.h>
44 #include <unistd.h>
45 #include "libadm.h"
46 
47 #define	VALSIZ	128
48 #define	NEWLINE	'\n'
49 #define	ESCAPE	'\\'
50 
51 static char sepset[] =	":=\n";
52 static char qset[] = 	"'\"";
53 static char *pkg_inst_root = NULL;
54 
55 char *pkgdir = NULL;
56 char *pkgfile = NULL;
57 
58 static char Adm_pkgold[PATH_MAX] = { 0 }; /* added for newroot */
59 static char Adm_pkgloc[PATH_MAX] = { 0 }; /* added for newroot */
60 static char Adm_pkgadm[PATH_MAX] = { 0 }; /* added for newroot */
61 
62 /*
63  * This looks in a directory that might be the top level directory of a
64  * package. It tests a temporary install directory first and then for a
65  * standard directory. This looks a little confusing, so here's what's
66  * happening. If this pkginfo is being openned in a script during a pkgadd
67  * which is updating an existing package, the original pkginfo file is in a
68  * directory that has been renamed from <pkginst> to .save.<pkginst>. If the
69  * pkgadd fails it will be renamed back to <pkginst>. We are always interested
70  * in the OLD pkginfo data because the new pkginfo data is already in our
71  * environment. For that reason, we try to open the backup first - that has
72  * the old data. This returns the first accessible path in "path" and a "1"
73  * if an appropriate pkginfo file was found. It returns a 0 if no type of
74  * pkginfo was located.
75  */
76 int
77 pkginfofind(char *path, char *pkg_dir, char *pkginst)
78 {
79 	int len = 0;
80 
81 	/* Construct the temporary pkginfo file name. */
82 	len =  snprintf(path, PATH_MAX, "%s/.save.%s/pkginfo", pkg_dir,
83 	    pkginst);
84 	if (len > PATH_MAX)
85 		return (0);
86 	if (access(path, 0)) {
87 		/*
88 		 * This isn't a temporary directory, so we look for a
89 		 * regular one.
90 		 */
91 		len =  snprintf(path, PATH_MAX, "%s/%s/pkginfo", pkg_dir,
92 		    pkginst);
93 		if (len > PATH_MAX)
94 			return (0);
95 		if (access(path, 0))
96 			return (0); /* doesn't appear to be a package */
97 	}
98 
99 	return (1);
100 }
101 
102 /*
103  * This opens the appropriate pkginfo file for a particular package.
104  */
105 FILE *
106 pkginfopen(char *pkg_dir, char *pkginst)
107 {
108 	FILE *fp = NULL;
109 	char temp[PATH_MAX];
110 
111 	if (pkginfofind(temp, pkg_dir, pkginst))
112 		fp = fopen(temp, "r");
113 
114 	return (fp);
115 }
116 
117 
118 char *
119 fpkgparam(FILE *fp, char *param)
120 {
121 	char	ch, buffer[VALSIZ];
122 	char	*mempt, *copy;
123 	int	c, n;
124 	boolean_t check_end_quote = B_FALSE;
125 	boolean_t begline, quoted, escape;
126 	int idx = 0;
127 
128 	if (param == NULL) {
129 		errno = ENOENT;
130 		return (NULL);
131 	}
132 
133 	mempt = NULL;
134 
135 	for (;;) {		/* For each entry in the file fp */
136 		copy = buffer;
137 		n = 0;
138 
139 		/* Get the next token. */
140 		while ((c = getc(fp)) != EOF) {
141 			ch = (char)c;
142 			if (strchr(sepset, ch))
143 				break;
144 			if (++n < VALSIZ)
145 				*copy++ = ch;
146 		}
147 
148 		/* If it's the end of the file, exit the for() loop */
149 		if (c == EOF) {
150 			errno = EINVAL;
151 			return (NULL); /* No more entries left */
152 
153 		/* If it's end of line, look for the next parameter. */
154 		} else if (c == NEWLINE)
155 			continue;
156 
157 		/* At this point copy points to the end of a valid parameter. */
158 		*copy = '\0';		/* Terminate the string. */
159 		if (buffer[0] == '#')	/* If it's a comment, drop thru. */
160 			copy = NULL;	/* Comments don't get buffered. */
161 		else {
162 			/* If parameter is NULL, we return whatever we got. */
163 			if (param[0] == '\0') {
164 				(void) strcpy(param, buffer);
165 				copy = buffer;
166 
167 			/* If this doesn't match the parameter, drop thru. */
168 			} else if (strcmp(param, buffer))
169 				copy = NULL;
170 
171 			/* Otherwise, this is our boy. */
172 			else
173 				copy = buffer;
174 		}
175 
176 		n = 0;
177 		quoted = escape = B_FALSE;
178 		begline = B_TRUE; /* Value's line begins */
179 
180 		/* Now read the parameter value. */
181 		while ((c = getc(fp)) != EOF) {
182 			ch = (char)c;
183 
184 			if (begline && ((ch == ' ') || (ch == '\t')))
185 				continue; /* Ignore leading white space */
186 
187 			/*
188 			 * Take last end quote 'verbatim' if anything
189 			 * other than space, newline and escape.
190 			 * Example:
191 			 * PARAM1="zonename="test-zone""
192 			 *	Here in this example the letter 't' inside
193 			 *	the value is followed by '"', this makes
194 			 *	the previous end quote candidate '"',
195 			 *	a part of value and the end quote
196 			 *	disqualfies. Reset check_end_quote.
197 			 * PARAM2="value"<== newline here
198 			 * PARAM3="value"\
199 			 * "continued"<== newline here.
200 			 *	Check for end quote continues.
201 			 */
202 			if (ch != NEWLINE && ch != ' ' && ch != ESCAPE &&
203 			    ch != '\t' && check_end_quote)
204 				check_end_quote = B_FALSE;
205 
206 			if (ch == NEWLINE) {
207 				if (!escape) {
208 					/*
209 					 * The end quote candidate qualifies.
210 					 * Eat any trailing spaces.
211 					 */
212 					if (check_end_quote) {
213 						copy -= n - idx;
214 						n = idx;
215 						check_end_quote = B_FALSE;
216 						quoted = B_FALSE;
217 					}
218 					break; /* End of entry */
219 				}
220 				/*
221 				 * The end quote if exists, doesn't qualify.
222 				 * Eat end quote and trailing spaces if any.
223 				 * Value spans to next line.
224 				 */
225 				if (check_end_quote) {
226 					copy -= n - idx;
227 					n = idx;
228 					check_end_quote = B_FALSE;
229 				} else if (copy) {
230 					copy--; /* Eat previous esc */
231 					n--;
232 				}
233 				escape = B_FALSE;
234 				begline = B_TRUE; /* New input line */
235 				continue;
236 			} else {
237 				if (!escape && strchr(qset, ch)) {
238 					/* Handle quotes */
239 					if (begline) {
240 						/* Starting quote */
241 						quoted = B_TRUE;
242 						begline = B_FALSE;
243 						continue;
244 					} else if (quoted) {
245 						/*
246 						 * This is the candidate
247 						 * for end quote. Check
248 						 * to see it qualifies.
249 						 */
250 						check_end_quote = B_TRUE;
251 						idx = n;
252 					}
253 				}
254 				if (ch == ESCAPE)
255 					escape = B_TRUE;
256 				else if (escape)
257 					escape = B_FALSE;
258 				if (copy) *copy++ = ch;
259 				begline = B_FALSE;
260 			}
261 
262 			if (copy && ((++n % VALSIZ) == 0)) {
263 				if (mempt) {
264 					mempt = realloc(mempt,
265 					    (n+VALSIZ)*sizeof (char));
266 					if (!mempt)
267 						return (NULL);
268 				} else {
269 					mempt = calloc((size_t)(2*VALSIZ),
270 					    sizeof (char));
271 					if (!mempt)
272 						return (NULL);
273 					(void) strncpy(mempt, buffer, n);
274 				}
275 				copy = &mempt[n];
276 			}
277 		}
278 
279 		/*
280 		 * Don't allow trailing white space.
281 		 * NOTE : White space in the middle is OK, since this may
282 		 * be a list. At some point it would be a good idea to let
283 		 * this function know how to validate such a list. -- JST
284 		 *
285 		 * Now while there's a parametric value and it ends in a
286 		 * space and the actual remaining string length is still
287 		 * greater than 0, back over the space.
288 		 */
289 		while (copy && isspace((unsigned char)*(copy - 1)) && n-- > 0)
290 			copy--;
291 
292 		if (quoted) {
293 			if (mempt)
294 				(void) free(mempt);
295 			errno = EFAULT; /* missing closing quote */
296 			return (NULL);
297 		}
298 		if (copy) {
299 			*copy = '\0';
300 			break;
301 		}
302 		if (c == EOF) {
303 			errno = EINVAL; /* parameter not found */
304 			return (NULL);
305 		}
306 	}
307 
308 	if (!mempt)
309 		mempt = strdup(buffer);
310 	else
311 		mempt = realloc(mempt, (strlen(mempt)+1)*sizeof (char));
312 	return (mempt);
313 }
314 
315 char *
316 pkgparam(char *pkg, char *param)
317 {
318 	static char lastfname[PATH_MAX];
319 	static FILE *fp = NULL;
320 	char *pt, *copy, *value, line[PATH_MAX];
321 
322 	if (!pkgdir)
323 		pkgdir = get_PKGLOC();
324 
325 	if (!pkg) {
326 		/* request to close file */
327 		if (fp) {
328 			(void) fclose(fp);
329 			fp = NULL;
330 		}
331 		return (NULL);
332 	}
333 
334 	if (!param) {
335 		errno = ENOENT;
336 		return (NULL);
337 	}
338 
339 	if (pkgfile)
340 		(void) strcpy(line, pkgfile); /* filename was passed */
341 	else
342 		(void) pkginfofind(line, pkgdir, pkg);
343 
344 	if (fp && strcmp(line, lastfname)) {
345 		/* different filename implies need for different fp */
346 		(void) fclose(fp);
347 		fp = NULL;
348 	}
349 	if (!fp) {
350 		(void) strcpy(lastfname, line);
351 		if ((fp = fopen(lastfname, "r")) == NULL)
352 			return (NULL);
353 	}
354 
355 	/*
356 	 * if parameter is a null string, then the user is requesting us
357 	 * to find the value of the next available parameter for this
358 	 * package and to copy the parameter name into the provided string;
359 	 * if it is not, then it is a request for a specified parameter, in
360 	 * which case we rewind the file to start search from beginning
361 	 */
362 	if (param[0]) {
363 		/* new parameter request, so reset file position */
364 		if (fseek(fp, 0L, 0))
365 			return (NULL);
366 	}
367 
368 	if (pt = fpkgparam(fp, param)) {
369 		if (strcmp(param, "ARCH") == NULL ||
370 		    strcmp(param, "CATEGORY") == NULL) {
371 			/* remove all whitespace from value */
372 			value = copy = pt;
373 			while (*value) {
374 				if (!isspace((unsigned char)*value))
375 					*copy++ = *value;
376 				value++;
377 			}
378 			*copy = '\0';
379 		}
380 		return (pt);
381 	}
382 	return (NULL);
383 }
384 /*
385  * This routine sets adm_pkgloc and adm_pkgadm which are the
386  * replacement location for PKGLOC and PKGADM.
387  */
388 
389 static void canonize_name(char *);
390 
391 void
392 set_PKGpaths(char *path)
393 {
394 	if (path && *path) {
395 		(void) sprintf(Adm_pkgloc, "%s%s", path, PKGLOC);
396 		(void) sprintf(Adm_pkgold, "%s%s", path, PKGOLD);
397 		(void) sprintf(Adm_pkgadm, "%s%s", path, PKGADM);
398 		set_install_root(path);
399 	} else {
400 		(void) sprintf(Adm_pkgloc, "%s", PKGLOC);
401 		(void) sprintf(Adm_pkgold, "%s", PKGOLD);
402 		(void) sprintf(Adm_pkgadm, "%s", PKGADM);
403 	}
404 	canonize_name(Adm_pkgloc);
405 	canonize_name(Adm_pkgold);
406 	canonize_name(Adm_pkgadm);
407 	pkgdir = Adm_pkgloc;
408 }
409 
410 char *
411 get_PKGLOC(void)
412 {
413 	if (Adm_pkgloc[0] == NULL)
414 		return (PKGLOC);
415 	else
416 		return (Adm_pkgloc);
417 }
418 
419 char *
420 get_PKGOLD(void)
421 {
422 	if (Adm_pkgold[0] == NULL)
423 		return (PKGOLD);
424 	else
425 		return (Adm_pkgold);
426 }
427 
428 char *
429 get_PKGADM(void)
430 {
431 	if (Adm_pkgadm[0] == NULL)
432 		return (PKGADM);
433 	else
434 		return (Adm_pkgadm);
435 }
436 
437 void
438 set_PKGADM(char *newpath)
439 {
440 	(void) strcpy(Adm_pkgadm, newpath);
441 }
442 
443 void
444 set_PKGLOC(char *newpath)
445 {
446 	(void) strcpy(Adm_pkgloc, newpath);
447 }
448 
449 #define	isdot(x)	((x[0] == '.')&&(!x[1]||(x[1] == '/')))
450 #define	isdotdot(x)	((x[0] == '.')&&(x[1] == '.')&&(!x[2]||(x[2] == '/')))
451 
452 static void
453 canonize_name(char *file)
454 {
455 	char *pt, *last;
456 	int level;
457 
458 	/* Remove references such as "./" and "../" and "//" */
459 
460 	for (pt = file; *pt; ) {
461 		if (isdot(pt))
462 			(void) strcpy(pt, pt[1] ? pt+2 : pt+1);
463 		else if (isdotdot(pt)) {
464 			level = 0;
465 			last = pt;
466 			do {
467 				level++;
468 				last += 2;
469 				if (*last)
470 					last++;
471 			} while (isdotdot(last));
472 			--pt; /* point to previous '/' */
473 			while (level--) {
474 				if (pt <= file)
475 					return;
476 				while ((*--pt != '/') && (pt > file))
477 					;
478 			}
479 			if (*pt == '/')
480 				pt++;
481 			(void) strcpy(pt, last);
482 		} else {
483 			while (*pt && (*pt != '/'))
484 				pt++;
485 			if (*pt == '/') {
486 				while (pt[1] == '/')
487 					(void) strcpy(pt, pt+1);
488 				pt++;
489 			}
490 		}
491 	}
492 	if ((--pt > file) && (*pt == '/'))
493 		*pt = '\0';
494 }
495 
496 void
497 set_install_root(char *path)
498 {
499 	pkg_inst_root = strdup(path);
500 }
501 
502 char *
503 get_install_root()
504 {
505 	return (pkg_inst_root);
506 }
507