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