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