37d145c71807801efd9cb02926515c82e84c1c71 braney Wed Feb 7 10:12:23 2024 -0800 set up a mechanism to limit memory through an hg.conf variable diff --git src/hg/lib/hgConfig.c src/hg/lib/hgConfig.c index 8c0d1f9..6e041bc 100644 --- src/hg/lib/hgConfig.c +++ src/hg/lib/hgConfig.c @@ -1,347 +1,360 @@ /* Copyright (C) 2014 The Regents of the University of California * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */ #include <stdio.h> #include "common.h" #include "hgConfig.h" #include "hash.h" #include "cheapcgi.h" #include "portable.h" #include "linefile.h" #include "customTrack.h" #include <sys/types.h> #include <sys/stat.h> /* the file to read the global configuration info from */ #define GLOBAL_CONFIG_PATH "." #define GLOBAL_CONFIG_FILE "hg.conf" //#define GLOBAL_CONFIG_FILE "/usr/local/apache/cgi-bin/hg.conf" /* the file to read the user configuration info from, starting at the user's home */ #define USER_CONFIG_FILE ".hg.conf" // forwards static void parseConfigFile(char *filename, int depth); /* the hash holding the config options */ static struct hash* cfgOptionsHash = NULL; static boolean isBrowserCgi() /* test if this is a browser CGI */ { #ifndef GBROWSE /* this long complicated test is needed because, cgiSpoof may have already * been called thus we have to look a little deeper to seem if were are really * a cgi we do this looking for cgiSpoof in the QUERY_STRING, if it exists. * If not a cgi, read from home directory, e.g. ~/.hg.conf */ static boolean firstTime = TRUE; static boolean result = FALSE; if (firstTime) { result = ((cgiIsOnWeb()) && !((getenv("QUERY_STRING") != NULL) && (strstr(getenv("QUERY_STRING"), "cgiSpoof") != NULL))); firstTime = FALSE; } return result; #else return FALSE; #endif } static void checkConfigPerms(char *filename) /* Check that that config files starting with "." are only readable by the * user or don't exist. */ { char *p = strrchr(filename, '/'); if (p != NULL) p++; else p = filename; // no dir in path struct stat statBuf; if ((p[0] == '.') && (stat(filename, &statBuf) == 0)) { if ((statBuf.st_mode & (S_IRWXG|S_IRWXO)) != 0) errAbort("config file %s allows group or other access, must only allow user access", filename); } } static void getConfigFile(char filename[PATH_LEN]) /* get path to .hg.conf file to use */ { if (!isEmpty(getenv("HGDB_CONF"))) { // use environment variable strcpy(filename, getenv("HGDB_CONF")); } else if (isBrowserCgi()) { /* on the web, read from global config file */ safef(filename, PATH_LEN, "%s/%s", GLOBAL_CONFIG_PATH, GLOBAL_CONFIG_FILE); } else { /*use one in home */ safef(filename, PATH_LEN, "%s/%s", getenv("HOME"), USER_CONFIG_FILE); } } static void parseConfigInclude(struct lineFile *lf, int depth, char *line) /* open and parse an included config file */ { if (depth > 10) errAbort("maximum config include depth exceeded: %s:%d", lf->fileName, lf->lineIx); // parse out file name char line2[PATH_LEN+64]; safecpy(line2, sizeof(line2), line); // copy so we can make a useful error message char *p = line2; (void)nextWord(&p); char *relfile = nextWord(&p); if ((relfile == NULL) || (p != NULL)) errAbort("invalid format for config include: %s:%d: %s", lf->fileName, lf->lineIx, line); // construct relative to current file path, unless include // file is absolute char incfile[PATH_LEN]; safecpy(incfile, sizeof(incfile), lf->fileName); char *dirp = strrchr(incfile, '/'); if ((dirp != NULL) && (relfile[0] != '/')) { // construct relative path *(dirp+1) ='\0'; safecat(incfile, sizeof(incfile), relfile); } else { // use as-is safecpy(incfile, sizeof(incfile), relfile); } parseConfigFile(incfile, depth+1); } static void parseConfigDelete(struct lineFile *lf, char *line) /* parse and process a delete directive */ { char *p = line, *var; (void)nextWord(&p); // skip "delete" while ((var = nextWord(&p)) != NULL) hashRemove(cfgOptionsHash, var); } static void parseConfigLine(struct lineFile *lf, int depth, char *line) /* parse one non-comment, non-blank line of the config file. * If keyword=value, put in global hash, otherwise process * includes. */ { /* parse the key/value pair */ char *name = trimSpaces(line); if (startsWithWord("include", line)) parseConfigInclude(lf, depth, line); else if (startsWithWord("delete", line)) parseConfigDelete(lf, line); else { char *value = strchr(name, '='); if (value == NULL) errAbort("invalid format in config file %s:%d: %s", lf->fileName, lf->lineIx, line); *value++ = '\0'; name = trimSpaces(name); value = trimSpaces(value); hashAdd(cfgOptionsHash, name, cloneString(value)); /* Set environment variables to enable sql tracing and/or profiling */ if (sameString(name, "JKSQL_TRACE") || sameString(name, "JKSQL_PROF")) envUpdate(name, value); } } static void parseConfigFile(char *filename, int depth) /* open and parse a config file */ { checkConfigPerms(filename); struct lineFile *lf = lineFileMayOpen(filename, TRUE); if (lf == NULL) return; char *line; while(lineFileNext(lf, &line, NULL)) { // check for comment or blank line char *p = skipLeadingSpaces(line); if (!((p[0] == '#') || (p[0] == '\0'))) parseConfigLine(lf, depth, line); } lineFileClose(&lf); } static void addConfigIfUndef(char *prefix, char *suffix, char *value) /* if the specified config item doesn't exist, add it */ { char name[256]; safef(name, sizeof(name), "%s.%s", prefix, suffix); if (cfgOption(name) == NULL) hashAdd(cfgOptionsHash, name, cloneString(value)); } static boolean haveProfile(char *profileName) /* see if we appear to have the profile. Can't do this in * actual profile code, since it wants the config object that is being * hacked */ { return (cfgOption2(profileName, "host") != NULL) && (cfgOption2(profileName, "user") != NULL) && (cfgOption2(profileName, "password") != NULL); } static void hackConfigProfiles() /* Add in some pre-defined profile mappings if needed. This was added to make * older conf files compatible with the db profile paradigm */ { /* add mapping of customTrash database to profile if we have customTracks * profile defined */ if (haveProfile(CUSTOM_TRACKS_PROFILE)) addConfigIfUndef(CUSTOM_TRASH, "profile", CUSTOM_TRACKS_PROFILE); } static void initConfig() /* create and initilize the config hash */ { char filename[PATH_LEN]; cfgOptionsHash = newHash(6); getConfigFile(filename); parseConfigFile(filename, 0); hackConfigProfiles(); } char* cfgOption(char* name) /* Return the option with the given name. Return NULL if * it doesn't exist. */ { /* initilize the config hash if it is not */ if(cfgOptionsHash == NULL) initConfig(); return hashFindVal(cfgOptionsHash, name); } char *cfgOptionDefault(char* name, char* def) /* Return the option with the given name or the given default * if it doesn't exist. */ { char *val = cfgOption(name); if (val == NULL) val = def; return val; } boolean cfgOptionBooleanDefault(char* name, boolean def) /* return the boolean value for option with the given name or the given * default if it doesn't exist. Booleans of yes/no/on/off/true/false are * recognized */ { char *val = cfgOptionDefault(name, NULL); if (val == NULL) return def; if (sameString(val, "yes") || sameString(val, "on") || sameString(val, "true")) return TRUE; else if (sameString(val, "no") || sameString(val, "off")|| sameString(val, "false")) return FALSE; else { errAbort("hg.conf value for %s: \"%s\", expected one of yes, no, on, off, true, false", name, val); return FALSE; } } char *cfgOption2(char *prefix, char *suffix) /* Return the option with the given two-part name, formed from prefix.suffix. * Return NULL if it doesn't exist. */ { /* initilize the config hash if it is not */ if(cfgOptionsHash == NULL) initConfig(); char name[256]; safef(name, sizeof(name), "%s.%s", prefix, suffix); return hashFindVal(cfgOptionsHash, name); } char* cfgOptionDefault2(char *prefix, char *suffix, char* def) /* Return the option with the given two-part name, formed from prefix.suffix, * or the given default if it doesn't exist. */ { char *val = cfgOption2(prefix, suffix); if (val == NULL) val = def; return val; } char *cfgOptionEnv(char *envName, char* name) /* get a configuration optional value, from either the environment or the cfg * file, with the env take precedence. Return NULL if not found */ { char *val = getenv(envName); if (val == NULL || (strlen(val) == 0)) val = cfgOption(name); return val; } char *cfgOptionEnvDefault(char *envName, char* name, char *def) /* get a configuration optional value, from either the environment or the cfg * file, with the env take precedence. Return default if not found */ { char *val = cfgOptionEnv(envName, name); return (val == NULL) ? def : val; } char *cfgVal(char *name) /* Return option with given name. Squawk and die if it * doesn't exist. */ { char *val = cfgOption(name); if (val == NULL) errAbort("%s doesn't exist in hg.conf file", name); return val; } struct slName *cfgNames() /* get list of names in config file. slFreeList when finished */ { if(cfgOptionsHash == NULL) initConfig(); struct slName *names = NULL; struct hashCookie cookie = hashFirst(cfgOptionsHash); struct hashEl *hel; while ((hel = hashNext(&cookie)) != NULL) slSafeAddHead(&names, slNameNew(hel->name)); return names; } struct slName *cfgNamesWithPrefix(char* prefix) /* get list of names in config file that start with prefix. slFreeList when finished */ { if(cfgOptionsHash == NULL) initConfig(); struct slName *names = NULL; struct hashCookie cookie = hashFirst(cfgOptionsHash); struct hashEl *hel; while ((hel = hashNext(&cookie)) != NULL) { if (startsWith(prefix, hel->name)) slSafeAddHead(&names, slNameNew(hel->name)); } return names; } unsigned long cfgModTime() /* Return modification time of config file */ { char filename[PATH_LEN]; getConfigFile(filename); return fileModTime(filename); } + +void cfgSetMaxMem() +/* Check hg.conf for maxMem. If not set, don't limit memory. Otherwise + * limit memory usage to that number. */ +{ +char *maxMemStr = NULL; + +if ((maxMemStr = cfgOption("maxMem")) != NULL) + { + unsigned long maxMem = atol(maxMemStr); + setMemLimit(maxMem); + } +}