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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 /*
26  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
27  * Use is subject to license terms.
28  */
29 
30 #pragma	ident	"%Z%%M%	%I%	%E% SMI"	/* SVr4.0 1.1 */
31 /*LINTLIBRARY*/
32 
33 /*   5-20-92   newroot support added  */
34 
35 #include <stdio.h>
36 #include <limits.h>
37 #include <ctype.h>
38 #include <errno.h>
39 #include <string.h>
40 #include <sys/types.h>
41 #include <pkgstrct.h>
42 #include <pkginfo.h>
43 #include <pkglocs.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46 #include "libadm.h"
47 
48 #define	VALSIZ	128
49 #define	NEWLINE	'\n'
50 #define	ESCAPE	'\\'
51 
52 static char sepset[] =	":=\n";
53 static char qset[] = 	"'\"";
54 static char *pkg_inst_root = NULL;
55 
56 char *pkgdir = NULL;
57 char *pkgfile = NULL;
58 
59 static char Adm_pkgold[PATH_MAX] = { 0 }; /* added for newroot */
60 static char Adm_pkgloc[PATH_MAX] = { 0 }; /* added for newroot */
61 static char Adm_pkgadm[PATH_MAX] = { 0 }; /* added for newroot */
62 
63 /*
64  * This looks in a directory that might be the top level directory of a
65  * package. It tests a temporary install directory first and then for a
66  * standard directory. This looks a little confusing, so here's what's
67  * happening. If this pkginfo is being openned in a script during a pkgadd
68  * which is updating an existing package, the original pkginfo file is in a
69  * directory that has been renamed from <pkginst> to .save.<pkginst>. If the
70  * pkgadd fails it will be renamed back to <pkginst>. We are always interested
71  * in the OLD pkginfo data because the new pkginfo data is already in our
72  * environment. For that reason, we try to open the backup first - that has
73  * the old data. This returns the first accessible path in "path" and a "1"
74  * if an appropriate pkginfo file was found. It returns a 0 if no type of
75  * pkginfo was located.
76  */
77 int
78 pkginfofind(char *path, char *pkg_dir, char *pkginst)
79 {
80 	int len = 0;
81 
82 	/* Construct the temporary pkginfo file name. */
83 	len =  snprintf(path, PATH_MAX, "%s/.save.%s/pkginfo", pkg_dir,
84 	    pkginst);
85 	if (len > PATH_MAX)
86 		return (0);
87 	if (access(path, 0)) {
88 		/*
89 		 * This isn't a temporary directory, so we look for a
90 		 * regular one.
91 		 */
92 		len =  snprintf(path, PATH_MAX, "%s/%s/pkginfo", pkg_dir,
93 		    pkginst);
94 		if (len > PATH_MAX)
95 			return (0);
96 		if (access(path, 0))
97 			return (0); /* doesn't appear to be a package */
98 	}
99 
100 	return (1);
101 }
102 
103 /*
104  * This opens the appropriate pkginfo file for a particular package.
105  */
106 FILE *
107 pkginfopen(char *pkg_dir, char *pkginst)
108 {
109 	FILE *fp = NULL;
110 	char temp[PATH_MAX];
111 
112 	if (pkginfofind(temp, pkg_dir, pkginst))
113 		fp = fopen(temp, "r");
114 
115 	return (fp);
116 }
117 
118 
119 char *
120 fpkgparam(FILE *fp, char *param)
121 {
122 	char	ch, buffer[VALSIZ];
123 	char	*mempt, *copy;
124 	int	c, n, escape, begline, quoted;
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 = quoted = escape = 0;
175 		begline = 1;
176 
177 		/* Now read the parameter value. */
178 		while ((c = getc(fp)) != EOF) {
179 			ch = (char)c;
180 			if (begline && ((ch == ' ') || (ch == '\t')))
181 				continue; /* ignore leading white space */
182 
183 			if (ch == NEWLINE) {
184 				if (!escape)
185 					break; /* end of entry */
186 				if (copy) {
187 					if (escape) {
188 						copy--; /* eat previous esc */
189 						n--;
190 					}
191 					*copy++ = NEWLINE;
192 				}
193 				escape = 0;
194 				begline = 1; /* new input line */
195 			} else {
196 				if (!escape && strchr(qset, ch)) {
197 					/* handle quotes */
198 					if (begline) {
199 						quoted++;
200 						begline = 0;
201 						continue;
202 					} else if (quoted) {
203 						quoted = 0;
204 						continue;
205 					}
206 				}
207 				if (ch == ESCAPE)
208 					escape++;
209 				else if (escape)
210 					escape = 0;
211 				if (copy) *copy++ = ch;
212 				begline = 0;
213 			}
214 
215 			if (copy && ((++n % VALSIZ) == 0)) {
216 				if (mempt) {
217 					mempt = realloc(mempt,
218 						(n+VALSIZ)*sizeof (char));
219 					if (!mempt)
220 						return (NULL);
221 				} else {
222 					mempt = calloc((size_t)(2*VALSIZ),
223 					    sizeof (char));
224 					if (!mempt)
225 						return (NULL);
226 					(void) strncpy(mempt, buffer, n);
227 				}
228 				copy = &mempt[n];
229 			}
230 		}
231 
232 		/*
233 		 * Don't allow trailing white space.
234 		 * NOTE : White space in the middle is OK, since this may
235 		 * be a list. At some point it would be a good idea to let
236 		 * this function know how to validate such a list. -- JST
237 		 *
238 		 * Now while there's a parametric value and it ends in a
239 		 * space and the actual remaining string length is still
240 		 * greater than 0, back over the space.
241 		 */
242 		while (copy && isspace((unsigned char)*(copy - 1)) && n-- > 0)
243 			copy--;
244 
245 		if (quoted) {
246 			if (mempt)
247 				(void) free(mempt);
248 			errno = EFAULT; /* missing closing quote */
249 			return (NULL);
250 		}
251 		if (copy) {
252 			*copy = '\0';
253 			break;
254 		}
255 		if (c == EOF) {
256 			errno = EINVAL; /* parameter not found */
257 			return (NULL);
258 		}
259 	}
260 
261 	if (!mempt)
262 		mempt = strdup(buffer);
263 	else
264 		mempt = realloc(mempt, (strlen(mempt)+1)*sizeof (char));
265 	return (mempt);
266 }
267 
268 char *
269 pkgparam(char *pkg, char *param)
270 {
271 	static char lastfname[PATH_MAX];
272 	static FILE *fp = NULL;
273 	char *pt, *copy, *value, line[PATH_MAX];
274 
275 	if (!pkgdir)
276 		pkgdir = get_PKGLOC();
277 
278 	if (!pkg) {
279 		/* request to close file */
280 		if (fp) {
281 			(void) fclose(fp);
282 			fp = NULL;
283 		}
284 		return (NULL);
285 	}
286 
287 	if (!param) {
288 		errno = ENOENT;
289 		return (NULL);
290 	}
291 
292 	if (pkgfile)
293 		(void) strcpy(line, pkgfile); /* filename was passed */
294 	else
295 		(void) pkginfofind(line, pkgdir, pkg);
296 
297 	if (fp && strcmp(line, lastfname)) {
298 		/* different filename implies need for different fp */
299 		(void) fclose(fp);
300 		fp = NULL;
301 	}
302 	if (!fp) {
303 		(void) strcpy(lastfname, line);
304 		if ((fp = fopen(lastfname, "r")) == NULL)
305 			return (NULL);
306 	}
307 
308 	/*
309 	 * if parameter is a null string, then the user is requesting us
310 	 * to find the value of the next available parameter for this
311 	 * package and to copy the parameter name into the provided string;
312 	 * if it is not, then it is a request for a specified parameter, in
313 	 * which case we rewind the file to start search from beginning
314 	 */
315 	if (param[0]) {
316 		/* new parameter request, so reset file position */
317 		if (fseek(fp, 0L, 0))
318 			return (NULL);
319 	}
320 
321 	if (pt = fpkgparam(fp, param)) {
322 		if (strcmp(param, "ARCH") == NULL ||
323 		    strcmp(param, "CATEGORY") == NULL) {
324 			/* remove all whitespace from value */
325 			value = copy = pt;
326 			while (*value) {
327 				if (!isspace((unsigned char)*value))
328 					*copy++ = *value;
329 				value++;
330 			}
331 			*copy = '\0';
332 		}
333 		return (pt);
334 	}
335 	return (NULL);
336 }
337 /*
338  * This routine sets adm_pkgloc and adm_pkgadm which are the
339  * replacement location for PKGLOC and PKGADM.
340  */
341 
342 static void canonize_name(char *);
343 
344 void
345 set_PKGpaths(char *path)
346 {
347 	if (path && *path) {
348 		(void) sprintf(Adm_pkgloc, "%s%s", path, PKGLOC);
349 		(void) sprintf(Adm_pkgold, "%s%s", path, PKGOLD);
350 		(void) sprintf(Adm_pkgadm, "%s%s", path, PKGADM);
351 		set_install_root(path);
352 	} else {
353 		(void) sprintf(Adm_pkgloc, "%s", PKGLOC);
354 		(void) sprintf(Adm_pkgold, "%s", PKGOLD);
355 		(void) sprintf(Adm_pkgadm, "%s", PKGADM);
356 	}
357 	canonize_name(Adm_pkgloc);
358 	canonize_name(Adm_pkgold);
359 	canonize_name(Adm_pkgadm);
360 	pkgdir = Adm_pkgloc;
361 }
362 
363 char *
364 get_PKGLOC(void)
365 {
366 	if (Adm_pkgloc[0] == NULL)
367 		return (PKGLOC);
368 	else
369 		return (Adm_pkgloc);
370 }
371 
372 char *
373 get_PKGOLD(void)
374 {
375 	if (Adm_pkgold[0] == NULL)
376 		return (PKGOLD);
377 	else
378 		return (Adm_pkgold);
379 }
380 
381 char *
382 get_PKGADM(void)
383 {
384 	if (Adm_pkgadm[0] == NULL)
385 		return (PKGADM);
386 	else
387 		return (Adm_pkgadm);
388 }
389 
390 void
391 set_PKGADM(char *newpath)
392 {
393 	(void) strcpy(Adm_pkgadm, newpath);
394 }
395 
396 void
397 set_PKGLOC(char *newpath)
398 {
399 	(void) strcpy(Adm_pkgloc, newpath);
400 }
401 
402 #define	isdot(x)	((x[0] == '.')&&(!x[1]||(x[1] == '/')))
403 #define	isdotdot(x)	((x[0] == '.')&&(x[1] == '.')&&(!x[2]||(x[2] == '/')))
404 
405 static void
406 canonize_name(char *file)
407 {
408 	char *pt, *last;
409 	int level;
410 
411 	/* Remove references such as "./" and "../" and "//" */
412 
413 	for (pt = file; *pt; ) {
414 		if (isdot(pt))
415 			(void) strcpy(pt, pt[1] ? pt+2 : pt+1);
416 		else if (isdotdot(pt)) {
417 			level = 0;
418 			last = pt;
419 			do {
420 				level++;
421 				last += 2;
422 				if (*last)
423 					last++;
424 			} while (isdotdot(last));
425 			--pt; /* point to previous '/' */
426 			while (level--) {
427 				if (pt <= file)
428 					return;
429 				while ((*--pt != '/') && (pt > file))
430 					;
431 			}
432 			if (*pt == '/')
433 				pt++;
434 			(void) strcpy(pt, last);
435 		} else {
436 			while (*pt && (*pt != '/'))
437 				pt++;
438 			if (*pt == '/') {
439 				while (pt[1] == '/')
440 					(void) strcpy(pt, pt+1);
441 				pt++;
442 			}
443 		}
444 	}
445 	if ((--pt > file) && (*pt == '/'))
446 		*pt = '\0';
447 }
448 
449 void
450 set_install_root(char *path)
451 {
452 	pkg_inst_root = strdup(path);
453 }
454 
455 char *
456 get_install_root()
457 {
458 	return (pkg_inst_root);
459 }
460