521dad5888fde0e10237c9c6b741e071cd0c5cee angie Tue May 14 09:47:56 2019 -0700 hgSession: added CGI param hgS_sessionDataDbSuffix to randomize choice of customData db* when bulk-converting old saved sessions to use sessionDataDir/sessionDataDbPrefix. refs #22440 diff --git src/hg/hgSession/sessionData.c src/hg/hgSession/sessionData.c index 826697a..46469e6 100644 --- src/hg/hgSession/sessionData.c +++ src/hg/hgSession/sessionData.c @@ -181,50 +181,42 @@ else { // No new path -- trash file doesn't exist. Remove from retString to avoid inf loop. char *newString = replaceChars(*retString, encTrashPath, ""); freez(retString); *retString = newString; } freeMem(encTrashPath); freeMem(newPath); } if (urlEncoded) freeMem(trashDirPrefix); } } -static unsigned dayOfMonth() -/* Return the day of the month [1..31]. (Yeah, not [0..30]! See man 3 localtime.) */ -{ -time_t now = time(NULL); -struct tm *tm = localtime(&now); -return tm->tm_mday; -} - -static char *sessionDataDbTableName(char *tableName, char *sessionDataDbPrefix) +static char *sessionDataDbTableName(char *tableName, char *sessionDataDbPrefix, char *dbSuffix) /* Alloc and return a new table name that includes a db derived from sessionDataDbPrefix. */ { -struct dyString *dy = dyStringCreate("%s%02u.%s", sessionDataDbPrefix, dayOfMonth(), tableName); +struct dyString *dy = dyStringCreate("%s%s.%s", sessionDataDbPrefix, dbSuffix, tableName); return dyStringCannibalize(&dy); } -static char *saveTrashTable(char *tableName, char *sessionDataDbPrefix) +static char *saveTrashTable(char *tableName, char *sessionDataDbPrefix, char *dbSuffix) /* Move trash tableName out of customTrash to a sessionDataDbPrefix database, unless that * has been done already. Return the new database.table name. */ { -char *newDbTableName = sessionDataDbTableName(tableName, sessionDataDbPrefix); +char *newDbTableName = sessionDataDbTableName(tableName, sessionDataDbPrefix, dbSuffix); struct sqlConnection *conn = hAllocConn(CUSTOM_TRASH); if (! sqlTableExists(conn, newDbTableName)) { if (! sqlTableExists(conn, tableName)) errAbort("saveTrashTable: neither "CUSTOM_TRASH".%s nor %s exist", tableName, newDbTableName); struct dyString *dy = sqlDyStringCreate("rename table %s to %s", tableName, newDbTableName); sqlUpdate(conn, dy->string); dyStringFree(&dy); } else if (sqlTableExists(conn, tableName)) errAbort("saveTrashTable: both %s and %s exist", tableName, newDbTableName); hFreeConn(&conn); return newDbTableName; } @@ -270,121 +262,124 @@ { char *newPath = saveTrashFile(actualTrashPath, sessionDir); if (newPath) replaceColumnValue(conn, tableName, columnName, newPath); freeMem(newPath); } dyStringFree(&dy); freeMem(trashPath); break; } } hFreeConn(&conn); } } -static void saveDbTableName(char **retString, char *sessionDataDbPrefix, char *sessionDir) +static void saveDbTableName(char **retString, char *sessionDataDbPrefix, char *dbSuffix, + char *sessionDir) /* If sessionDataDbPrefix is given then scan for dbTableName setting; if found, move table and * update retString with new location. Also, if table contains a trash path and sessionDir * is given, then replace the old trash path in the table with the new sessionDir location. */ { char *prefix = "dbTableName"; if (sessionDataDbPrefix) { int prefixLen = strlen(prefix); char *setting = stringIn(prefix, *retString); if (setting && (setting[prefixLen] == '=' || setting[prefixLen] == ' ')) { char *start = setting + prefixLen + 1; char quote = *start; if (quote == '\'' || quote == '"') start++; else quote = '\0'; char *end = start; while (*end && ((quote && *end != quote) || (!quote && !isspace(*end)))) end++; if (stringIn(prefix, end)) errAbort("saveDbTableName: encountered two instances of '%s', expected 0 or 1", prefix); char *tableName = cloneStringZ(start, (end - start)); if (!startsWith(sessionDataDbPrefix, tableName)) { - char *newDbTableName = saveTrashTable(tableName, sessionDataDbPrefix); + char *newDbTableName = saveTrashTable(tableName, sessionDataDbPrefix, dbSuffix); updateSessionDataTablePaths(newDbTableName, sessionDir); char *newString = replaceChars(*retString, tableName, newDbTableName); freez(retString); *retString = newString; freeMem(newDbTableName); } freeMem(tableName); } } } static char *newCtTrashFile() /* Alloc and return the name of a new trash file to hold custom track metadata. */ { struct tempName tn; trashDirFile(&tn, "ct", CT_PREFIX, ".ctfile"); return cloneString(tn.forCgi); } static char *saveTrackFile(struct cart *cart, char *varName, char *oldFile, - char *sessionDataDbPrefix, char *sessionDir) + char *sessionDataDbPrefix, char *dbSuffix, char *sessionDir) /* oldFile contains custom track lines or track collection hub trackDb; scan for trashDir paths * and/or customTrash tables and move files and tables to safe locations per sessionDataDbPrefix and * sessionDir. If oldFile does not exist or has already been saved, return NULL. */ { char *newFile = NULL; if (fileExists(oldFile)) { if (isTrashPath(oldFile) && !maybeReadlink(oldFile)) { struct lineFile *lf = lineFileOpen(oldFile, TRUE); if (isNotEmpty(sessionDir)) newFile = sessionDataPathFromTrash(oldFile, sessionDir); else newFile = newCtTrashFile(); if (fileExists(newFile)) errAbort("saveTrackFile: new file '%s' already exists", newFile); makeDirsForFile(newFile); FILE *newF = mustOpen(newFile, "w"); char *line; while (lineFileNext(lf, &line, NULL)) { char *s = skipLeadingSpaces(line); if (*s != '\0' && *s != '#') { char *trackLine = cloneString(line); saveTrashPaths(&trackLine, sessionDir, FALSE); - saveDbTableName(&trackLine, sessionDataDbPrefix, sessionDir); + saveDbTableName(&trackLine, sessionDataDbPrefix, dbSuffix, sessionDir); fprintf(newF, "%s\n", trackLine); freeMem(trackLine); } else fprintf(newF, "%s\n", line); } carefulClose(&newF); + fprintf(stderr, "Wrote new file %s\n", newFile); if (isNotEmpty(sessionDir)) { if (unlink(oldFile) != 0) errnoAbort("saveTrackFile: unlink(oldFile='%s') failed", oldFile); if (symlink(newFile, oldFile) != 0) errnoAbort("saveTrackFile: symlink(newFile='%s', oldFile='%s') failed", newFile, oldFile); + fprintf(stderr, "symlinked %s to %s\n", oldFile, newFile); } cartSetString(cart, varName, newFile); } } else cartRemove(cart, varName); return newFile; } char *sessionDirFromNames(char *sessionDataDir, char *encUserName, char *encSessionName) /* Alloc and return session data directory: * sessionDataDir/2ByteHashOfEncUserName/encUserName/8ByteHashOfEncSessionName * 2ByteHashOfEncUserName spreads userName values across up to 256 subdirectories because * we have ~15000 distinct namedSessionDb.userName values in 2019. * 8ByteHashOfEncSessionName because session names can be very long. */ @@ -400,53 +395,66 @@ sessionHash[8] = '\0'; struct dyString *dy = dyStringCreate("%s/%s/%s/%s", sessionDataDir, userHash, encUserName, sessionHash); dir = dyStringCannibalize(&dy); freeMem(sessionHash); } return dir; } INLINE boolean cartVarIsCustomComposite(char *cartVar) /* Return TRUE if cartVar starts with "customComposite-". */ { return startsWith(customCompositeCartName "-", cartVar); } -void saveSessionData(struct cart *cart, char *encUserName, char *encSessionName) +static char *dayOfMonthString() +/* Return a two-character string with the current day of the month [01..31]. Do not free. + * (Yeah, not [0..30]! See man 3 localtime.) */ +{ +static char dayString[16]; +time_t now = time(NULL); +struct tm *tm = localtime(&now); +safef(dayString, sizeof dayString, "%02u", tm->tm_mday); +return dayString; +} + +void saveSessionData(struct cart *cart, char *encUserName, char *encSessionName, char *dbSuffix) /* If hg.conf specifies safe places to store files and/or tables that belong to user sessions, * then scan cart for trashDir files and/or customTrash tables, store them in safe locations, * and update cart to point to the new locations. */ { char *sessionDataDbPrefix = cfgOption("sessionDataDbPrefix"); char *sessionDataDir = cfgOption("sessionDataDir"); // Use (URL-encoded) userName and sessionName to make directory hierarchy under sessionDataDir char *sessionDir = sessionDirFromNames(sessionDataDir, encUserName, encSessionName); if (isNotEmpty(sessionDataDbPrefix) || isNotEmpty(sessionDir)) { + if (isNotEmpty(sessionDataDbPrefix) && dbSuffix == NULL) + dbSuffix = dayOfMonthString(); struct slPair *allVars = cartVarsLike(cart, "*"); struct slPair *var; for (var = allVars; var != NULL; var = var->next) { if (startsWith(CT_FILE_VAR_PREFIX, var->name) || cartVarIsCustomComposite(var->name)) { // val is file that contains references to trash files and customTrash db tables; // replace with new file containing references to saved files and tables. char *oldTrackFile = cloneString(var->val); char *newTrackFile = saveTrackFile(cart, var->name, var->val, - sessionDataDbPrefix, sessionDir); + sessionDataDbPrefix, dbSuffix, sessionDir); if (newTrackFile && cartVarIsCustomComposite(var->name)) cartReplaceHubVars(cart, var->name, oldTrackFile, newTrackFile); freeMem(oldTrackFile); freeMem(newTrackFile); } else { // Regular cart var; save trash paths (possibly encoded) in value, if any are found. char *newVal = cloneString(var->val); saveTrashPaths(&newVal, sessionDir, FALSE); saveTrashPaths(&newVal, sessionDir, TRUE); if (newVal != var->val && differentString(newVal, var->val)) cartSetString(cart, var->name, newVal); freeMem(newVal); }