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