cdesktopenv/cde/lib/DtSvc/DtUtil2/DtEnvMap.c

1473 lines
39 KiB
C

/*
* CDE - Common Desktop Environment
*
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
*
* These libraries and programs are free software; you can
* redistribute them and/or modify them under the terms of the GNU
* Lesser General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* These libraries and programs are distributed in the hope that
* they will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with these librararies and programs; if not, write
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301 USA
*/
/*
* (c) Copyright 1993, 1994 Hewlett-Packard Company *
* (c) Copyright 1993, 1994 International Business Machines Corp. *
* (c) Copyright 1993, 1994 Sun Microsystems, Inc. *
* (c) Copyright 1993, 1994 Novell, Inc. *
*/
/******************************************************************************
******************************************************************************
**
** DtEnvMap.c
**
** $TOG: DtEnvMap.c /main/7 1998/07/30 12:11:59 mgreess $
**
** Map the path elements of environment strings as specified by yet
** another environment string from the local host to a remote host.
** When possible, cache the results since filename mapping is expensive.
**
** DTENVMAPFORREMOTE="NAME1[:NAME2[...]]"
**
** NAME1=path1:path2
** NAME2=path3:path4:...
**
******************************************************************************
*****************************************************************************/
#define X_INCLUDE_STRING_H
#define XOS_USE_XT_LOCKING
#include <X11/Xos_r.h>
#include <DtNlUtils.h>
#include <Tt/tt_c.h>
#include "DtSvcLock.h"
/******************************************************************************
******************************************************************************
**
** Data Structures and Defines.
**
**/
/**********************************************************
*
* What to map...
*
* ... is specified by either an environment variable or
* a global X resource. If present, the X resource acts
* globally, and is referred to on every attempt to map.
* If an environment variable is set, it overrides the X
* resource setting, thereby allowing for local control
* of individual process threads (by way of propagated
* environments).
*/
#define _DTENV_MAP_RESOURCE_NAME "dtEnvMapForRemote"
#define _DTENV_MAP_RESOURCE_CLASS "DtEnvMapForRemote"
#define _DTENV_MAP_ENV_VAR "DTENVMAPFORREMOTE"
/**********************************************************
*
* The primary caching/mapping data structures.
*/
typedef struct {
char *localEnvVarPtr; /* original ptr, used for restoring */
char *localEnvVarCopy; /* eg, /users/foo:/usr/bar */
char *mappedEnvVarPtr; /* eg, PATH=/nfs/.../foo:/nfs/.../bar */
} cachedEnvVar;
typedef struct {
char *remoteHost; /* host to map env vars to */
int cacheHit; /* usage rate for this mapping info */
char *mapListStr; /* copy of DTENVMAPFORREMOTE=... */
int mapListCnt; /* how many _real_ env vars to map */
char **mapList; /* simple list of env var names to map*/
cachedEnvVar *mapListDetails; /* list of map info per env var name */
} cacheForTargetHost;
/*
* Cache information. Keep mapping information for MAX_HOSTS_CACHED
* targetHosts.
*/
#define MAX_HOSTS_CACHED 8
static cacheForTargetHost cachePoolG[MAX_HOSTS_CACHED];
/*
* Track the most recent targetHost.
*/
static char *mostRecentRemoteHostG;
/**********************************************************
*
* Misc defines.
*
* To reduce malloc calls when creating lists, malloc
* "list" in blocks:
*
* char *list[0..15 , 16..31, 32..63, ...]
*/
#define MALLOC_BUMP_SIZE 16
#define freeAndNull(ptr) if (ptr) free(ptr); ptr = NULL;
#define ttfreeAndNull(ptr) if (ptr) tt_free(ptr); ptr = NULL;
/**********************************************************
*
* Functionality hooks.
*
* - _DTENV_SUPPORT_COMMA_SEPARATED - turn on code that
* special cases some environment variables and
* treats them as comma separated and possibly
* host qualified.
*
* - _DTENV_SUPPORT_MAPERROR_CACHING - turn on code that
* would cache error codes when mapping files in
* addition to caching successfully mapped files.
* In configurations where mapping errors are
* common, this could be a big help, but on the
* downside, a mapping error that is the result
* of a temporary network error (for example)
* could get locked into a cache.
*
* - _DTENV_OPTIMIZATION_LOCALHOST - turn on code that
* would prevent even trying to map/cache environment
* variables if the targetHost was the localhost.
*/
#define _DTENV_SUPPORT_COMMA_SEPARATED 1
#undef _DTENV_SUPPORT_MAPERROR_CACHING
#undef _DTENV_OPTIMIZATION_LOCALHOST
/**********************************************************
*
* Performance hooks.
*
* - DTENV_PERF_HOOK - instrunment file mapping code for
* performance measuring.
*/
#undef DTENV_PERF_HOOK
#ifdef DTENV_PERF_HOOK
#include <time.h>
int stopwatch_repeat_rate = 10;
unsigned long stopwatch_tt_file_netfile;
unsigned long stopwatch_tt_netfile_file;
#endif /* DTENV_PERF_HOOK */
/******************************************************************************
******************************************************************************
**
** _DtEnv_tt_file_netfile()
** _DtEnv_tt_netfile_file()
**
** Both are caching versions built on the non-caching Tooltalk versions.
**
** The one flaw in both is that if the filesystem topology changes
** (eg, NFS mounts, symlinks, etc), old and incorrect mapping information
** may be returned. To minimize this, a cached mapping will only be used
** so many times before being recomputed. An alternative (not implemented
** here) would be to recompute based on age of the mapping via
** gettimeofday().
**
** ******************* XXX Alert **************************
**
** Tooltalk currently allocates a range of char*'s
** and then associates them with a range of error
** messages (ala tt_ptr_error()). In effect, the
** allocated char*'s become constants within Tooltalk.
**
** This code takes this into account when returning
** values. Non-reserved char*'s are strdup()ed
** often, while reserved char*'s are not to preserve
** their ptr value.
**
** tjg: Other than differing cache repositories, all the onion-skin
** routines below look the same. Probably better to have one
** core routine to minimize code size.
**/
/*
* Per cache setup (ie, one for _DtEnv_tt_file_netfile(), one for
* _DtEnv_tt_netfile_file()), start off by caching SIZE_START
* mappings, growing by SIZE_BUMP as needed, while limiting the
* total cache growth to SIZE_MAX mappings.
*/
#define CACHE_FILEFRAG_SIZE_START 15
#define CACHE_FILEFRAG_SIZE_BUMP 15
#define CACHE_FILEFRAG_SIZE_MAX 45
/*
* After a cached mapping is used REMAP_AFTER times, age it out
* and force a recomputation of the mapping from scratch.
*/
#define CACHE_FILEFRAG_REMAP_AFTER 25
/*
* After RESET_PRI hits on a cache of size SIZE_MAX, reset all
* cacheHit counters. This will allow new mappings to work into
* the cache against mappings that might have been popular long
* ago.
*/
#define CACHE_FILEFRAG_RESET_PRI 200
/******************************************************************************
*
* _DtEnv_tt_file_netfile()
*
* A caching version of tt_file_netfile().
*/
typedef struct {
char *pathFragOrig; /* eg, /usr/dt */
char *pathFragMapped; /* eg, <from tt_file_netfile> */
int cacheHit; /* usage of */
} cachedFileFrag;
char *_DtEnv_tt_file_netfile(
const char *filename)
{
static int first_time = 1;
static int fragListAvail = CACHE_FILEFRAG_SIZE_START;
static int fragListCnt = 0;
static cachedFileFrag *fragList;
static int cacheGen = 0;
static int hitIdxStart = 0;
char *netpath;
int hitval, hitIdx, i;
cachedFileFrag *tmpCffP;
char *tmpStr;
int newCount = fragListCnt;
_DtSvcProcessLock();
if (first_time) {
fragList = (cachedFileFrag *) calloc( fragListAvail,
sizeof(cachedFileFrag) );
first_time = 0;
}
/*
* Take care of the obvious.
*/
if (!filename) {
_DtSvcProcessUnlock();
return( (char *) NULL );
}
/*
* Look for existing answer in cache.
*
* While at it, also look for least used entry just in case.
*/
if (fragListCnt)
hitIdxStart = (hitIdxStart + 7) % fragListCnt;
else
hitIdxStart = 0;
hitIdx = hitIdxStart;
hitval = fragList[hitIdx].cacheHit;
tmpCffP = fragList; /* walk rather than index */
for ( i = 0; i < fragListCnt; i++ ) {
if (tmpCffP->cacheHit && !strcmp( filename, tmpCffP->pathFragOrig ) ) {
break;
}
if (tmpCffP->cacheHit < hitval) {
hitIdx = i;
hitval = tmpCffP->cacheHit;
}
tmpCffP++;
}
/*
* Decide what was found.
*/
if ( i != fragListCnt ) {
/*
* Found a cached entry.
*/
hitIdx = i;
if ( fragList[hitIdx].cacheHit++ > CACHE_FILEFRAG_REMAP_AFTER ) {
/*
* This looks like an old entry, so re-compute it.
*/
freeAndNull( fragList[hitIdx].pathFragOrig );
ttfreeAndNull( fragList[hitIdx].pathFragMapped );
fragList[hitIdx].cacheHit = 0; /* 0 means remap below */
}
}
else {
/*
* Did not find a cache entry, so scrounge around for
* a new entry.
*/
if ( fragListCnt < fragListAvail ) {
/*
* Use next already-malloc'ed cacheEntry.
*/
hitIdx = fragListCnt;
newCount = fragListCnt + 1;
}
else if ( fragListCnt < CACHE_FILEFRAG_SIZE_MAX ) {
/*
* Can grow fragList[]
*/
fragListAvail += CACHE_FILEFRAG_SIZE_BUMP;
fragList = (cachedFileFrag *) realloc( (char *) fragList,
sizeof(cachedFileFrag) * fragListAvail);
/*
* Zero out new memory.
*/
memset( fragList + (fragListAvail-CACHE_FILEFRAG_SIZE_BUMP),
0, CACHE_FILEFRAG_SIZE_BUMP*sizeof(cachedFileFrag) );
hitIdx = fragListCnt;
newCount = fragListCnt + 1;
}
else {
/*
* Last resort - bump out the least used entry.
*/
freeAndNull( fragList[hitIdx].pathFragOrig );
ttfreeAndNull( fragList[hitIdx].pathFragMapped );
/*
* Since the cache is 100% full, ocassionally reset
* everyone's cacheHit rate so entries that were only
* popular long ago don't get locked in.
*/
if ( cacheGen++ > CACHE_FILEFRAG_RESET_PRI ) {
cacheGen = 0;
tmpCffP = fragList;
for ( i = 0; i < fragListCnt; i++ ) {
tmpCffP->cacheHit = 1;
tmpCffP++;
}
}
}
fragList[hitIdx].cacheHit = 0; /* 0 means remap below */
}
if ( ! fragList[hitIdx].cacheHit ) {
/*
* Need to perform mapping.
*/
netpath = tt_file_netfile( filename );
#ifdef _DTENV_SUPPORT_MAPERROR_CACHING
fragList[hitIdx].pathFragOrig = strdup( filename );
fragList[hitIdx].cacheHit = 1;
fragList[hitIdx].pathFragMapped = netpath;
fragListCnt = newCount;
#else
if ( tt_ptr_error(netpath) == TT_OK ) {
fragList[hitIdx].pathFragOrig = strdup( filename );
fragList[hitIdx].cacheHit = 1;
fragList[hitIdx].pathFragMapped = netpath;
/*
* Only change the count if we are successful in adding
* a new entry.
*/
fragListCnt = newCount;
}
else {
/*
* Don't cache errors. Leave this cache slot empty
* and it will be rediscovered and used in the future.
*/
fragList[hitIdx].cacheHit = 0;
/*
* Do not change the fragListCount since we don't want to
* add in error entries.
*/
}
#endif /* _DTENV_SUPPORT_MAPERROR_CACHING */
}
/*
* Dig out answer and return it.
*/
#ifdef _DTENV_SUPPORT_MAPERROR_CACHING
if ( tt_ptr_error(netpath) == TT_OK )
#else
if ( fragList[hitIdx].cacheHit )
#endif /* _DTENV_SUPPORT_MAPERROR_CACHING */
{
/*
* Return a tt_free-able copy of the answer.
*/
tmpStr = tt_malloc( strlen(fragList[hitIdx].pathFragMapped) + 1 );
strcpy( tmpStr, fragList[hitIdx].pathFragMapped );
_DtSvcProcessUnlock();
return(tmpStr);
}
else {
/*
* See XXX comment.
*
* Since netpath is an error code, return as is.
*/
_DtSvcProcessUnlock();
return(netpath);
}
}
/******************************************************************************
*
* _DtEnv_tt_netfile_file()
*
* A caching version of tt_netfile_file().
*/
typedef struct {
char *targetHost;
char *pathFragOrig; /* eg, <from tt_file_netfile> */
char *pathFragMapped; /* eg, /nfs/hostb/usr/dt */
int cacheHit; /* usage of */
} cachedNetfileFrag;
char *_DtEnv_tt_host_netfile_file(
const char *host,
const char *filename)
{
static int first_time = 1;
static int fragListAvail = CACHE_FILEFRAG_SIZE_START;
static int fragListCnt = 0;
static cachedNetfileFrag *fragList;
static int cacheGen = 0;
static int hitIdxStart = 0;
char *newfile;
int hitval, hitIdx, i;
cachedNetfileFrag *tmpCffP;
char *tmpStr;
int newCount = fragListCnt;
_DtSvcProcessLock();
if (first_time) {
fragList = (cachedNetfileFrag *) calloc( fragListAvail,
sizeof(cachedNetfileFrag) );
first_time = 0;
}
/*
* Take care of the obvious.
*/
if (!filename) {
_DtSvcProcessUnlock();
return( (char *) NULL );
}
if (!host) {
/*
* Return a tt_free-able un-mapped copy.
*/
tmpStr = tt_malloc( strlen(filename) + 1 );
strcpy( tmpStr, filename );
_DtSvcProcessUnlock();
return(tmpStr);
}
/*
* Look for existing answer in cache.
*
* While at it, also look for least used entry just in case.
*/
if (fragListCnt)
hitIdxStart = (hitIdxStart + 7) % fragListCnt;
else
hitIdxStart = 0;
hitIdx = hitIdxStart;
hitval = fragList[hitIdx].cacheHit;
tmpCffP = fragList; /* walk rather than index */
for ( i = 0; i < fragListCnt; i++ ) {
if (tmpCffP->cacheHit && !strcmp( filename, tmpCffP->pathFragOrig ) ) {
if (!strcmp( host, tmpCffP->targetHost ) ) {
break;
}
}
/*
* Save index of least used entry
*/
if (tmpCffP->cacheHit < hitval) {
hitIdx = i;
hitval = tmpCffP->cacheHit;
}
tmpCffP++;
}
/*
* Decide what was found.
*/
if ( i != fragListCnt ) {
/*
* Found a cached entry.
*/
hitIdx = i;
if ( fragList[hitIdx].cacheHit++ > CACHE_FILEFRAG_REMAP_AFTER ) {
/*
* This looks like an old entry, so re-compute it.
*/
freeAndNull( fragList[hitIdx].targetHost );
freeAndNull( fragList[hitIdx].pathFragOrig );
ttfreeAndNull( fragList[hitIdx].pathFragMapped );
fragList[hitIdx].cacheHit = 0; /* 0 means remap below */
}
}
else {
/*
* Did not find a cache entry, so scrounge around for
* a new entry.
*/
if ( fragListCnt < fragListAvail ) {
/*
* Use next already-malloc'ed cacheEntry.
*/
hitIdx = fragListCnt;
newCount = fragListCnt + 1;
}
else if ( fragListCnt < CACHE_FILEFRAG_SIZE_MAX ) {
/*
* Can grow fragList[]
*/
fragListAvail += CACHE_FILEFRAG_SIZE_BUMP;
fragList = (cachedNetfileFrag *) realloc( (char *) fragList,
sizeof(cachedNetfileFrag) * fragListAvail);
/*
* Zero out new memory.
*/
memset( fragList + (fragListAvail-CACHE_FILEFRAG_SIZE_BUMP),
0, CACHE_FILEFRAG_SIZE_BUMP*sizeof(cachedNetfileFrag) );
hitIdx = fragListCnt;
newCount = fragListCnt + 1;
}
else {
/*
* Last resort - bump out the least used entry.
*/
freeAndNull( fragList[hitIdx].targetHost );
freeAndNull( fragList[hitIdx].pathFragOrig );
ttfreeAndNull( fragList[hitIdx].pathFragMapped );
/*
* Since the cache is 100% full, ocassionally reset
* everyone's cacheHit rate so entries that were only
* popular long ago don't get locked in.
*/
if ( cacheGen++ > CACHE_FILEFRAG_RESET_PRI ) {
cacheGen = 0;
tmpCffP = fragList;
for ( i = 0; i < fragListCnt; i++ ) {
tmpCffP->cacheHit = 1;
tmpCffP++;
}
}
}
fragList[hitIdx].cacheHit = 0; /* 0 means remap below */
}
if ( ! fragList[hitIdx].cacheHit ) {
/*
* Need to perform mapping.
*/
newfile = tt_host_netfile_file( host, filename );
#ifdef _DTENV_SUPPORT_MAPERROR_CACHING
fragList[hitIdx].targetHost = strdup ( host );
fragList[hitIdx].pathFragOrig = strdup( filename );
fragList[hitIdx].cacheHit = 1;
fragList[hitIdx].pathFragMapped = newfile;
fragListCnt = newCount;
#else
if ( tt_ptr_error(newfile) == TT_OK ) {
fragList[hitIdx].targetHost = strdup ( host );
fragList[hitIdx].pathFragOrig = strdup( filename );
fragList[hitIdx].cacheHit = 1;
fragList[hitIdx].pathFragMapped = newfile;
/*
* Only change the count if we are successful in adding
* a new entry.
*/
fragListCnt = newCount;
}
else {
/*
* Don't cache errors. Leave this cache slot empty
* and it will be rediscovered and used in the future.
*/
fragList[hitIdx].cacheHit = 0;
/*
* Do not change the fragListCount since we are not saving
* error entries.
*/
}
#endif /* _DTENV_SUPPORT_MAPERROR_CACHING */
}
/*
* Dig out answer and return it.
*/
#ifdef _DTENV_SUPPORT_MAPERROR_CACHING
if ( tt_ptr_error(newfile) == TT_OK )
#else
if ( fragList[hitIdx].cacheHit )
#endif /* _DTENV_SUPPORT_MAPERROR_CACHING */
{
/*
* Return a tt_free-able copy of the answer.
*/
tmpStr = tt_malloc( strlen(fragList[hitIdx].pathFragMapped) + 1 );
strcpy( tmpStr, fragList[hitIdx].pathFragMapped );
_DtSvcProcessUnlock();
return(tmpStr);
}
else {
/*
* See XXX comment.
*
* Since newfile is an error code, return as is.
*/
_DtSvcProcessUnlock();
return(newfile);
}
}
/******************************************************************************
******************************************************************************
**
** Environment variable mapping code.
**
*/
/******************************************************************************
*
* _DtEnvGetMapList()
*
* Fetch the environment variable who's value is a colon separated list
* of environment variable names who's values need to be mapped. Then
* break down the list into an indexable array.
*/
static char **_DtEnvGetMapList(
char *mapListStr,
int *mapListCount)
{
char **mapList; /* decomposition of above */
char *tmpPtr, *tmpPtr2;
int availListSize;
int firstTime;
_Xstrtokparams strtok_buf;
/*
* Handle NULL mapListStr.
*/
*mapListCount = 0;
if ( !mapListStr ) {
return( (char **) NULL );
}
/*
* Create a block of string pointers - remalloc() as needed.
*/
availListSize = MALLOC_BUMP_SIZE;
mapList = (char **) malloc( sizeof(char *) * (availListSize) );
/*
* Break up the colon seperated string into an indexable array.
*/
tmpPtr = strdup(mapListStr); /* work copy for strtok */
firstTime = 1;
while (1) {
if (firstTime) {
tmpPtr2 = _XStrtok( tmpPtr, ":", strtok_buf );
firstTime = 0;
}
else
tmpPtr2 = _XStrtok( (char *) NULL, ":", strtok_buf );
if (tmpPtr2) {
/*
* Have possible env var name to map - make sure it exists.
*/
if ( getenv(tmpPtr2) ) {
(*mapListCount)++;
if (*mapListCount > availListSize) {
availListSize += MALLOC_BUMP_SIZE;
mapList = (char **) realloc( (char *) mapList,
sizeof(char *) * (availListSize) );
}
mapList[*mapListCount-1] = strdup(tmpPtr2);
}
}
else {
break;
}
}
free(tmpPtr);
return( (char **) mapList );
}
/******************************************************************************
*
* _DtEnvCleanCacheSlot()
*
* Free up all memory associated with a cache slot for a targetHost.
*/
static void _DtEnvCleanCacheSlot( cacheForTargetHost *targetCache )
{
int i;
freeAndNull( targetCache->remoteHost );
targetCache->cacheHit = 1;
freeAndNull( targetCache->mapListStr );
for ( i = 0; i < targetCache->mapListCnt; i++ ) {
freeAndNull( targetCache->mapList[i] );
freeAndNull( targetCache->mapListDetails[i].localEnvVarCopy );
freeAndNull( targetCache->mapListDetails[i].mappedEnvVarPtr );
/* do not free .localEnvVarPtr - belongs to environ */
}
freeAndNull( targetCache->mapList );
targetCache->mapListCnt = 0;
}
/******************************************************************************
*
* _DtEnvGetTargetCache()
*
* For a specified targetHost, find existing cache information and
* optionally create a cache for a targetHost if one doesn't exist.
*
* The define MAX_HOSTS_CACHED controls how many targetHosts can
* be cached.
*/
static cacheForTargetHost *_DtEnvGetTargetCache(
char *targetHost,
int createIfNeeded)
{
static int cacheHitGen = 0;
static int hitIdxStart;
int i, hitidx, hitval;
/*
* Handle obvious.
*/
if (!targetHost) {
return( (cacheForTargetHost *) NULL );
}
_DtSvcProcessLock();
/*
* Look for targetHost in current cache pool.
*/
for ( i = 0; i < MAX_HOSTS_CACHED; i++ ) {
if ( cachePoolG[i].remoteHost ) {
if ( !strcmp( targetHost, cachePoolG[i].remoteHost ) ) {
cachePoolG[i].cacheHit++;
break;
}
}
}
if ( i != MAX_HOSTS_CACHED ) {
/*
* targetHost is in a cache slot already.
*/
_DtSvcProcessUnlock();
return( &cachePoolG[i] );
}
else if ( !createIfNeeded ) {
/*
* No cache slot for, and we shouldn't create one either.
*/
_DtSvcProcessUnlock();
return( (cacheForTargetHost *) NULL );
}
else {
/*
* Find an empty cache slot or take over a rarely used slot.
*/
hitIdxStart = (hitIdxStart + 7) % MAX_HOSTS_CACHED;
hitidx = hitIdxStart;
hitval = cachePoolG[hitidx].cacheHit;
for ( i = 0; i < MAX_HOSTS_CACHED; i++ ) {
if ( ! cachePoolG[i].remoteHost ) {
/*
* Empty slot - take it.
*/
hitidx = i;
break;
}
else if ( cachePoolG[i].cacheHit < hitval ) {
hitidx = i;
hitval = cachePoolG[i].cacheHit;
}
}
if ( cachePoolG[hitidx].remoteHost ) {
/*
* Cache was in use, clean first.
*/
_DtEnvCleanCacheSlot( &cachePoolG[hitidx] );
cachePoolG[hitidx].remoteHost = strdup( targetHost );
/*
* Since all the slots are full, occasionally reset everyones
* cacheHit counters. This gives new targetHosts a chance
* to compete with targetHosts that were popular long ago.
*/
if ( cacheHitGen++ > 50 ) {
for ( i = 0; i < MAX_HOSTS_CACHED; i++ ) {
cachePoolG[i].cacheHit = 1;
}
cacheHitGen = 1;
}
}
_DtSvcProcessUnlock();
return( &cachePoolG[hitidx] );
}
}
/******************************************************************************
*
* _DtEnvMapIt()
*
* Fill out a map cache for a single environment variable.
*/
static void _DtEnvMapIt(
char *envVar,
cachedEnvVar *envVarCache,
char *targetHost)
{
char *separator, *tmpPtr, *tmpPtr2, swapout, *netpath;
char *prePend, *postPend, *newPrePend;
char **pathList;
int availPathListSize, pathListCount, availEnvStrSize, len, tmpi, i;
int considerMapping;
_Xstrtokparams strtok_buf;
/*
* Information Layout:
*
* localEnvVarPtr = ptr to original "PATH=/users/foo:/users/bar"
* localEnvVarCopy = copy of original "/users/foo:/users/bar"
* mappedEnvVarPtr = mapped "PATH=/nfs/.../users/foo:/nfs/.../users/bar"
*/
if ( (envVarCache->localEnvVarPtr = getenv( envVar )) ) {
envVarCache->localEnvVarCopy = strdup( envVarCache->localEnvVarPtr );
/* sneak back past "NAME=" portion. */
envVarCache->localEnvVarPtr -= strlen( envVar ) + 1;
}
else {
/*
* Nothing to map. Punt.
*/
envVarCache->localEnvVarCopy = (char *) NULL;
envVarCache->localEnvVarPtr = (char *) NULL;
return;
}
#ifdef _DTENV_SUPPORT_COMMA_SEPARATED
/*
* Pick between colon-separated and comma-separated host-qualified
* mapping code.
*/
if ( !strcmp(envVar, "DTDATABASESEARCHPATH") ) {
/*
* comma-separated and host-qualified mapping.
*/
separator = ",";
}
else {
/*
* colon-separated mapping.
*/
separator = ":";
}
#else
separator = ":";
#endif /* _DTENV_SUPPORT_COMMA_SEPARATED */
/*
* Break path list into elements
*/
availPathListSize = MALLOC_BUMP_SIZE;
pathListCount = 0;
pathList = (char **) malloc( sizeof(char *) * availPathListSize );
/*
* Break up path list into an array of path elements.
*/
tmpPtr = strdup( envVarCache->localEnvVarCopy ); /* work copy */
while (1) {
if (!pathListCount)
tmpPtr2 = _XStrtok( tmpPtr, separator, strtok_buf );
else
tmpPtr2 = _XStrtok( (char *) NULL, separator, strtok_buf );
if (tmpPtr2) {
pathListCount++;
if (pathListCount > availPathListSize) {
availPathListSize += MALLOC_BUMP_SIZE;
pathList = (char **) realloc( (char *) pathList,
sizeof(char *) * availPathListSize );
}
pathList[pathListCount-1] = strdup( tmpPtr2 );
}
else {
break;
}
}
free( tmpPtr );
/*
* Setup new "NAME=....." string.
*/
availEnvStrSize = strlen( envVar ) + 64;
envVarCache->mappedEnvVarPtr = (char *) calloc( availEnvStrSize, sizeof(char) );
strcpy( envVarCache->mappedEnvVarPtr, envVar );
strcat( envVarCache->mappedEnvVarPtr, "=" );
/*
* Start mapping each path element.
*/
for ( i = 0; i < pathListCount; i++ ) {
prePend = pathList[i];
postPend = (char *) NULL;
newPrePend = (char *) NULL;
/*
* Assume we need to map this path element.
*/
considerMapping = 1;
#ifdef _DTENV_SUPPORT_COMMA_SEPARATED
if ( !strcmp( separator, "," ) ) {
if ( DtStrchr(prePend, ':' ) ) {
/*
* Host qualified elements in a comma separated list
* will NOT be mapped.
*/
considerMapping = 0;
}
}
#endif /* _DTENV_SUPPORT_COMMA_SEPARATED */
if (considerMapping) {
/*
* Tear apart and check for so called substitution characters.
*/
if ( tmpPtr = DtStrchr(prePend, '%') ) {
/*
* Temporarly shorten path up to substitution character.
*/
swapout = *tmpPtr;
*tmpPtr = '\0';
/*
* Move the dividing point back to a directory element.
*/
tmpPtr2 = DtStrrchr( prePend, '/' );
/*
* Restore the send half of the string.
*/
*tmpPtr = swapout;
if (tmpPtr2) {
/*
* Can do a split around the "/".
*
* Will have "<prePath>/" and "/<postPath>".
*/
postPend = strdup( tmpPtr2 );
*(tmpPtr2 + mblen(tmpPtr2, MB_CUR_MAX)) = '\0';
}
}
#ifdef DTENV_PERF_HOOK
{
int tpi;
extern unsigned long stopwatch_tt_file_netfile;
extern int stopwatch_repeat_rate;
struct timeval start, stop;
struct timezone junk;
gettimeofday( &start, &junk );
for ( tpi = 0; tpi < stopwatch_repeat_rate-1; tpi++ ) {
netpath = _DtEnv_tt_file_netfile( prePend );
if ( tt_ptr_error(netpath) == TT_OK )
ttfreeAndNull( netpath );
}
netpath = _DtEnv_tt_file_netfile( prePend );
gettimeofday( &stop, &junk );
if (start.tv_usec > stop.tv_usec) {
stop.tv_usec += 1000000;
stop.tv_sec--;
}
stopwatch_tt_file_netfile += (stop.tv_usec - start.tv_usec);
stopwatch_tt_file_netfile += (stop.tv_sec - start.tv_sec) * 1000000;
}
#else
netpath = _DtEnv_tt_file_netfile( prePend );
#endif /* DTENV_PERF_HOOK */
if ( tt_ptr_error(netpath) != TT_OK ) {
newPrePend = (char *) NULL;
}
else {
#ifdef DTENV_PERF_HOOK
{
int tpi;
extern unsigned long stopwatch_tt_netfile_file;
extern int stopwatch_repeat_rate;
struct timeval start, stop;
struct timezone junk;
gettimeofday( &start, &junk );
for ( tpi = 0; tpi < stopwatch_repeat_rate-1; tpi++ ) {
newPrePend = _DtEnv_tt_host_netfile_file (targetHost, netpath);
if ( tt_ptr_error(newPrePend) == TT_OK )
ttfreeAndNull( newPrePend );
}
newPrePend = _DtEnv_tt_host_netfile_file (targetHost, netpath);
gettimeofday( &stop, &junk );
if (start.tv_usec > stop.tv_usec) {
stop.tv_usec += 1000000;
stop.tv_sec--;
}
stopwatch_tt_netfile_file += (stop.tv_usec - start.tv_usec);
stopwatch_tt_netfile_file += (stop.tv_sec - start.tv_sec) * 1000000;
}
#else
newPrePend = _DtEnv_tt_host_netfile_file (targetHost, netpath);
#endif /* DTENV_PERF_HOOK */
if ( tt_ptr_error(newPrePend) != TT_OK ) {
newPrePend = (char *) NULL;
}
ttfreeAndNull( netpath );
}
}
/*
* Calculate length of the new path element to the new path list.
*/
tmpi = strlen(envVarCache->mappedEnvVarPtr)+1; /* current list + ... */
if ( i != 0 )
tmpi += 1; /* separator */
if (newPrePend)
tmpi += strlen(newPrePend); /* new prePend or ... */
else
tmpi += strlen(prePend); /* ... old prePend */
if (postPend)
tmpi += strlen(postPend); /* new postPend */
if ( tmpi > availEnvStrSize ) {
/*
* Grow new mappedEnvVar space.
*/
availEnvStrSize = tmpi + 64;
envVarCache->mappedEnvVarPtr = (char *) realloc(
(char *) envVarCache->mappedEnvVarPtr,
availEnvStrSize );
}
/*
* Add the new path element.
*/
if ( i != 0 )
strcat( envVarCache->mappedEnvVarPtr, separator );
if (newPrePend)
strcat( envVarCache->mappedEnvVarPtr, newPrePend );
else
strcat( envVarCache->mappedEnvVarPtr, prePend );
if (postPend)
strcat( envVarCache->mappedEnvVarPtr, postPend );
freeAndNull( prePend ); /* aka pathList[i] */
ttfreeAndNull( newPrePend );
freeAndNull( postPend );
}
freeAndNull( pathList );
}
/******************************************************************************
*
* _DtEnvGetMapInformation()
*/
#define _DtEnv_MAX_BUF_SIZE 1024
#define _DtEnv_NULL_GUARD(s) ((s) ? (s) : "")
static char *_DtEnvGetMapInformation( void )
{
char *mapInfo;
char nameBuf[_DtEnv_MAX_BUF_SIZE];
char classBuf[_DtEnv_MAX_BUF_SIZE];
XrmValue resource_value;
XrmDatabase db;
char *rep_type;
int bytesNeeded;
char *name;
char *class;
extern char *_DtApplicationName; /* set in DtUtil.c */
extern char *_DtApplicationClass;
extern Display *_DtDisplay;
/*
* See if an environment variable has been set. If so, get
* the map info from there.
*/
mapInfo = getenv( _DTENV_MAP_ENV_VAR );
if (mapInfo)
return( XtNewString( mapInfo ) );
/*
* Try to get map info from the resource database.
*/
bytesNeeded = strlen(_DTENV_MAP_RESOURCE_NAME)
+ strlen(_DtApplicationName) + 4;
if ( bytesNeeded > _DtEnv_MAX_BUF_SIZE )
name = XtMalloc(bytesNeeded);
else
name = nameBuf;
sprintf (name, "%s*%s",
_DtEnv_NULL_GUARD( _DtApplicationName) , _DTENV_MAP_RESOURCE_NAME);
bytesNeeded = strlen(_DTENV_MAP_RESOURCE_CLASS)
+ strlen(_DtApplicationClass) + 4;
if ( bytesNeeded > _DtEnv_MAX_BUF_SIZE )
class = XtMalloc(bytesNeeded);
else
class = classBuf;
sprintf (class, "%s*%s",
_DtEnv_NULL_GUARD(_DtApplicationClass) , _DTENV_MAP_RESOURCE_CLASS);
db = XtDatabase (_DtDisplay);
if (XrmGetResource (db, nameBuf, classBuf, &rep_type, &resource_value))
mapInfo = (char *) resource_value.addr;
else
mapInfo = (char *) NULL;
if ( name != nameBuf )
XtFree(name);
if ( class != classBuf )
XtFree(class);
if (mapInfo)
return( XtNewString( mapInfo ) );
else
return( (char *) NULL );
}
/******************************************************************************
*
* _DtEnvMapForRemote()
*
* Perform filename mapping on the current environment so it makes
* sense on the target host. The original environment is saved
* for later restoring, and caching is used to minimize mapping
* computations.
*/
void _DtEnvMapForRemote (char *targetHost)
{
int i, cacheRegen;
char *mapListStr, *tmpPtr;
char **mapList;
cacheForTargetHost *targetCache;
int ttMark = 0;
extern char *mostRecentRemoteHostG;
_DtSvcProcessLock();
if (mostRecentRemoteHostG) {
/*
* Warning - a _DtEnvRestoreLocal() was not called for
* the most recent _DtEnvMapForRemote(). Tossing.
*/
freeAndNull(mostRecentRemoteHostG);
}
if (!targetHost) {
/*
* No target host to cache.
*/
_DtSvcProcessUnlock();
return;
}
/*
* Performance enhancement: Check if we can map our $HOME directory
* to the remote host. If we fail because the host cannot be accessed
* then give up the attempt to map the environment now because we'll
* run into a lot of timeouts on remote mapping failures otherwise.
*/
ttMark = tt_mark();
switch ( tt_ptr_error(
_DtEnv_tt_host_netfile_file(targetHost,
_DtEnv_tt_file_netfile(getenv("HOME")) ) ) )
{
case TT_ERR_DBEXIST: /* cannot contact remote host */
case TT_ERR_DBAVAIL: /* timeouts occur trying to make contact */
case TT_ERR_UNIMP: /* remote server doesn't support file naming */
/*
* It will do no good to attempt to map filenames to this remote host
* So forget it -- the user may try again later.
*/
tt_release(ttMark); /* free up tooltalk memory used for test */
_DtSvcProcessUnlock();
return;
break;
default:
tt_release(ttMark); /* free up tooltalk memory used for test */
break;
}
#ifdef _DTENV_OPTIMIZATION_LOCALHOST
/*
* _DtEnvMapForRemote() is normally called from the remote execution
* code within libDtSvc. If by chance it gets called when spawning
* local processes, then a test should be done here to bypass mapping
* environment variables for a local fork/exec.
*/
if ( <unimplemented - targetHost is local test> ) {
mostRecentRemoteHostG = "localhost";
_DtSvcProcessUnlock();
return;
}
#endif /* _DTENV_OPTIMIZATION_LOCALHOST */
/*
* Get list of env vars to be mapped.
*/
mapListStr = _DtEnvGetMapInformation();
if (!mapListStr) {
/*
* Nothing to map.
*/
_DtSvcProcessUnlock();
return;
}
/*
* We have a targetHost that needs some mapping done. Start
* stashing data away.
*/
mostRecentRemoteHostG = strdup( targetHost );
_DtSvcProcessUnlock();
/*
* Find or allocate a cache entry.
*/
targetCache = _DtEnvGetTargetCache( targetHost, 1 );
/*
* See if cache information for targetHost is available, and
* if so, if it still looks valid.
*
* To maximize performance, an all-or-nothing regeneration of
* the cache will be done rather than incremental regeneration
* of some portions of the cache. One would expect that the
* list of variables to map and their contents would remain
* fairly static.
*/
if (targetCache->mapListStr) {
if ( !strcmp( targetCache->mapListStr, mapListStr ) ) {
/*
* Atleast the list of environment variables that need
* to be mapped is the same as previous.
*/
cacheRegen = 0;
for ( i = 0; i < targetCache->mapListCnt; i++ ) {
if ( tmpPtr = getenv(targetCache->mapList[i]) ) {
if ( strcmp( tmpPtr,
targetCache->mapListDetails[i].localEnvVarCopy) ) {
cacheRegen = 1; /* one map entry is no longer valid */
break;
}
}
else {
/*
* Env Var does not exist, but maybe it never did.
*/
if (targetCache->mapListDetails[i].localEnvVarCopy) {
/*
* Was in cache, but now no longer exists.
*/
cacheRegen = 1; /* env var no longer exists */
break;
}
}
}
}
else {
cacheRegen = 1; /* map list changed - need to regen cache */
}
}
else {
cacheRegen = 1; /* need to create cache */
}
if (cacheRegen) {
/*
* Toss out the old.
*/
_DtEnvCleanCacheSlot(targetCache);
/*
* Bring in the new.
*/
targetCache->remoteHost = strdup( targetHost );
targetCache->mapListStr = strdup( mapListStr );
targetCache->mapList = _DtEnvGetMapList( mapListStr,
&(targetCache->mapListCnt) );
targetCache->mapListDetails = (cachedEnvVar *)
malloc( sizeof(cachedEnvVar) *
targetCache->mapListCnt );
for ( i = 0; i < targetCache->mapListCnt; i++ ) {
_DtEnvMapIt( targetCache->mapList[i],
&(targetCache->mapListDetails[i]),
targetHost );
}
}
else {
/*
* We can use cached information. Even though all the
* environment variable "strings" match, the users
* (environ **) pointers may be different, so re-cache
* the restoration pointers.
*/
for ( i = 0; i < targetCache->mapListCnt; i++ ) {
if ( targetCache->mapListDetails[i].localEnvVarPtr =
getenv( targetCache->mapList[i] ) ) {
targetCache->mapListDetails[i].localEnvVarPtr -=
strlen( targetCache->mapList[i] ) + 1;
}
}
}
/*
* Install the mapped environment variables.
*/
for ( i = 0; i < targetCache->mapListCnt; i++ ) {
putenv( targetCache->mapListDetails[i].mappedEnvVarPtr );
}
XtFree(mapListStr);
}
/******************************************************************************
*
* _DtEnvRestoreLocal()
*
* Presuming a _DtEnvMapForRemote() was called, _DtEnvRestoreLocal()
* restores the original envirnment settings for a number of
* environment variables.
*/
void _DtEnvRestoreLocal ()
{
extern char *mostRecentRemoteHostG;
cacheForTargetHost *targetCache;
char *tmpP;
int i;
_DtSvcProcessLock();
if (mostRecentRemoteHostG) {
#ifdef _DTENV_OPTIMIZATION_LOCALHOST
/*
* See comment with the other ifdef'ed block.
*
* If localhost, then nothing to restore.
*/
if ( !strcmp(mostRecentRemoteHostG, "localhost") ) {
mostRecentRemoteHostG = (char *) NULL;
_DtSvcProcessUnlock();
return;
}
#endif /* _DTENV_OPTIMIZATION_LOCALHOST */
targetCache = _DtEnvGetTargetCache( mostRecentRemoteHostG, 0 );
if (targetCache) {
/*
* Install the mapped environment variables.
*/
for ( i = 0; i < targetCache->mapListCnt; i++ ) {
tmpP = targetCache->mapListDetails[i].localEnvVarPtr;
if ( tmpP ) {
putenv( targetCache->mapListDetails[i].localEnvVarPtr );
}
}
}
freeAndNull( mostRecentRemoteHostG );
}
_DtSvcProcessUnlock();
}