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