diff options
Diffstat (limited to 'usr/src/lib/libadm/common/pkginfo.c')
-rw-r--r-- | usr/src/lib/libadm/common/pkginfo.c | 600 |
1 files changed, 600 insertions, 0 deletions
diff --git a/usr/src/lib/libadm/common/pkginfo.c b/usr/src/lib/libadm/common/pkginfo.c new file mode 100644 index 0000000000..f6d4e72ee4 --- /dev/null +++ b/usr/src/lib/libadm/common/pkginfo.c @@ -0,0 +1,600 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + + +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.2 */ +/*LINTLIBRARY*/ + +/* 5-20-92 added newroot functions */ + +#include <stdio.h> +#include <limits.h> +#include <stdarg.h> +#include <unistd.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <pkginfo.h> +#include <pkgstrct.h> +#include <pkglocs.h> +#include <errno.h> +#include "libadm.h" + +static void initpkg(struct pkginfo *); +static char *svr4inst(char *); +static int rdconfig(struct pkginfo *, char *, char *); +static int svr4info(struct pkginfo *, char *, char *); +static int ckinfo(char *, char *, char *); +static int ckinst(char *, char *, char *, char *, char *); +static int verscmp(char *, char *); +static int archcmp(char *, char *); +static int compver(char *, char *); + +/* + * Globals: + * pkgdir - specifies the directory where information about packages + * resides, i.e. the pkginfo file is located in a subdirectory + * + * Caveats: + * The structure provided via "info" will contain malloc'd information; + * this will be free'd upon the next call to pkginfo with this + * same structure. Application calls must make sure this structure + * is null on the first call, or else we'll free static memory areas + * If the "pkg" argument is a wildcard specification, the next found + * instance available which matches the request will be returned + * If the "pkg" argument is a NULL pointer, the structure pointed to + * via "info" will have its elements deallocated and all files + * associated with this routine will be closed + * + * Return codes: + * A non-zero exit code indicates error with "errno" appropriately set: + * EINVAL - invalid argument + * ESRCH - there are no more instances of this package around + * EACCESS - unable to access files which should have been there + */ + +/*VARARGS*/ +int +pkginfo(struct pkginfo *info, char *pkginst, ...) +{ + char *ckarch, *ckvers; + int check; + va_list ap; + + va_start(ap, pkginst); + if (info == NULL) { + errno = EINVAL; + return (-1); + } + if (pkginst == NULL) { + info->pkginst = NULL; + (void) fpkginfo(info, NULL); + (void) fpkginst(NULL); + return (0); + } + ckarch = va_arg(ap, char *); + ckvers = va_arg(ap, char *); + va_end(ap); + + check = 0; + if (pkgnmchk(pkginst, "all", 1)) { + /* wild card specification */ + pkginst = fpkginst(pkginst, ckarch, ckvers); + if (pkginst == NULL) + return (-1); + } else { + /* request to check indicated instance */ + if (ckarch || ckvers) + check++; + } + + info->pkginst = NULL; + if (fpkginfo(info, pkginst)) + return (-1); + + if (check) { + /* + * verify that the provided instance matches + * any arch & vers specs that were provided + */ + if (ckinst(pkginst, info->arch, info->version, ckarch, + ckvers)) { + errno = ESRCH; + return (-1); + } + } + return (0); +} +/*ARGSUSED*/ + +int +fpkginfo(struct pkginfo *info, char *pkginst) +{ + + if (info == NULL) { + errno = EINVAL; + return (-1); + } + + initpkg(info); + + if (pkginst == NULL) + return (0); + else if (pkgnmchk(pkginst, "all", 1)) { + errno = EINVAL; /* not an instance identifier */ + return (-1); + } + if (pkgdir == NULL) + pkgdir = get_PKGLOC(); + + if (rdconfig(info, pkginst, NULL)) { + initpkg(info); + return (-1); + } + return (0); +} + +static void +initpkg(struct pkginfo *info) +{ + /* free previously allocated space */ + if (info->pkginst) { + free(info->pkginst); + if (info->arch) + free(info->arch); + if (info->version) + free(info->version); + if (info->basedir) + free(info->basedir); + if (info->name) + free(info->name); + if (info->vendor) + free(info->vendor); + if (info->catg) + free(info->catg); + } + + info->pkginst = NULL; + info->arch = info->version = NULL; + info->basedir = info->name = NULL; + info->vendor = info->catg = NULL; + info->status = PI_UNKNOWN; +} + +static int +rdconfig(struct pkginfo *info, char *pkginst, char *ckvers) +{ + FILE *fp; + char temp[256]; + char *value, *pt, *copy, **memloc; + int count; + + if ((fp = pkginfopen(pkgdir, pkginst)) == NULL) { + if ((errno == ENOENT) && strcmp(pkgdir, get_PKGLOC()) == 0) + return (svr4info(info, pkginst, ckvers)); + + errno = EACCES; + return (-1); + } + + *temp = '\0'; + count = 0; + while (value = fpkgparam(fp, temp)) { + if (strcmp(temp, "ARCH") == 0 || + strcmp(temp, "CATEGORY") == 0) { + /* remove all whitespace from value */ + pt = copy = value; + while (*pt) { + if (!isspace((unsigned char)*pt)) + *copy++ = *pt; + pt++; + } + *copy = '\0'; + } + count++; + memloc = NULL; + if (strcmp(temp, "NAME") == 0) + memloc = &info->name; + else if (strcmp(temp, "VERSION") == 0) + memloc = &info->version; + else if (strcmp(temp, "ARCH") == 0) + memloc = &info->arch; + else if (strcmp(temp, "VENDOR") == 0) + memloc = &info->vendor; + else if (strcmp(temp, "BASEDIR") == 0) + memloc = &info->basedir; + else if (strcmp(temp, "CATEGORY") == 0) + memloc = &info->catg; + + temp[0] = '\0'; + if (memloc == NULL) + continue; /* not a parameter we're looking for */ + + *memloc = strdup(value); + if (!*memloc) { + (void) fclose(fp); + errno = ENOMEM; + return (-1); /* malloc from strdup failed */ + } + } + (void) fclose(fp); + + if (!count) { + errno = ESRCH; + return (-1); + } + + info->status = (strcmp(pkgdir, get_PKGLOC()) ? PI_SPOOLED : + PI_INSTALLED); + + if (info->status == PI_INSTALLED) { + (void) sprintf(temp, "%s/%s/!I-Lock!", pkgdir, pkginst); + if (access(temp, 0) == 0) + info->status = PI_PARTIAL; + else { + (void) sprintf(temp, "%s/%s/!R-Lock!", pkgdir, pkginst); + if (access(temp, 0) == 0) + info->status = PI_PARTIAL; + } + } + info->pkginst = strdup(pkginst); + return (0); +} + +static int +svr4info(struct pkginfo *info, char *pkginst, char *ckvers) +{ + static DIR *pdirfp; + struct stat64 status; + FILE *fp; + char *pt, path[128], line[128]; + char temp[PKGSIZ+1]; + + if (strcmp(pkginst, "all")) { + if (pdirfp) { + (void) closedir(pdirfp); + pdirfp = NULL; + } + /* determine pkginst - remove '.*' extension, if any */ + (void) strncpy(temp, pkginst, PKGSIZ); + if (((pt = strchr(temp, '.')) != NULL) && strcmp(pt, ".*") == 0) + *pt = '\0'; + } + + /* look in /usr/options direcotry for 'name' file */ + (void) sprintf(path, "%s/%s.name", get_PKGOLD(), temp); + if (lstat64(path, &status)) { + errno = (errno == ENOENT) ? ESRCH : EACCES; + return (-1); + } + if ((status.st_mode & S_IFMT) != S_IFREG) { + errno = ESRCH; + return (-1); + } + if ((fp = fopen(path, "r")) == NULL) { + errno = (errno == ENOENT) ? ESRCH : EACCES; + return (-1); + } + + /* /usr/options/xxx.name exists */ + (void) fgets(line, 128, fp); + (void) fclose(fp); + if (pt = strchr(line, '\n')) + *pt = '\0'; /* remove trailing newline */ + if (pt = strchr(line, ':')) + *pt++ = '\0'; /* assumed version specification */ + + if (info) { + info->name = strdup(line); + info->pkginst = strdup(temp); + if (!info->name || !info->pkginst) { + errno = ENOMEM; + return (-1); + } + info->status = PI_PRESVR4; + info->version = NULL; + } + + if (pt) { + /* eat leading space off of version spec */ + while (isspace((unsigned char)*pt)) + pt++; + } + if (ckvers && verscmp(ckvers, pt)) { + errno = ESRCH; + return (-1); + } + if (info && *pt) + info->version = strdup(pt); + return (0); +} + +static int +ckinst(char *pkginst, char *pkgarch, char *pkgvers, char *ckarch, char *ckvers) +{ + if (ckarch && archcmp(ckarch, pkgarch)) + return (-1); + if (ckvers) { + /* Check for exact version match */ + if (verscmp(ckvers, pkgvers)) { + /* Check for compatable version */ + if (compver(pkginst, ckvers)) + return (-1); + } + } + return (0); +} + +/*VARARGS*/ +char * +fpkginst(char *pkg, ...) +{ + static char pkginst[PKGSIZ+1]; + static DIR *pdirfp; + struct dirent64 *dp; + char *pt, *ckarch, *ckvers; + va_list ap; + + va_start(ap, pkg); + + if (pkg == NULL) { + /* request to close or rewind the file */ + if (pdirfp) { + (void) closedir(pdirfp); + pdirfp = NULL; + } + (void) svr4inst(NULL); /* close any files used here */ + return (NULL); + } + + ckarch = va_arg(ap, char *); + ckvers = va_arg(ap, char *); + va_end(ap); + + if (!pkgdir) + pkgdir = get_PKGLOC(); + + if (!pdirfp && ((pdirfp = opendir(pkgdir)) == NULL)) { + errno = EACCES; + return (NULL); + } + + while ((dp = readdir64(pdirfp)) != NULL) { + if (dp->d_name[0] == '.') + continue; + + if (pkgnmchk(dp->d_name, pkg, 0)) + continue; /* ignore invalid SVR4 package names */ + + if (ckinfo(dp->d_name, ckarch, ckvers)) + continue; + + /* + * Leave directory open in case user requests another + * instance. + */ + (void) strcpy(pkginst, dp->d_name); + return (pkginst); + } + + /* + * If we are searching the directory which contains info about + * installed packages, check the pre-svr4 directory for an instance + * and be sure it matches any version specification provided to us + */ + if (strcmp(pkgdir, get_PKGLOC()) == 0 && (ckarch == NULL)) { + /* search for pre-SVR4 instance */ + if (pt = svr4inst(pkg)) + return (pt); + } + errno = ESRCH; + /* close any file we might have open */ + (void) closedir(pdirfp); + pdirfp = NULL; + return (NULL); +} +/*ARGSUSED*/ + +static char * +svr4inst(char *pkg) +{ + static char pkginst[PKGSIZ]; + static DIR *pdirfp; + struct dirent64 *dp; + struct stat64 status; /* file status buffer */ + char *pt; + char path[PATH_MAX]; + + if (pkg == NULL) { + if (pdirfp) { + (void) closedir(pdirfp); + pdirfp = NULL; + } + return (NULL); + } + + if (!pdirfp && ((pdirfp = opendir(get_PKGOLD())) == NULL)) + return (NULL); + + while ((dp = readdir64(pdirfp)) != NULL) { + if (dp->d_name[0] == '.') + continue; + pt = strchr(dp->d_name, '.'); + if (pt && strcmp(pt, ".name") == 0) { + /* the pkgnmchk function works on .name extensions */ + if (pkgnmchk(dp->d_name, pkg, 1)) + continue; + (void) sprintf(path, "%s/%s", get_PKGOLD(), dp->d_name); + if (lstat64(path, &status)) + continue; + if ((status.st_mode & S_IFMT) != S_IFREG) + continue; + *pt = '\0'; + (void) strcpy(pkginst, dp->d_name); + return (pkginst); + } + } + (void) closedir(pdirfp); + pdirfp = NULL; + return (NULL); +} + +static int +verscmp(char *request, char *actual) +{ + /* eat leading white space */ + while (isspace((unsigned char)*actual)) + actual++; + while (isspace((unsigned char)*request)) + request++; + + while (*request || *actual) { + /* + * Once the pointers don't match, return an error condition. + */ + + if (*request++ != *actual++) + return (-1); + + /* eat white space if any in both the strings */ + if (isspace((unsigned char)*request)) { + if (*actual && !isspace((unsigned char)*actual)) + return (-1); + while (isspace((unsigned char)*request)) + request++; + while (isspace((unsigned char)*actual)) + actual++; + } + } + + return (0); + +} + +static int +compver(char *pkginst, char *version) +{ + FILE *fp; + char temp[256]; + + (void) sprintf(temp, "%s/%s/install/compver", get_PKGLOC(), pkginst); + if ((fp = fopen(temp, "r")) == NULL) + return (-1); + + while (fgets(temp, 256, fp)) { + if (*temp == '#') + continue; + if (verscmp(temp, version) == 0) { + (void) fclose(fp); + return (0); + } + } + (void) fclose(fp); + return (-1); +} + +static int +archcmp(char *arch, char *archlist) +{ + char *pt; + + if (arch == NULL) + return (0); + + /* arch and archlist must not contain whitespace! */ + + while (*archlist) { + for (pt = arch; *pt && (*pt == *archlist); ) + pt++, archlist++; + if (!*pt && (!*archlist || (*archlist == ','))) + return (0); + while (*archlist) { + if (*archlist++ == ',') + break; + } + } + return (-1); +} + +static int +ckinfo(char *inst, char *arch, char *vers) +{ + FILE *fp; + char temp[128]; + char file[PATH_MAX]; + char *pt, *copy, *value, *myarch, *myvers; + int errflg; + + (void) sprintf(file, "%s/%s/pkginfo", pkgdir, inst); + if ((fp = fopen(file, "r")) == NULL) + return (1); + + if ((arch == NULL) && (vers == NULL)) { + (void) fclose(fp); + return (0); + } + temp[0] = '\0'; + myarch = myvers = NULL; + while (value = fpkgparam(fp, temp)) { + if (strcmp(temp, "ARCH") == 0) { + /* remove all whitespace from value */ + pt = copy = value; + while (*pt) { + if (!isspace((unsigned char)*pt)) + *copy++ = *pt; + pt++; + } + *copy = '\0'; + myarch = value; + if (myvers) + break; + } else if (strcmp(temp, "VERSION") == 0) { + myvers = value; + if (myarch) + break; + } else + free(value); + temp[0] = '\0'; + } + (void) fclose(fp); + errflg = 0; + + if (ckinst(inst, myarch, myvers, arch, vers)) + errflg++; + + if (myarch) + free(myarch); + if (myvers) + free(myvers); + + return (errflg); +} |