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 /* 27 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 28 * Use is subject to license terms. 29 */ 30 31 #pragma ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.2 */ 32 /*LINTLIBRARY*/ 33 34 /* 5-20-92 added newroot functions */ 35 36 #include <stdio.h> 37 #include <limits.h> 38 #include <stdarg.h> 39 #include <unistd.h> 40 #include <stdlib.h> 41 #include <ctype.h> 42 #include <string.h> 43 #include <sys/types.h> 44 #include <sys/stat.h> 45 #include <dirent.h> 46 #include <pkginfo.h> 47 #include <pkgstrct.h> 48 #include <pkglocs.h> 49 #include <errno.h> 50 #include "libadm.h" 51 52 static void initpkg(struct pkginfo *); 53 static char *svr4inst(char *); 54 static int rdconfig(struct pkginfo *, char *, char *); 55 static int svr4info(struct pkginfo *, char *, char *); 56 static int ckinfo(char *, char *, char *); 57 static int ckinst(char *, char *, char *, char *, char *); 58 static int verscmp(char *, char *); 59 static int archcmp(char *, char *); 60 static int compver(char *, char *); 61 62 /* 63 * Globals: 64 * pkgdir - specifies the directory where information about packages 65 * resides, i.e. the pkginfo file is located in a subdirectory 66 * 67 * Caveats: 68 * The structure provided via "info" will contain malloc'd information; 69 * this will be free'd upon the next call to pkginfo with this 70 * same structure. Application calls must make sure this structure 71 * is null on the first call, or else we'll free static memory areas 72 * If the "pkg" argument is a wildcard specification, the next found 73 * instance available which matches the request will be returned 74 * If the "pkg" argument is a NULL pointer, the structure pointed to 75 * via "info" will have its elements deallocated and all files 76 * associated with this routine will be closed 77 * 78 * Return codes: 79 * A non-zero exit code indicates error with "errno" appropriately set: 80 * EINVAL - invalid argument 81 * ESRCH - there are no more instances of this package around 82 * EACCESS - unable to access files which should have been there 83 */ 84 85 /*VARARGS*/ 86 int 87 pkginfo(struct pkginfo *info, char *pkginst, ...) 88 { 89 char *ckarch, *ckvers; 90 int check; 91 va_list ap; 92 93 va_start(ap, pkginst); 94 if (info == NULL) { 95 errno = EINVAL; 96 return (-1); 97 } 98 if (pkginst == NULL) { 99 info->pkginst = NULL; 100 (void) fpkginfo(info, NULL); 101 (void) fpkginst(NULL); 102 return (0); 103 } 104 ckarch = va_arg(ap, char *); 105 ckvers = va_arg(ap, char *); 106 va_end(ap); 107 108 check = 0; 109 if (pkgnmchk(pkginst, "all", 1)) { 110 /* wild card specification */ 111 pkginst = fpkginst(pkginst, ckarch, ckvers); 112 if (pkginst == NULL) 113 return (-1); 114 } else { 115 /* request to check indicated instance */ 116 if (ckarch || ckvers) 117 check++; 118 } 119 120 info->pkginst = NULL; 121 if (fpkginfo(info, pkginst)) 122 return (-1); 123 124 if (check) { 125 /* 126 * verify that the provided instance matches 127 * any arch & vers specs that were provided 128 */ 129 if (ckinst(pkginst, info->arch, info->version, ckarch, 130 ckvers)) { 131 errno = ESRCH; 132 return (-1); 133 } 134 } 135 return (0); 136 } 137 /*ARGSUSED*/ 138 139 int 140 fpkginfo(struct pkginfo *info, char *pkginst) 141 { 142 143 if (info == NULL) { 144 errno = EINVAL; 145 return (-1); 146 } 147 148 initpkg(info); 149 150 if (pkginst == NULL) 151 return (0); 152 else if (pkgnmchk(pkginst, "all", 1)) { 153 errno = EINVAL; /* not an instance identifier */ 154 return (-1); 155 } 156 if (pkgdir == NULL) 157 pkgdir = get_PKGLOC(); 158 159 if (rdconfig(info, pkginst, NULL)) { 160 initpkg(info); 161 return (-1); 162 } 163 return (0); 164 } 165 166 static void 167 initpkg(struct pkginfo *info) 168 { 169 /* free previously allocated space */ 170 if (info->pkginst) { 171 free(info->pkginst); 172 if (info->arch) 173 free(info->arch); 174 if (info->version) 175 free(info->version); 176 if (info->basedir) 177 free(info->basedir); 178 if (info->name) 179 free(info->name); 180 if (info->vendor) 181 free(info->vendor); 182 if (info->catg) 183 free(info->catg); 184 } 185 186 info->pkginst = NULL; 187 info->arch = info->version = NULL; 188 info->basedir = info->name = NULL; 189 info->vendor = info->catg = NULL; 190 info->status = PI_UNKNOWN; 191 } 192 193 static int 194 rdconfig(struct pkginfo *info, char *pkginst, char *ckvers) 195 { 196 FILE *fp; 197 char temp[256]; 198 char *value, *pt, *copy, **memloc; 199 int count; 200 201 if ((fp = pkginfopen(pkgdir, pkginst)) == NULL) { 202 if ((errno == ENOENT) && strcmp(pkgdir, get_PKGLOC()) == 0) 203 return (svr4info(info, pkginst, ckvers)); 204 205 errno = EACCES; 206 return (-1); 207 } 208 209 *temp = '\0'; 210 count = 0; 211 while (value = fpkgparam(fp, temp)) { 212 if (strcmp(temp, "ARCH") == 0 || 213 strcmp(temp, "CATEGORY") == 0) { 214 /* remove all whitespace from value */ 215 pt = copy = value; 216 while (*pt) { 217 if (!isspace((unsigned char)*pt)) 218 *copy++ = *pt; 219 pt++; 220 } 221 *copy = '\0'; 222 } 223 count++; 224 memloc = NULL; 225 if (strcmp(temp, "NAME") == 0) 226 memloc = &info->name; 227 else if (strcmp(temp, "VERSION") == 0) 228 memloc = &info->version; 229 else if (strcmp(temp, "ARCH") == 0) 230 memloc = &info->arch; 231 else if (strcmp(temp, "VENDOR") == 0) 232 memloc = &info->vendor; 233 else if (strcmp(temp, "BASEDIR") == 0) 234 memloc = &info->basedir; 235 else if (strcmp(temp, "CATEGORY") == 0) 236 memloc = &info->catg; 237 238 temp[0] = '\0'; 239 if (memloc == NULL) 240 continue; /* not a parameter we're looking for */ 241 242 *memloc = strdup(value); 243 if (!*memloc) { 244 (void) fclose(fp); 245 errno = ENOMEM; 246 return (-1); /* malloc from strdup failed */ 247 } 248 } 249 (void) fclose(fp); 250 251 if (!count) { 252 errno = ESRCH; 253 return (-1); 254 } 255 256 info->status = (strcmp(pkgdir, get_PKGLOC()) ? PI_SPOOLED : 257 PI_INSTALLED); 258 259 if (info->status == PI_INSTALLED) { 260 (void) sprintf(temp, "%s/%s/!I-Lock!", pkgdir, pkginst); 261 if (access(temp, 0) == 0) 262 info->status = PI_PARTIAL; 263 else { 264 (void) sprintf(temp, "%s/%s/!R-Lock!", pkgdir, pkginst); 265 if (access(temp, 0) == 0) 266 info->status = PI_PARTIAL; 267 } 268 } 269 info->pkginst = strdup(pkginst); 270 return (0); 271 } 272 273 static int 274 svr4info(struct pkginfo *info, char *pkginst, char *ckvers) 275 { 276 static DIR *pdirfp; 277 struct stat64 status; 278 FILE *fp; 279 char *pt, path[128], line[128]; 280 char temp[PKGSIZ+1]; 281 282 if (strcmp(pkginst, "all")) { 283 if (pdirfp) { 284 (void) closedir(pdirfp); 285 pdirfp = NULL; 286 } 287 /* determine pkginst - remove '.*' extension, if any */ 288 (void) strncpy(temp, pkginst, PKGSIZ); 289 if (((pt = strchr(temp, '.')) != NULL) && strcmp(pt, ".*") == 0) 290 *pt = '\0'; 291 } 292 293 /* look in /usr/options direcotry for 'name' file */ 294 (void) sprintf(path, "%s/%s.name", get_PKGOLD(), temp); 295 if (lstat64(path, &status)) { 296 errno = (errno == ENOENT) ? ESRCH : EACCES; 297 return (-1); 298 } 299 if ((status.st_mode & S_IFMT) != S_IFREG) { 300 errno = ESRCH; 301 return (-1); 302 } 303 if ((fp = fopen(path, "r")) == NULL) { 304 errno = (errno == ENOENT) ? ESRCH : EACCES; 305 return (-1); 306 } 307 308 /* /usr/options/xxx.name exists */ 309 (void) fgets(line, 128, fp); 310 (void) fclose(fp); 311 if (pt = strchr(line, '\n')) 312 *pt = '\0'; /* remove trailing newline */ 313 if (pt = strchr(line, ':')) 314 *pt++ = '\0'; /* assumed version specification */ 315 316 if (info) { 317 info->name = strdup(line); 318 info->pkginst = strdup(temp); 319 if (!info->name || !info->pkginst) { 320 errno = ENOMEM; 321 return (-1); 322 } 323 info->status = PI_PRESVR4; 324 info->version = NULL; 325 } 326 327 if (pt) { 328 /* eat leading space off of version spec */ 329 while (isspace((unsigned char)*pt)) 330 pt++; 331 } 332 if (ckvers && verscmp(ckvers, pt)) { 333 errno = ESRCH; 334 return (-1); 335 } 336 if (info && *pt) 337 info->version = strdup(pt); 338 return (0); 339 } 340 341 static int 342 ckinst(char *pkginst, char *pkgarch, char *pkgvers, char *ckarch, char *ckvers) 343 { 344 if (ckarch && archcmp(ckarch, pkgarch)) 345 return (-1); 346 if (ckvers) { 347 /* Check for exact version match */ 348 if (verscmp(ckvers, pkgvers)) { 349 /* Check for compatable version */ 350 if (compver(pkginst, ckvers)) 351 return (-1); 352 } 353 } 354 return (0); 355 } 356 357 /*VARARGS*/ 358 char * 359 fpkginst(char *pkg, ...) 360 { 361 static char pkginst[PKGSIZ+1]; 362 static DIR *pdirfp; 363 struct dirent64 *dp; 364 char *pt, *ckarch, *ckvers; 365 va_list ap; 366 367 va_start(ap, pkg); 368 369 if (pkg == NULL) { 370 /* request to close or rewind the file */ 371 if (pdirfp) { 372 (void) closedir(pdirfp); 373 pdirfp = NULL; 374 } 375 (void) svr4inst(NULL); /* close any files used here */ 376 return (NULL); 377 } 378 379 ckarch = va_arg(ap, char *); 380 ckvers = va_arg(ap, char *); 381 va_end(ap); 382 383 if (!pkgdir) 384 pkgdir = get_PKGLOC(); 385 386 if (!pdirfp && ((pdirfp = opendir(pkgdir)) == NULL)) { 387 errno = EACCES; 388 return (NULL); 389 } 390 391 while ((dp = readdir64(pdirfp)) != NULL) { 392 if (dp->d_name[0] == '.') 393 continue; 394 395 if (pkgnmchk(dp->d_name, pkg, 0)) 396 continue; /* ignore invalid SVR4 package names */ 397 398 if (ckinfo(dp->d_name, ckarch, ckvers)) 399 continue; 400 401 /* 402 * Leave directory open in case user requests another 403 * instance. 404 */ 405 (void) strcpy(pkginst, dp->d_name); 406 return (pkginst); 407 } 408 409 /* 410 * If we are searching the directory which contains info about 411 * installed packages, check the pre-svr4 directory for an instance 412 * and be sure it matches any version specification provided to us 413 */ 414 if (strcmp(pkgdir, get_PKGLOC()) == 0 && (ckarch == NULL)) { 415 /* search for pre-SVR4 instance */ 416 if (pt = svr4inst(pkg)) 417 return (pt); 418 } 419 errno = ESRCH; 420 /* close any file we might have open */ 421 (void) closedir(pdirfp); 422 pdirfp = NULL; 423 return (NULL); 424 } 425 /*ARGSUSED*/ 426 427 static char * 428 svr4inst(char *pkg) 429 { 430 static char pkginst[PKGSIZ]; 431 static DIR *pdirfp; 432 struct dirent64 *dp; 433 struct stat64 status; /* file status buffer */ 434 char *pt; 435 char path[PATH_MAX]; 436 437 if (pkg == NULL) { 438 if (pdirfp) { 439 (void) closedir(pdirfp); 440 pdirfp = NULL; 441 } 442 return (NULL); 443 } 444 445 if (!pdirfp && ((pdirfp = opendir(get_PKGOLD())) == NULL)) 446 return (NULL); 447 448 while ((dp = readdir64(pdirfp)) != NULL) { 449 if (dp->d_name[0] == '.') 450 continue; 451 pt = strchr(dp->d_name, '.'); 452 if (pt && strcmp(pt, ".name") == 0) { 453 /* the pkgnmchk function works on .name extensions */ 454 if (pkgnmchk(dp->d_name, pkg, 1)) 455 continue; 456 (void) sprintf(path, "%s/%s", get_PKGOLD(), dp->d_name); 457 if (lstat64(path, &status)) 458 continue; 459 if ((status.st_mode & S_IFMT) != S_IFREG) 460 continue; 461 *pt = '\0'; 462 (void) strcpy(pkginst, dp->d_name); 463 return (pkginst); 464 } 465 } 466 (void) closedir(pdirfp); 467 pdirfp = NULL; 468 return (NULL); 469 } 470 471 static int 472 verscmp(char *request, char *actual) 473 { 474 /* eat leading white space */ 475 while (isspace((unsigned char)*actual)) 476 actual++; 477 while (isspace((unsigned char)*request)) 478 request++; 479 480 while (*request || *actual) { 481 /* 482 * Once the pointers don't match, return an error condition. 483 */ 484 485 if (*request++ != *actual++) 486 return (-1); 487 488 /* eat white space if any in both the strings */ 489 if (isspace((unsigned char)*request)) { 490 if (*actual && !isspace((unsigned char)*actual)) 491 return (-1); 492 while (isspace((unsigned char)*request)) 493 request++; 494 while (isspace((unsigned char)*actual)) 495 actual++; 496 } 497 } 498 499 return (0); 500 501 } 502 503 static int 504 compver(char *pkginst, char *version) 505 { 506 FILE *fp; 507 char temp[256]; 508 509 (void) sprintf(temp, "%s/%s/install/compver", get_PKGLOC(), pkginst); 510 if ((fp = fopen(temp, "r")) == NULL) 511 return (-1); 512 513 while (fgets(temp, 256, fp)) { 514 if (*temp == '#') 515 continue; 516 if (verscmp(temp, version) == 0) { 517 (void) fclose(fp); 518 return (0); 519 } 520 } 521 (void) fclose(fp); 522 return (-1); 523 } 524 525 static int 526 archcmp(char *arch, char *archlist) 527 { 528 char *pt; 529 530 if (arch == NULL) 531 return (0); 532 533 /* arch and archlist must not contain whitespace! */ 534 535 while (*archlist) { 536 for (pt = arch; *pt && (*pt == *archlist); ) 537 pt++, archlist++; 538 if (!*pt && (!*archlist || (*archlist == ','))) 539 return (0); 540 while (*archlist) { 541 if (*archlist++ == ',') 542 break; 543 } 544 } 545 return (-1); 546 } 547 548 static int 549 ckinfo(char *inst, char *arch, char *vers) 550 { 551 FILE *fp; 552 char temp[128]; 553 char file[PATH_MAX]; 554 char *pt, *copy, *value, *myarch, *myvers; 555 int errflg; 556 557 (void) sprintf(file, "%s/%s/pkginfo", pkgdir, inst); 558 if ((fp = fopen(file, "r")) == NULL) 559 return (1); 560 561 if ((arch == NULL) && (vers == NULL)) { 562 (void) fclose(fp); 563 return (0); 564 } 565 temp[0] = '\0'; 566 myarch = myvers = NULL; 567 while (value = fpkgparam(fp, temp)) { 568 if (strcmp(temp, "ARCH") == 0) { 569 /* remove all whitespace from value */ 570 pt = copy = value; 571 while (*pt) { 572 if (!isspace((unsigned char)*pt)) 573 *copy++ = *pt; 574 pt++; 575 } 576 *copy = '\0'; 577 myarch = value; 578 if (myvers) 579 break; 580 } else if (strcmp(temp, "VERSION") == 0) { 581 myvers = value; 582 if (myarch) 583 break; 584 } else 585 free(value); 586 temp[0] = '\0'; 587 } 588 (void) fclose(fp); 589 errflg = 0; 590 591 if (ckinst(inst, myarch, myvers, arch, vers)) 592 errflg++; 593 594 if (myarch) 595 free(myarch); 596 if (myvers) 597 free(myvers); 598 599 return (errflg); 600 } 601