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
pkginfofind(char * path,char * pkg_dir,char * pkginst)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 *
pkginfopen(char * pkg_dir,char * pkginst)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 *
fpkgparam(FILE * fp,char * param)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 *
pkgparam(char * pkg,char * param)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") == NULL ||
372 strcmp(param, "CATEGORY") == NULL) {
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
set_PKGpaths(char * path)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 *
get_PKGLOC(void)412 get_PKGLOC(void)
413 {
414 if (Adm_pkgloc[0] == NULL)
415 return (PKGLOC);
416 else
417 return (Adm_pkgloc);
418 }
419
420 char *
get_PKGADM(void)421 get_PKGADM(void)
422 {
423 if (Adm_pkgadm[0] == NULL)
424 return (PKGADM);
425 else
426 return (Adm_pkgadm);
427 }
428
429 void
set_PKGADM(char * newpath)430 set_PKGADM(char *newpath)
431 {
432 (void) strcpy(Adm_pkgadm, newpath);
433 }
434
435 void
set_PKGLOC(char * newpath)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
canonize_name(char * file)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
set_install_root(char * path)489 set_install_root(char *path)
490 {
491 pkg_inst_root = strdup(path);
492 }
493
494 char *
get_install_root()495 get_install_root()
496 {
497 return (pkg_inst_root);
498 }
499