/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern char *basedir, *root, *rootlist[], **environ; /* * IMPORTANT NOTE: PLEASE SEE THE DEFINITION OF temp[] BELOW BEFORE * CHANGING THE DEFINITION OF PATH_LGTH!!!! */ #define PATH_LGTH 4096 #define MAXPARAMS 256 #define NRECURS 20 #define MSG_BPARAMC "parametric class specification for <%s> not allowed" #define MSG_SRCHLOC "no object for <%s> found in local path" #define MSG_SRCHSRCH "no object for <%s> found in search path" #define MSG_SRCHROOT "no object for <%s> found in root directory" #define MSG_CONTENTS "unable to process contents of object <%s>" #define MSG_WRITE "write of entry failed, errno=%d" #define MSG_GARBDEFLT "garbled default settings: %s" #define MSG_BANG "unknown directive: %s" #define MSG_CHDIR "unable to change directory to <%s>" #define MSG_INCOMPLETE "processing of <%s> may be incomplete" #define MSG_NRECURS "too many levels of include (limit is %d)" #define MSG_RDINCLUDE "unable to process include file <%s>, errno=%d" #define MSG_IGNINCLUDE "ignoring include file <%s>" #define MSG_NODEVICE "device numbers cannot be determined for <%s>" #define WRN_BADATTR "WARNING: attributes set to %04o %s %s for <%s>" #define WRN_BADATTRM "WARNING: attributes set to %s %s %s for <%s>" #define WRN_FAKEBD "WARNING: parametric paths may ignore BASEDIR" #define ERR_TEMP "unable to obtain temporary file resources, errno=%d" #define ERR_ENVBUILD "unable to build parameter environment, errno=%d" #define ERR_MAXPARAMS "too many parameter definitions (limit is %d)" #define ERR_GETCWD "unable to get current directory, errno=%d" #define ERR_PATHVAR "cannot resolve all build parameters associated with " \ "path <%s>." static struct cfent entry; static FILE *fp, *sfp[20]; static char *dname[NRECURS], *params[256], *proto[NRECURS], *rootp[NRECURS][16], *srchp[NRECURS][16], *d_own[NRECURS], *d_grp[NRECURS], *rdonly[256]; static mode_t d_mod[NRECURS]; static int nfp = (-1); static int nrdonly = 0; static int errflg = 0; static char *separ = " \t\n, "; /* libpkg/gpkgmap.c */ extern void attrpreset(int mode, char *owner, char *group); extern void attrdefault(); static char *findfile(char *path, char *local); static char *srchroot(char *path, char *copy); static int popenv(void); static int doattrib(void); static void doinclude(void); static void dorsearch(void); static void dosearch(void); static void error(int flag); static void lputenv(char *s); static void pushenv(char *file); static void translate(register char *pt, register char *copy); int mkpkgmap(char *outfile, char *protofile, char **envparam) { FILE *tmpfp; char *pt, *path, mybuff[PATH_LGTH]; char **envsave; int c, fakebasedir; int i, n; /* * NOTE: THE SIZE OF temp IS HARD CODED INTO CALLS TO fscanf. * YOU *MUST* MAKE SURE TO CHANGE THOSE CALLS IF THE SIZE OF temp * IS EVER CHANGED!!!!!! */ char temp[PATH_LGTH]; if ((tmpfp = fopen(outfile, "w")) == NULL) { progerr(gettext(ERR_TEMP), errno); quit(99); } envsave = environ; environ = params; /* use only local environ */ attrdefault(); /* assume no default attributes */ /* * Environment parameters are optional, so variable * (envparam[i]) could be NULL. */ for (i = 0; (envparam[i] != NULL) && (pt = strchr(envparam[i], '=')); i++) { *pt = '\0'; rdonly[nrdonly++] = qstrdup(envparam[i]); *pt = '='; if (putenv(qstrdup(envparam[i]))) { /* bugid 1090920 */ progerr(gettext(ERR_ENVBUILD), errno); quit(99); } if (nrdonly >= MAXPARAMS) { progerr(gettext(ERR_MAXPARAMS), MAXPARAMS); quit(1); } } pushenv(protofile); errflg = 0; again: fakebasedir = 0; while (!feof(fp)) { c = getc(fp); while (isspace(c)) c = getc(fp); if (c == '#') { do c = getc(fp); while ((c != EOF) && (c != '\n')); continue; } if (c == EOF) break; if (c == '!') { /* * IMPORTANT NOTE: THE SIZE OF temp IS HARD CODED INTO * the FOLLOWING CALL TO fscanf -- YOU MUST CHANGE THIS * LINE IF THE SIZE OF fscanf IS EVER CHANGED!!! */ (void) fscanf(fp, "%4096s", temp); if (strcmp(temp, "include") == 0) doinclude(); else if (strcmp(temp, "rsearch") == 0) dorsearch(); else if (strcmp(temp, "search") == 0) dosearch(); else if (strcmp(temp, "default") == 0) { if (doattrib()) break; } else if (strchr(temp, '=')) { translate(temp, mybuff); /* put this into the local environment */ lputenv(mybuff); (void) fscanf(fp, "%*[^\n]"); /* rest of line */ (void) fscanf(fp, "\n"); /* rest of line */ } else { error(1); logerr(gettext(MSG_BANG), temp); (void) fscanf(fp, "%*[^\n]"); /* read of line */ (void) fscanf(fp, "\n"); /* read of line */ } continue; } (void) ungetc(c, fp); if ((n = gpkgmap(&entry, fp)) < 0) { char *errstr; error(1); errstr = getErrstr(); logerr(gettext("garbled entry")); logerr(gettext("- pathname: %s"), (entry.path && *entry.path) ? entry.path : "Unknown"); logerr(gettext("- problem: %s"), (errstr && *errstr) ? errstr : "Unknown"); break; } if (n == 0) break; /* done with file */ /* don't allow classname to be parametric */ if (entry.ftype != 'i') { if (entry.pkg_class[0] == '$') { error(1); logerr(gettext(MSG_BPARAMC), entry.path); } } if (strchr("dxlscbp", entry.ftype)) { /* * We don't need to search for things without any * contents in them. */ if (strchr("cb", entry.ftype)) { if (entry.ainfo.major == BADMAJOR || entry.ainfo.minor == BADMINOR) { error(1); logerr(gettext(MSG_NODEVICE), entry.path); } } path = NULL; } else { path = findfile(entry.path, entry.ainfo.local); if (!path) continue; entry.ainfo.local = path; if (strchr("fevin?", entry.ftype)) { if (cverify(0, &entry.ftype, path, &entry.cinfo, 1)) { error(1); logerr(gettext(MSG_CONTENTS), path); } } } /* Warn if attributes are not set correctly. */ if (!strchr("isl", entry.ftype)) { int dowarning = 0; int hasbadmode = 0; if (entry.ainfo.mode == NOMODE) { entry.ainfo.mode = CURMODE; dowarning = 1; hasbadmode = 1; } if (strcmp(entry.ainfo.owner, NOOWNER) == 0) { (void) strlcpy(entry.ainfo.owner, CUROWNER, sizeof (entry.ainfo.owner)); dowarning = 1; } if (strcmp(entry.ainfo.group, NOGROUP) == 0) { (void) strlcpy(entry.ainfo.group, CURGROUP, sizeof (entry.ainfo.group)); dowarning = 1; } if (dowarning) { if (hasbadmode) logerr(gettext(WRN_BADATTRM), "?", entry.ainfo.owner, entry.ainfo.group, entry.path); else logerr(gettext(WRN_BADATTR), (int)entry.ainfo.mode, entry.ainfo.owner, entry.ainfo.group, entry.path); } } /* * Resolve build parameters (initial lower case) in * the link and target paths. */ if (strchr("ls", entry.ftype)) { if (!RELATIVE(entry.ainfo.local) || PARAMETRIC(entry.ainfo.local)) { if (mappath(1, entry.ainfo.local)) { error(1); logerr(gettext(ERR_PATHVAR), entry.ainfo.local); break; } canonize(entry.ainfo.local); } } /* * Warn if top level file or directory is an install * parameter */ if (entry.ftype != 'i') { if (entry.path[0] == '$' && isupper(entry.path[1])) fakebasedir = 1; } if (mappath(1, entry.path)) { error(1); logerr(gettext(ERR_PATHVAR), entry.path); break; } canonize(entry.path); if (ppkgmap(&entry, tmpfp)) { error(1); logerr(gettext(MSG_WRITE), errno); break; } } if (fakebasedir) { logerr(gettext(WRN_FAKEBD)); fakebasedir = 0; } if (popenv()) goto again; (void) fclose(tmpfp); environ = envsave; /* restore environment */ return (errflg ? 1 : 0); } static char * findfile(char *path, char *local) { struct stat statbuf; static char host[PATH_MAX]; register char *pt; char temp[PATH_MAX], *basename; int i; /* * map any parameters specified in path to their corresponding values * and make sure the path is in its canonical form; any parmeters for * which a value is not defined will be left unexpanded. Since this * is an actual search for a real file (which will not end up in the * package) - we map ALL variables (both build and Install). */ (void) strlcpy(temp, (local && local[0] ? local : path), sizeof (temp)); mappath(0, temp); canonize(temp); *host = '\0'; if (rootlist[0] || (basedir && (*temp != '/'))) { /* * search for path in the pseudo-root/basedir directory; note * that package information files should NOT be included in * this list */ if (entry.ftype != 'i') return (srchroot(temp, host)); } /* looking for local object file */ if (local && *local) { basepath(temp, dname[nfp], NULL); /* * If it equals "/dev/null", that just means it's an empty * file. Otherwise, we'll really be writing stuff, so we need * to verify the source. */ if (strcmp(temp, "/dev/null") != 0) { if (stat(temp, &statbuf) || !(statbuf.st_mode & S_IFREG)) { error(1); logerr(gettext(MSG_SRCHLOC), path); return (NULL); } } (void) strlcpy(host, temp, sizeof (host)); return (host); } for (i = 0; rootp[nfp][i]; i++) { (void) snprintf(host, sizeof (host), "%s/%s", rootp[nfp][i], temp + (*temp == '/' ? 1 : 0)); if ((stat(host, &statbuf) == 0) && (statbuf.st_mode & S_IFREG)) { return (host); } } pt = strrchr(temp, '/'); if (!pt++) pt = temp; basename = pt; for (i = 0; srchp[nfp][i]; i++) { (void) snprintf(host, sizeof (host), "%s/%s", srchp[nfp][i], basename); if ((stat(host, &statbuf) == 0) && (statbuf.st_mode & S_IFREG)) { return (host); } } /* check current directory as a last resort */ (void) snprintf(host, sizeof (host), "%s/%s", dname[nfp], basename); if ((stat(host, &statbuf) == 0) && (statbuf.st_mode & S_IFREG)) return (host); error(1); logerr(gettext(MSG_SRCHSRCH), path); return (NULL); } static void dosearch(void) { char temp[PATH_MAX], lookpath[PATH_MAX], *pt; int n; (void) fgets(temp, PATH_MAX, fp); translate(temp, lookpath); for (n = 0; srchp[nfp][n]; n++) free(srchp[nfp][n]); n = 0; pt = strtok(lookpath, separ); if (pt && *pt) { do { if (*pt != '/') { /* make relative path an absolute directory */ (void) snprintf(temp, sizeof (temp), "%s/%s", dname[nfp], pt); pt = temp; } canonize(pt); srchp[nfp][n++] = qstrdup(pt); } while (pt = strtok(NULL, separ)); srchp[nfp][n] = NULL; } } static void dorsearch(void) { char temp[PATH_MAX], lookpath[PATH_MAX], *pt; int n; (void) fgets(temp, PATH_MAX, fp); translate(temp, lookpath); for (n = 0; rootp[nfp][n]; n++) free(rootp[nfp][n]); n = 0; pt = strtok(lookpath, separ); do { if (*pt != '/') { /* make relative path an absolute directory */ (void) snprintf(temp, sizeof (temp), "%s/%s", dname[nfp], pt); pt = temp; } canonize(pt); rootp[nfp][n++] = qstrdup(pt); } while (pt = strtok(NULL, separ)); rootp[nfp][n] = NULL; } /* * This function reads the default mode, owner and group from the prototype * file and makes that available. */ static int doattrib(void) { char *pt, attrib[PATH_MAX], *mode_ptr, *owner_ptr, *group_ptr, *eol; int mode; char owner[ATRSIZ+1], group[ATRSIZ+1], attrib_save[(4*ATRSIZ)]; (void) fgets(attrib, PATH_MAX, fp); (void) strlcpy(attrib_save, attrib, sizeof (attrib_save)); /* * Now resolve any variables that may be present. Start on group and * move backward since that keeps the resolved string from * overwriting any of the other entries. This is required since * mapvar() writes the resolved string over the string provided. */ mode_ptr = strtok(attrib, " \t"); owner_ptr = strtok(NULL, " \t"); group_ptr = strtok(NULL, " \t\n"); eol = strtok(NULL, " \t\n"); if (strtok(NULL, " \t\n")) { /* extra tokens on the line */ error(1); logerr(gettext(MSG_GARBDEFLT), (eol) ? eol : gettext("unreadable at end of line")); return (1); } if (group_ptr && mapvar(1, group_ptr) == 0) (void) strncpy(group, group_ptr, ATRSIZ); else { error(1); logerr(gettext(MSG_GARBDEFLT), (attrib_save) ? ((attrib_save[0]) ? attrib_save : gettext("none")) : gettext("unreadable at group")); return (1); } if (owner_ptr && mapvar(1, owner_ptr) == 0) (void) strncpy(owner, owner_ptr, ATRSIZ); else { error(1); logerr(gettext(MSG_GARBDEFLT), (attrib_save) ? ((attrib_save[0]) ? attrib_save : gettext("none")) : gettext("unreadable at owner")); return (1); } /* * For mode, don't use scanf, since we want to force an octal * interpretation and need to limit the length of the owner and group * specifications. */ if (mode_ptr && mapvar(1, mode_ptr) == 0) mode = strtol(mode_ptr, &pt, 8); else { error(1); logerr(gettext(MSG_GARBDEFLT), (attrib_save) ? ((attrib_save[0]) ? attrib_save : gettext("none")) : gettext("unreadable at mode")); return (1); } /* free any previous memory from qstrdup */ if (d_own[nfp]) free(d_own[nfp]); if (d_grp[nfp]) free(d_grp[nfp]); d_mod[nfp] = mode; d_own[nfp] = qstrdup(owner); d_grp[nfp] = qstrdup(group); attrpreset(d_mod[nfp], d_own[nfp], d_grp[nfp]); return (0); } static void doinclude(void) { char file[PATH_MAX]; char temp[PATH_MAX]; (void) fgets(temp, PATH_MAX, fp); /* * IMPORTANT NOTE: THE SIZE OF temp IS HARD CODED INTO THE * FOLLOWING CALL TO fscanf -- YOU MUST CHANGE THIS LINE IF * THE SIZE OF fscanf IS EVER CHANGED!!! */ (void) sscanf(temp, "%1024s", file); translate(file, temp); canonize(temp); if (*temp == '\0') return; else if (*temp != '/') (void) snprintf(file, sizeof (file), "%s/%s", dname[nfp], temp); else (void) strlcpy(file, temp, sizeof (file)); canonize(file); pushenv(file); } /* * This does what mappath() does except that it does it for ALL variables * using whitespace as a token separator. This is used to resolve search * paths and assignment statements. It doesn't effect the build versus * install decision made for pkgmap variables. */ static void translate(register char *pt, register char *copy) { char *pt2, varname[MAX_PKG_PARAM_LENGTH]; token: /* eat white space */ while (isspace(*pt)) pt++; while (*pt && !isspace(*pt)) { if (*pt == '$') { pt2 = varname; while (*++pt && !strchr("/= \t\n\r", *pt)) *pt2++ = *pt; *pt2 = '\0'; if (pt2 = getenv(varname)) { while (*pt2) *copy++ = *pt2++; } } else *copy++ = *pt++; } if (*pt) { *copy++ = ' '; goto token; } *copy = '\0'; } static void error(int flag) { static char *lasterr = NULL; if (lasterr != proto[nfp]) { lasterr = proto[nfp]; (void) fprintf(stderr, gettext("ERROR in %s:\n"), lasterr); } if (flag) errflg++; } /* Set up defaults and change to the build directory. */ static void pushenv(char *file) { register char *pt; static char topdir[PATH_MAX]; if ((nfp+1) >= NRECURS) { error(1); logerr(gettext(MSG_NRECURS), NRECURS); logerr(gettext(MSG_IGNINCLUDE), file); return; } if (strcmp(file, "-") == 0) { fp = stdin; } else if ((fp = fopen(file, "r")) == NULL) { error(1); logerr(gettext(MSG_RDINCLUDE), file, errno); if (nfp >= 0) { logerr(gettext(MSG_IGNINCLUDE), file); fp = sfp[nfp]; return; } else quit(1); } sfp[++nfp] = fp; srchp[nfp][0] = NULL; rootp[nfp][0] = NULL; d_mod[nfp] = (mode_t)(-1); d_own[nfp] = NULL; d_grp[nfp] = NULL; if (!nfp) { /* upper level proto file */ proto[nfp] = file; if (file[0] == '/') pt = strcpy(topdir, file); else { /* path is relative to the prototype file specified */ pt = getcwd(NULL, PATH_MAX); if (pt == NULL) { progerr(gettext(ERR_GETCWD), errno); quit(99); } (void) snprintf(topdir, sizeof (topdir), "%s/%s", pt, file); } if (pt = strrchr(topdir, '/')) *pt = '\0'; /* should always happen */ if (topdir[0] == '\0') (void) strlcpy(topdir, "/", sizeof (topdir)); dname[nfp] = topdir; } else { proto[nfp] = qstrdup(file); dname[nfp] = qstrdup(file); if (pt = strrchr(dname[nfp], '/')) *pt = '\0'; else { /* same directory as the last prototype */ free(dname[nfp]); dname[nfp] = qstrdup(dname[nfp-1]); return; /* no need to canonize() or chdir() */ } } canonize(dname[nfp]); if (chdir(dname[nfp])) { error(1); logerr(gettext(MSG_CHDIR), dname[nfp]); if (!nfp) quit(1); /* must be able to cd to upper level */ logerr(gettext(MSG_IGNINCLUDE), proto[nfp]); (void) popenv(); } } /* Restore defaults and return to the prior directory. */ static int popenv(void) { int i; (void) fclose(fp); if (nfp) { if (proto[nfp]) free(proto[nfp]); if (dname[nfp]) free(dname[nfp]); for (i = 0; srchp[nfp][i]; i++) free(srchp[nfp][i]); for (i = 0; rootp[nfp][i]; i++) free(rootp[nfp][i]); if (d_own[nfp]) free(d_own[nfp]); if (d_grp[nfp]) free(d_grp[nfp]); fp = sfp[--nfp]; if (chdir(dname[nfp])) { error(1); logerr(gettext(MSG_CHDIR), dname[nfp]); logerr(gettext(MSG_INCOMPLETE), proto[nfp]); return (popenv()); } return (1); } return (0); } /* * If this parameter isn't already in place, put it into the local * environment. This means that command line directives override prototype * file directives. */ static void lputenv(char *s) { char *pt; int i; pt = strchr(s, '='); if (!pt) return; *pt = '\0'; for (i = 0; i < nrdonly; i++) { if (strcmp(rdonly[i], s) == 0) { *pt = '='; return; } } *pt = '='; if (putenv(qstrdup(s))) { progerr(gettext(ERR_ENVBUILD), errno); quit(99); } } static char * srchroot(char *path, char *copy) { struct stat statbuf; int i; i = 0; root = rootlist[i++]; do { /* convert with root & basedir info */ cvtpath(path, copy); /* make it pretty again */ canonize(copy); if (stat(copy, &statbuf) || !(statbuf.st_mode & S_IFREG)) { root = rootlist[i++]; continue; /* host source must be a regular file */ } return (copy); } while (root != NULL); error(1); logerr(gettext(MSG_SRCHROOT), path); return (NULL); }