2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these libraries and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
24 * (c) Copyright 1993, 1994 Hewlett-Packard Company *
25 * (c) Copyright 1993, 1994 International Business Machines Corp. *
26 * (c) Copyright 1993, 1994 Sun Microsystems, Inc. *
27 * (c) Copyright 1993, 1994 Novell, Inc. *
29 /******************************************************************************
30 ******************************************************************************
34 ** $TOG: DtEnvMap.c /main/7 1998/07/30 12:11:59 mgreess $
36 ** Map the path elements of environment strings as specified by yet
37 ** another environment string from the local host to a remote host.
38 ** When possible, cache the results since filename mapping is expensive.
40 ** DTENVMAPFORREMOTE="NAME1[:NAME2[...]]"
43 ** NAME2=path3:path4:...
45 ******************************************************************************
46 *****************************************************************************/
48 #define X_INCLUDE_STRING_H
49 #define XOS_USE_XT_LOCKING
50 #include <X11/Xos_r.h>
52 #include <DtNlUtils.h>
54 #include "DtSvcLock.h"
56 /******************************************************************************
57 ******************************************************************************
59 ** Data Structures and Defines.
63 /**********************************************************
67 * ... is specified by either an environment variable or
68 * a global X resource. If present, the X resource acts
69 * globally, and is referred to on every attempt to map.
70 * If an environment variable is set, it overrides the X
71 * resource setting, thereby allowing for local control
72 * of individual process threads (by way of propagated
76 #define _DTENV_MAP_RESOURCE_NAME "dtEnvMapForRemote"
77 #define _DTENV_MAP_RESOURCE_CLASS "DtEnvMapForRemote"
78 #define _DTENV_MAP_ENV_VAR "DTENVMAPFORREMOTE"
80 /**********************************************************
82 * The primary caching/mapping data structures.
86 char *localEnvVarPtr; /* original ptr, used for restoring */
87 char *localEnvVarCopy; /* eg, /users/foo:/usr/bar */
88 char *mappedEnvVarPtr; /* eg, PATH=/nfs/.../foo:/nfs/.../bar */
92 char *remoteHost; /* host to map env vars to */
93 int cacheHit; /* usage rate for this mapping info */
94 char *mapListStr; /* copy of DTENVMAPFORREMOTE=... */
96 int mapListCnt; /* how many _real_ env vars to map */
97 char **mapList; /* simple list of env var names to map*/
98 cachedEnvVar *mapListDetails; /* list of map info per env var name */
102 * Cache information. Keep mapping information for MAX_HOSTS_CACHED
105 #define MAX_HOSTS_CACHED 8
106 static cacheForTargetHost cachePoolG[MAX_HOSTS_CACHED];
109 * Track the most recent targetHost.
111 static char *mostRecentRemoteHostG;
113 /**********************************************************
117 * To reduce malloc calls when creating lists, malloc
120 * char *list[0..15 , 16..31, 32..63, ...]
122 #define MALLOC_BUMP_SIZE 16
124 #define freeAndNull(ptr) if (ptr) free(ptr); ptr = NULL;
125 #define ttfreeAndNull(ptr) if (ptr) tt_free(ptr); ptr = NULL;
127 /**********************************************************
129 * Functionality hooks.
131 * - _DTENV_SUPPORT_COMMA_SEPARATED - turn on code that
132 * special cases some environment variables and
133 * treats them as comma separated and possibly
136 * - _DTENV_SUPPORT_MAPERROR_CACHING - turn on code that
137 * would cache error codes when mapping files in
138 * addition to caching successfully mapped files.
139 * In configurations where mapping errors are
140 * common, this could be a big help, but on the
141 * downside, a mapping error that is the result
142 * of a temporary network error (for example)
143 * could get locked into a cache.
145 * - _DTENV_OPTIMIZATION_LOCALHOST - turn on code that
146 * would prevent even trying to map/cache environment
147 * variables if the targetHost was the localhost.
149 #define _DTENV_SUPPORT_COMMA_SEPARATED 1
150 #undef _DTENV_SUPPORT_MAPERROR_CACHING
151 #undef _DTENV_OPTIMIZATION_LOCALHOST
154 /**********************************************************
158 * - DTENV_PERF_HOOK - instrunment file mapping code for
159 * performance measuring.
161 #undef DTENV_PERF_HOOK
163 #ifdef DTENV_PERF_HOOK
166 int stopwatch_repeat_rate = 10;
167 unsigned long stopwatch_tt_file_netfile;
168 unsigned long stopwatch_tt_netfile_file;
169 #endif /* DTENV_PERF_HOOK */
172 /******************************************************************************
173 ******************************************************************************
175 ** _DtEnv_tt_file_netfile()
176 ** _DtEnv_tt_netfile_file()
178 ** Both are caching versions built on the non-caching Tooltalk versions.
180 ** The one flaw in both is that if the filesystem topology changes
181 ** (eg, NFS mounts, symlinks, etc), old and incorrect mapping information
182 ** may be returned. To minimize this, a cached mapping will only be used
183 ** so many times before being recomputed. An alternative (not implemented
184 ** here) would be to recompute based on age of the mapping via
187 ** ******************* XXX Alert **************************
189 ** Tooltalk currently allocates a range of char*'s
190 ** and then associates them with a range of error
191 ** messages (ala tt_ptr_error()). In effect, the
192 ** allocated char*'s become constants within Tooltalk.
194 ** This code takes this into account when returning
195 ** values. Non-reserved char*'s are strdup()ed
196 ** often, while reserved char*'s are not to preserve
199 ** tjg: Other than differing cache repositories, all the onion-skin
200 ** routines below look the same. Probably better to have one
201 ** core routine to minimize code size.
205 * Per cache setup (ie, one for _DtEnv_tt_file_netfile(), one for
206 * _DtEnv_tt_netfile_file()), start off by caching SIZE_START
207 * mappings, growing by SIZE_BUMP as needed, while limiting the
208 * total cache growth to SIZE_MAX mappings.
210 #define CACHE_FILEFRAG_SIZE_START 15
211 #define CACHE_FILEFRAG_SIZE_BUMP 15
212 #define CACHE_FILEFRAG_SIZE_MAX 45
215 * After a cached mapping is used REMAP_AFTER times, age it out
216 * and force a recomputation of the mapping from scratch.
218 #define CACHE_FILEFRAG_REMAP_AFTER 25
221 * After RESET_PRI hits on a cache of size SIZE_MAX, reset all
222 * cacheHit counters. This will allow new mappings to work into
223 * the cache against mappings that might have been popular long
226 #define CACHE_FILEFRAG_RESET_PRI 200
229 /******************************************************************************
231 * _DtEnv_tt_file_netfile()
233 * A caching version of tt_file_netfile().
236 char *pathFragOrig; /* eg, /usr/dt */
237 char *pathFragMapped; /* eg, <from tt_file_netfile> */
238 int cacheHit; /* usage of */
241 char *_DtEnv_tt_file_netfile(
242 const char *filename)
244 static int first_time = 1;
245 static int fragListAvail = CACHE_FILEFRAG_SIZE_START;
246 static int fragListCnt = 0;
247 static cachedFileFrag *fragList;
248 static int cacheGen = 0;
249 static int hitIdxStart = 0;
252 int hitval, hitIdx, i;
253 cachedFileFrag *tmpCffP;
255 int newCount = fragListCnt;
260 fragList = (cachedFileFrag *) calloc( fragListAvail,
261 sizeof(cachedFileFrag) );
266 * Take care of the obvious.
269 _DtSvcProcessUnlock();
270 return( (char *) NULL );
274 * Look for existing answer in cache.
276 * While at it, also look for least used entry just in case.
279 hitIdxStart = (hitIdxStart + 7) % fragListCnt;
283 hitIdx = hitIdxStart;
284 hitval = fragList[hitIdx].cacheHit;
285 tmpCffP = fragList; /* walk rather than index */
287 for ( i = 0; i < fragListCnt; i++ ) {
288 if (tmpCffP->cacheHit && !strcmp( filename, tmpCffP->pathFragOrig ) ) {
292 if (tmpCffP->cacheHit < hitval) {
294 hitval = tmpCffP->cacheHit;
300 * Decide what was found.
302 if ( i != fragListCnt ) {
304 * Found a cached entry.
307 if ( fragList[hitIdx].cacheHit++ > CACHE_FILEFRAG_REMAP_AFTER ) {
309 * This looks like an old entry, so re-compute it.
311 freeAndNull( fragList[hitIdx].pathFragOrig );
312 ttfreeAndNull( fragList[hitIdx].pathFragMapped );
313 fragList[hitIdx].cacheHit = 0; /* 0 means remap below */
318 * Did not find a cache entry, so scrounge around for
321 if ( fragListCnt < fragListAvail ) {
323 * Use next already-malloc'ed cacheEntry.
325 hitIdx = fragListCnt;
326 newCount = fragListCnt + 1;
328 else if ( fragListCnt < CACHE_FILEFRAG_SIZE_MAX ) {
330 * Can grow fragList[]
332 fragListAvail += CACHE_FILEFRAG_SIZE_BUMP;
333 fragList = (cachedFileFrag *) realloc( (char *) fragList,
334 sizeof(cachedFileFrag) * fragListAvail);
336 * Zero out new memory.
338 memset( fragList + (fragListAvail-CACHE_FILEFRAG_SIZE_BUMP),
339 0, CACHE_FILEFRAG_SIZE_BUMP*sizeof(cachedFileFrag) );
340 hitIdx = fragListCnt;
341 newCount = fragListCnt + 1;
345 * Last resort - bump out the least used entry.
347 freeAndNull( fragList[hitIdx].pathFragOrig );
348 ttfreeAndNull( fragList[hitIdx].pathFragMapped );
351 * Since the cache is 100% full, ocassionally reset
352 * everyone's cacheHit rate so entries that were only
353 * popular long ago don't get locked in.
355 if ( cacheGen++ > CACHE_FILEFRAG_RESET_PRI ) {
358 for ( i = 0; i < fragListCnt; i++ ) {
359 tmpCffP->cacheHit = 1;
365 fragList[hitIdx].cacheHit = 0; /* 0 means remap below */
368 if ( ! fragList[hitIdx].cacheHit ) {
370 * Need to perform mapping.
372 netpath = tt_file_netfile( filename );
374 #ifdef _DTENV_SUPPORT_MAPERROR_CACHING
375 fragList[hitIdx].pathFragOrig = strdup( filename );
376 fragList[hitIdx].cacheHit = 1;
377 fragList[hitIdx].pathFragMapped = netpath;
379 fragListCnt = newCount;
381 if ( tt_ptr_error(netpath) == TT_OK ) {
382 fragList[hitIdx].pathFragOrig = strdup( filename );
383 fragList[hitIdx].cacheHit = 1;
384 fragList[hitIdx].pathFragMapped = netpath;
387 * Only change the count if we are successful in adding
390 fragListCnt = newCount;
394 * Don't cache errors. Leave this cache slot empty
395 * and it will be rediscovered and used in the future.
397 fragList[hitIdx].cacheHit = 0;
399 * Do not change the fragListCount since we don't want to
400 * add in error entries.
403 #endif /* _DTENV_SUPPORT_MAPERROR_CACHING */
407 * Dig out answer and return it.
409 #ifdef _DTENV_SUPPORT_MAPERROR_CACHING
410 if ( tt_ptr_error(netpath) == TT_OK )
412 if ( fragList[hitIdx].cacheHit )
413 #endif /* _DTENV_SUPPORT_MAPERROR_CACHING */
416 * Return a tt_free-able copy of the answer.
418 tmpStr = tt_malloc( strlen(fragList[hitIdx].pathFragMapped) + 1 );
419 strcpy( tmpStr, fragList[hitIdx].pathFragMapped );
421 _DtSvcProcessUnlock();
428 * Since netpath is an error code, return as is.
430 _DtSvcProcessUnlock();
436 /******************************************************************************
438 * _DtEnv_tt_netfile_file()
440 * A caching version of tt_netfile_file().
444 char *pathFragOrig; /* eg, <from tt_file_netfile> */
445 char *pathFragMapped; /* eg, /nfs/hostb/usr/dt */
446 int cacheHit; /* usage of */
449 char *_DtEnv_tt_host_netfile_file(
451 const char *filename)
453 static int first_time = 1;
454 static int fragListAvail = CACHE_FILEFRAG_SIZE_START;
455 static int fragListCnt = 0;
456 static cachedNetfileFrag *fragList;
457 static int cacheGen = 0;
458 static int hitIdxStart = 0;
461 int hitval, hitIdx, i;
462 cachedNetfileFrag *tmpCffP;
464 int newCount = fragListCnt;
468 fragList = (cachedNetfileFrag *) calloc( fragListAvail,
469 sizeof(cachedNetfileFrag) );
474 * Take care of the obvious.
477 _DtSvcProcessUnlock();
478 return( (char *) NULL );
483 * Return a tt_free-able un-mapped copy.
485 tmpStr = tt_malloc( strlen(filename) + 1 );
486 strcpy( tmpStr, filename );
488 _DtSvcProcessUnlock();
493 * Look for existing answer in cache.
495 * While at it, also look for least used entry just in case.
498 hitIdxStart = (hitIdxStart + 7) % fragListCnt;
502 hitIdx = hitIdxStart;
503 hitval = fragList[hitIdx].cacheHit;
504 tmpCffP = fragList; /* walk rather than index */
506 for ( i = 0; i < fragListCnt; i++ ) {
507 if (tmpCffP->cacheHit && !strcmp( filename, tmpCffP->pathFragOrig ) ) {
508 if (!strcmp( host, tmpCffP->targetHost ) ) {
514 * Save index of least used entry
516 if (tmpCffP->cacheHit < hitval) {
518 hitval = tmpCffP->cacheHit;
524 * Decide what was found.
526 if ( i != fragListCnt ) {
528 * Found a cached entry.
531 if ( fragList[hitIdx].cacheHit++ > CACHE_FILEFRAG_REMAP_AFTER ) {
533 * This looks like an old entry, so re-compute it.
535 freeAndNull( fragList[hitIdx].targetHost );
536 freeAndNull( fragList[hitIdx].pathFragOrig );
537 ttfreeAndNull( fragList[hitIdx].pathFragMapped );
538 fragList[hitIdx].cacheHit = 0; /* 0 means remap below */
543 * Did not find a cache entry, so scrounge around for
546 if ( fragListCnt < fragListAvail ) {
548 * Use next already-malloc'ed cacheEntry.
550 hitIdx = fragListCnt;
551 newCount = fragListCnt + 1;
553 else if ( fragListCnt < CACHE_FILEFRAG_SIZE_MAX ) {
555 * Can grow fragList[]
557 fragListAvail += CACHE_FILEFRAG_SIZE_BUMP;
558 fragList = (cachedNetfileFrag *) realloc( (char *) fragList,
559 sizeof(cachedNetfileFrag) * fragListAvail);
561 * Zero out new memory.
563 memset( fragList + (fragListAvail-CACHE_FILEFRAG_SIZE_BUMP),
564 0, CACHE_FILEFRAG_SIZE_BUMP*sizeof(cachedNetfileFrag) );
565 hitIdx = fragListCnt;
566 newCount = fragListCnt + 1;
570 * Last resort - bump out the least used entry.
572 freeAndNull( fragList[hitIdx].targetHost );
573 freeAndNull( fragList[hitIdx].pathFragOrig );
574 ttfreeAndNull( fragList[hitIdx].pathFragMapped );
577 * Since the cache is 100% full, ocassionally reset
578 * everyone's cacheHit rate so entries that were only
579 * popular long ago don't get locked in.
581 if ( cacheGen++ > CACHE_FILEFRAG_RESET_PRI ) {
584 for ( i = 0; i < fragListCnt; i++ ) {
585 tmpCffP->cacheHit = 1;
591 fragList[hitIdx].cacheHit = 0; /* 0 means remap below */
594 if ( ! fragList[hitIdx].cacheHit ) {
596 * Need to perform mapping.
598 newfile = tt_host_netfile_file( host, filename );
600 #ifdef _DTENV_SUPPORT_MAPERROR_CACHING
601 fragList[hitIdx].targetHost = strdup ( host );
602 fragList[hitIdx].pathFragOrig = strdup( filename );
603 fragList[hitIdx].cacheHit = 1;
604 fragList[hitIdx].pathFragMapped = newfile;
606 fragListCnt = newCount;
608 if ( tt_ptr_error(newfile) == TT_OK ) {
609 fragList[hitIdx].targetHost = strdup ( host );
610 fragList[hitIdx].pathFragOrig = strdup( filename );
611 fragList[hitIdx].cacheHit = 1;
612 fragList[hitIdx].pathFragMapped = newfile;
614 * Only change the count if we are successful in adding
617 fragListCnt = newCount;
621 * Don't cache errors. Leave this cache slot empty
622 * and it will be rediscovered and used in the future.
624 fragList[hitIdx].cacheHit = 0;
627 * Do not change the fragListCount since we are not saving
631 #endif /* _DTENV_SUPPORT_MAPERROR_CACHING */
635 * Dig out answer and return it.
637 #ifdef _DTENV_SUPPORT_MAPERROR_CACHING
638 if ( tt_ptr_error(newfile) == TT_OK )
640 if ( fragList[hitIdx].cacheHit )
641 #endif /* _DTENV_SUPPORT_MAPERROR_CACHING */
644 * Return a tt_free-able copy of the answer.
646 tmpStr = tt_malloc( strlen(fragList[hitIdx].pathFragMapped) + 1 );
647 strcpy( tmpStr, fragList[hitIdx].pathFragMapped );
649 _DtSvcProcessUnlock();
656 * Since newfile is an error code, return as is.
658 _DtSvcProcessUnlock();
664 /******************************************************************************
665 ******************************************************************************
667 ** Environment variable mapping code.
671 /******************************************************************************
675 * Fetch the environment variable who's value is a colon separated list
676 * of environment variable names who's values need to be mapped. Then
677 * break down the list into an indexable array.
679 static char **_DtEnvGetMapList(
683 char **mapList; /* decomposition of above */
684 char *tmpPtr, *tmpPtr2;
687 _Xstrtokparams strtok_buf;
690 * Handle NULL mapListStr.
694 return( (char **) NULL );
698 * Create a block of string pointers - remalloc() as needed.
700 availListSize = MALLOC_BUMP_SIZE;
701 mapList = (char **) malloc( sizeof(char *) * (availListSize) );
704 * Break up the colon seperated string into an indexable array.
706 tmpPtr = strdup(mapListStr); /* work copy for strtok */
711 tmpPtr2 = _XStrtok( tmpPtr, ":", strtok_buf );
715 tmpPtr2 = _XStrtok( (char *) NULL, ":", strtok_buf );
719 * Have possible env var name to map - make sure it exists.
721 if ( getenv(tmpPtr2) ) {
723 if (*mapListCount > availListSize) {
724 availListSize += MALLOC_BUMP_SIZE;
725 mapList = (char **) realloc( (char *) mapList,
726 sizeof(char *) * (availListSize) );
728 mapList[*mapListCount-1] = strdup(tmpPtr2);
738 return( (char **) mapList );
742 /******************************************************************************
744 * _DtEnvCleanCacheSlot()
746 * Free up all memory associated with a cache slot for a targetHost.
748 static void _DtEnvCleanCacheSlot( cacheForTargetHost *targetCache )
753 freeAndNull( targetCache->remoteHost );
755 targetCache->cacheHit = 1;
757 freeAndNull( targetCache->mapListStr );
759 for ( i = 0; i < targetCache->mapListCnt; i++ ) {
760 freeAndNull( targetCache->mapList[i] );
761 freeAndNull( targetCache->mapListDetails[i].localEnvVarCopy );
762 freeAndNull( targetCache->mapListDetails[i].mappedEnvVarPtr );
763 /* do not free .localEnvVarPtr - belongs to environ */
765 freeAndNull( targetCache->mapList );
766 targetCache->mapListCnt = 0;
770 /******************************************************************************
772 * _DtEnvGetTargetCache()
774 * For a specified targetHost, find existing cache information and
775 * optionally create a cache for a targetHost if one doesn't exist.
777 * The define MAX_HOSTS_CACHED controls how many targetHosts can
780 static cacheForTargetHost *_DtEnvGetTargetCache(
784 static int cacheHitGen = 0;
785 static int hitIdxStart;
786 int i, hitidx, hitval;
793 return( (cacheForTargetHost *) NULL );
798 * Look for targetHost in current cache pool.
800 for ( i = 0; i < MAX_HOSTS_CACHED; i++ ) {
801 if ( cachePoolG[i].remoteHost ) {
802 if ( !strcmp( targetHost, cachePoolG[i].remoteHost ) ) {
803 cachePoolG[i].cacheHit++;
809 if ( i != MAX_HOSTS_CACHED ) {
811 * targetHost is in a cache slot already.
813 _DtSvcProcessUnlock();
814 return( &cachePoolG[i] );
816 else if ( !createIfNeeded ) {
818 * No cache slot for, and we shouldn't create one either.
820 _DtSvcProcessUnlock();
821 return( (cacheForTargetHost *) NULL );
825 * Find an empty cache slot or take over a rarely used slot.
827 hitIdxStart = (hitIdxStart + 7) % MAX_HOSTS_CACHED;
829 hitidx = hitIdxStart;
830 hitval = cachePoolG[hitidx].cacheHit;
832 for ( i = 0; i < MAX_HOSTS_CACHED; i++ ) {
833 if ( ! cachePoolG[i].remoteHost ) {
835 * Empty slot - take it.
840 else if ( cachePoolG[i].cacheHit < hitval ) {
842 hitval = cachePoolG[i].cacheHit;
846 if ( cachePoolG[hitidx].remoteHost ) {
848 * Cache was in use, clean first.
850 _DtEnvCleanCacheSlot( &cachePoolG[hitidx] );
851 cachePoolG[hitidx].remoteHost = strdup( targetHost );
854 * Since all the slots are full, occasionally reset everyones
855 * cacheHit counters. This gives new targetHosts a chance
856 * to compete with targetHosts that were popular long ago.
858 if ( cacheHitGen++ > 50 ) {
859 for ( i = 0; i < MAX_HOSTS_CACHED; i++ ) {
860 cachePoolG[i].cacheHit = 1;
865 _DtSvcProcessUnlock();
866 return( &cachePoolG[hitidx] );
871 /******************************************************************************
875 * Fill out a map cache for a single environment variable.
877 static void _DtEnvMapIt(
879 cachedEnvVar *envVarCache,
882 char *separator, *tmpPtr, *tmpPtr2, swapout, *netpath;
883 char *prePend, *postPend, *newPrePend;
887 int availPathListSize, pathListCount, availEnvStrSize, len, tmpi, i;
889 _Xstrtokparams strtok_buf;
892 * Information Layout:
894 * localEnvVarPtr = ptr to original "PATH=/users/foo:/users/bar"
895 * localEnvVarCopy = copy of original "/users/foo:/users/bar"
896 * mappedEnvVarPtr = mapped "PATH=/nfs/.../users/foo:/nfs/.../users/bar"
898 if ( (envVarCache->localEnvVarPtr = getenv( envVar )) ) {
899 envVarCache->localEnvVarCopy = strdup( envVarCache->localEnvVarPtr );
901 /* sneak back past "NAME=" portion. */
902 envVarCache->localEnvVarPtr -= strlen( envVar ) + 1;
906 * Nothing to map. Punt.
908 envVarCache->localEnvVarCopy = (char *) NULL;
909 envVarCache->localEnvVarPtr = (char *) NULL;
913 #ifdef _DTENV_SUPPORT_COMMA_SEPARATED
915 * Pick between colon-separated and comma-separated host-qualified
918 if ( !strcmp(envVar, "DTDATABASESEARCHPATH") ) {
920 * comma-separated and host-qualified mapping.
926 * colon-separated mapping.
932 #endif /* _DTENV_SUPPORT_COMMA_SEPARATED */
935 * Break path list into elements
937 availPathListSize = MALLOC_BUMP_SIZE;
939 pathList = (char **) malloc( sizeof(char *) * availPathListSize );
942 * Break up path list into an array of path elements.
944 tmpPtr = strdup( envVarCache->localEnvVarCopy ); /* work copy */
948 tmpPtr2 = _XStrtok( tmpPtr, separator, strtok_buf );
950 tmpPtr2 = _XStrtok( (char *) NULL, separator, strtok_buf );
954 if (pathListCount > availPathListSize) {
955 availPathListSize += MALLOC_BUMP_SIZE;
956 pathList = (char **) realloc( (char *) pathList,
957 sizeof(char *) * availPathListSize );
959 pathList[pathListCount-1] = strdup( tmpPtr2 );
968 * Setup new "NAME=....." string.
970 availEnvStrSize = strlen( envVar ) + 64;
971 envVarCache->mappedEnvVarPtr = (char *) calloc( availEnvStrSize, sizeof(char) );
972 strcpy( envVarCache->mappedEnvVarPtr, envVar );
973 strcat( envVarCache->mappedEnvVarPtr, "=" );
976 * Start mapping each path element.
978 for ( i = 0; i < pathListCount; i++ ) {
979 prePend = pathList[i];
980 postPend = (char *) NULL;
981 newPrePend = (char *) NULL;
984 * Assume we need to map this path element.
988 #ifdef _DTENV_SUPPORT_COMMA_SEPARATED
989 if ( !strcmp( separator, "," ) ) {
990 if ( DtStrchr(prePend, ':' ) ) {
992 * Host qualified elements in a comma separated list
993 * will NOT be mapped.
998 #endif /* _DTENV_SUPPORT_COMMA_SEPARATED */
1000 if (considerMapping) {
1002 * Tear apart and check for so called substitution characters.
1004 if (( tmpPtr = DtStrchr(prePend, '%') )) {
1006 * Temporarly shorten path up to substitution character.
1012 * Move the dividing point back to a directory element.
1014 tmpPtr2 = DtStrrchr( prePend, '/' );
1017 * Restore the send half of the string.
1023 * Can do a split around the "/".
1025 * Will have "<prePath>/" and "/<postPath>".
1027 postPend = strdup( tmpPtr2 );
1028 *(tmpPtr2 + mblen(tmpPtr2, MB_CUR_MAX)) = '\0';
1032 #ifdef DTENV_PERF_HOOK
1035 extern unsigned long stopwatch_tt_file_netfile;
1036 extern int stopwatch_repeat_rate;
1038 struct timeval start, stop;
1039 struct timezone junk;
1041 gettimeofday( &start, &junk );
1043 for ( tpi = 0; tpi < stopwatch_repeat_rate-1; tpi++ ) {
1044 netpath = _DtEnv_tt_file_netfile( prePend );
1045 if ( tt_ptr_error(netpath) == TT_OK )
1046 ttfreeAndNull( netpath );
1048 netpath = _DtEnv_tt_file_netfile( prePend );
1050 gettimeofday( &stop, &junk );
1052 if (start.tv_usec > stop.tv_usec) {
1053 stop.tv_usec += 1000000;
1057 stopwatch_tt_file_netfile += (stop.tv_usec - start.tv_usec);
1058 stopwatch_tt_file_netfile += (stop.tv_sec - start.tv_sec) * 1000000;
1061 netpath = _DtEnv_tt_file_netfile( prePend );
1062 #endif /* DTENV_PERF_HOOK */
1063 if ( tt_ptr_error(netpath) != TT_OK ) {
1064 newPrePend = (char *) NULL;
1067 #ifdef DTENV_PERF_HOOK
1070 extern unsigned long stopwatch_tt_netfile_file;
1071 extern int stopwatch_repeat_rate;
1073 struct timeval start, stop;
1074 struct timezone junk;
1076 gettimeofday( &start, &junk );
1078 for ( tpi = 0; tpi < stopwatch_repeat_rate-1; tpi++ ) {
1079 newPrePend = _DtEnv_tt_host_netfile_file (targetHost, netpath);
1080 if ( tt_ptr_error(newPrePend) == TT_OK )
1081 ttfreeAndNull( newPrePend );
1084 newPrePend = _DtEnv_tt_host_netfile_file (targetHost, netpath);
1086 gettimeofday( &stop, &junk );
1088 if (start.tv_usec > stop.tv_usec) {
1089 stop.tv_usec += 1000000;
1093 stopwatch_tt_netfile_file += (stop.tv_usec - start.tv_usec);
1094 stopwatch_tt_netfile_file += (stop.tv_sec - start.tv_sec) * 1000000;
1097 newPrePend = _DtEnv_tt_host_netfile_file (targetHost, netpath);
1098 #endif /* DTENV_PERF_HOOK */
1099 if ( tt_ptr_error(newPrePend) != TT_OK ) {
1100 newPrePend = (char *) NULL;
1102 ttfreeAndNull( netpath );
1107 * Calculate length of the new path element to the new path list.
1109 tmpi = strlen(envVarCache->mappedEnvVarPtr)+1; /* current list + ... */
1111 tmpi += 1; /* separator */
1113 tmpi += strlen(newPrePend); /* new prePend or ... */
1115 tmpi += strlen(prePend); /* ... old prePend */
1117 tmpi += strlen(postPend); /* new postPend */
1119 if ( tmpi > availEnvStrSize ) {
1121 * Grow new mappedEnvVar space.
1123 availEnvStrSize = tmpi + 64;
1124 envVarCache->mappedEnvVarPtr = (char *) realloc(
1125 (char *) envVarCache->mappedEnvVarPtr,
1130 * Add the new path element.
1133 strcat( envVarCache->mappedEnvVarPtr, separator );
1136 strcat( envVarCache->mappedEnvVarPtr, newPrePend );
1138 strcat( envVarCache->mappedEnvVarPtr, prePend );
1141 strcat( envVarCache->mappedEnvVarPtr, postPend );
1143 freeAndNull( prePend ); /* aka pathList[i] */
1144 ttfreeAndNull( newPrePend );
1145 freeAndNull( postPend );
1147 freeAndNull( pathList );
1150 /******************************************************************************
1152 * _DtEnvGetMapInformation()
1154 #define _DtEnv_MAX_BUF_SIZE 1024
1155 #define _DtEnv_NULL_GUARD(s) ((s) ? (s) : "")
1157 static char *_DtEnvGetMapInformation( void )
1160 char nameBuf[_DtEnv_MAX_BUF_SIZE];
1161 char classBuf[_DtEnv_MAX_BUF_SIZE];
1162 XrmValue resource_value;
1169 extern char *_DtApplicationName; /* set in DtUtil.c */
1170 extern char *_DtApplicationClass;
1171 extern Display *_DtDisplay;
1175 * See if an environment variable has been set. If so, get
1176 * the map info from there.
1178 mapInfo = getenv( _DTENV_MAP_ENV_VAR );
1181 return( XtNewString( mapInfo ) );
1184 * Try to get map info from the resource database.
1186 bytesNeeded = strlen(_DTENV_MAP_RESOURCE_NAME)
1187 + strlen(_DtApplicationName) + 4;
1188 if ( bytesNeeded > _DtEnv_MAX_BUF_SIZE )
1189 name = XtMalloc(bytesNeeded);
1193 sprintf (name, "%s*%s",
1194 _DtEnv_NULL_GUARD( _DtApplicationName) , _DTENV_MAP_RESOURCE_NAME);
1196 bytesNeeded = strlen(_DTENV_MAP_RESOURCE_CLASS)
1197 + strlen(_DtApplicationClass) + 4;
1198 if ( bytesNeeded > _DtEnv_MAX_BUF_SIZE )
1199 class = XtMalloc(bytesNeeded);
1202 sprintf (class, "%s*%s",
1203 _DtEnv_NULL_GUARD(_DtApplicationClass) , _DTENV_MAP_RESOURCE_CLASS);
1205 db = XtDatabase (_DtDisplay);
1206 if (XrmGetResource (db, nameBuf, classBuf, &rep_type, &resource_value))
1207 mapInfo = (char *) resource_value.addr;
1209 mapInfo = (char *) NULL;
1211 if ( name != nameBuf )
1213 if ( class != classBuf )
1217 return( XtNewString( mapInfo ) );
1219 return( (char *) NULL );
1222 /******************************************************************************
1224 * _DtEnvMapForRemote()
1226 * Perform filename mapping on the current environment so it makes
1227 * sense on the target host. The original environment is saved
1228 * for later restoring, and caching is used to minimize mapping
1231 void _DtEnvMapForRemote (char *targetHost)
1234 char *mapListStr, *tmpPtr;
1237 cacheForTargetHost *targetCache;
1240 extern char *mostRecentRemoteHostG;
1242 _DtSvcProcessLock();
1243 if (mostRecentRemoteHostG) {
1245 * Warning - a _DtEnvRestoreLocal() was not called for
1246 * the most recent _DtEnvMapForRemote(). Tossing.
1248 freeAndNull(mostRecentRemoteHostG);
1253 * No target host to cache.
1255 _DtSvcProcessUnlock();
1260 * Performance enhancement: Check if we can map our $HOME directory
1261 * to the remote host. If we fail because the host cannot be accessed
1262 * then give up the attempt to map the environment now because we'll
1263 * run into a lot of timeouts on remote mapping failures otherwise.
1266 switch ( tt_ptr_error(
1267 _DtEnv_tt_host_netfile_file(targetHost,
1268 _DtEnv_tt_file_netfile(getenv("HOME")) ) ) )
1270 case TT_ERR_DBEXIST: /* cannot contact remote host */
1271 case TT_ERR_DBAVAIL: /* timeouts occur trying to make contact */
1272 case TT_ERR_UNIMP: /* remote server doesn't support file naming */
1274 * It will do no good to attempt to map filenames to this remote host
1275 * So forget it -- the user may try again later.
1277 tt_release(ttMark); /* free up tooltalk memory used for test */
1278 _DtSvcProcessUnlock();
1282 tt_release(ttMark); /* free up tooltalk memory used for test */
1287 #ifdef _DTENV_OPTIMIZATION_LOCALHOST
1289 * _DtEnvMapForRemote() is normally called from the remote execution
1290 * code within libDtSvc. If by chance it gets called when spawning
1291 * local processes, then a test should be done here to bypass mapping
1292 * environment variables for a local fork/exec.
1294 if ( <unimplemented - targetHost is local test> ) {
1295 mostRecentRemoteHostG = "localhost";
1296 _DtSvcProcessUnlock();
1299 #endif /* _DTENV_OPTIMIZATION_LOCALHOST */
1302 * Get list of env vars to be mapped.
1304 mapListStr = _DtEnvGetMapInformation();
1310 _DtSvcProcessUnlock();
1315 * We have a targetHost that needs some mapping done. Start
1316 * stashing data away.
1318 mostRecentRemoteHostG = strdup( targetHost );
1319 _DtSvcProcessUnlock();
1322 * Find or allocate a cache entry.
1324 targetCache = _DtEnvGetTargetCache( targetHost, 1 );
1327 * See if cache information for targetHost is available, and
1328 * if so, if it still looks valid.
1330 * To maximize performance, an all-or-nothing regeneration of
1331 * the cache will be done rather than incremental regeneration
1332 * of some portions of the cache. One would expect that the
1333 * list of variables to map and their contents would remain
1336 if (targetCache->mapListStr) {
1337 if ( !strcmp( targetCache->mapListStr, mapListStr ) ) {
1339 * Atleast the list of environment variables that need
1340 * to be mapped is the same as previous.
1343 for ( i = 0; i < targetCache->mapListCnt; i++ ) {
1344 if (( tmpPtr = getenv(targetCache->mapList[i]) )) {
1345 if ( strcmp( tmpPtr,
1346 targetCache->mapListDetails[i].localEnvVarCopy) ) {
1347 cacheRegen = 1; /* one map entry is no longer valid */
1353 * Env Var does not exist, but maybe it never did.
1355 if (targetCache->mapListDetails[i].localEnvVarCopy) {
1357 * Was in cache, but now no longer exists.
1359 cacheRegen = 1; /* env var no longer exists */
1366 cacheRegen = 1; /* map list changed - need to regen cache */
1370 cacheRegen = 1; /* need to create cache */
1377 _DtEnvCleanCacheSlot(targetCache);
1382 targetCache->remoteHost = strdup( targetHost );
1383 targetCache->mapListStr = strdup( mapListStr );
1384 targetCache->mapList = _DtEnvGetMapList( mapListStr,
1385 &(targetCache->mapListCnt) );
1387 targetCache->mapListDetails = (cachedEnvVar *)
1388 malloc( sizeof(cachedEnvVar) *
1389 targetCache->mapListCnt );
1391 for ( i = 0; i < targetCache->mapListCnt; i++ ) {
1392 _DtEnvMapIt( targetCache->mapList[i],
1393 &(targetCache->mapListDetails[i]),
1399 * We can use cached information. Even though all the
1400 * environment variable "strings" match, the users
1401 * (environ **) pointers may be different, so re-cache
1402 * the restoration pointers.
1404 for ( i = 0; i < targetCache->mapListCnt; i++ ) {
1405 if (( targetCache->mapListDetails[i].localEnvVarPtr =
1406 getenv( targetCache->mapList[i] ) )) {
1407 targetCache->mapListDetails[i].localEnvVarPtr -=
1408 strlen( targetCache->mapList[i] ) + 1;
1414 * Install the mapped environment variables.
1416 for ( i = 0; i < targetCache->mapListCnt; i++ ) {
1417 putenv( targetCache->mapListDetails[i].mappedEnvVarPtr );
1424 /******************************************************************************
1426 * _DtEnvRestoreLocal()
1428 * Presuming a _DtEnvMapForRemote() was called, _DtEnvRestoreLocal()
1429 * restores the original envirnment settings for a number of
1430 * environment variables.
1432 void _DtEnvRestoreLocal ()
1434 extern char *mostRecentRemoteHostG;
1435 cacheForTargetHost *targetCache;
1439 _DtSvcProcessLock();
1440 if (mostRecentRemoteHostG) {
1441 #ifdef _DTENV_OPTIMIZATION_LOCALHOST
1443 * See comment with the other ifdef'ed block.
1445 * If localhost, then nothing to restore.
1447 if ( !strcmp(mostRecentRemoteHostG, "localhost") ) {
1448 mostRecentRemoteHostG = (char *) NULL;
1449 _DtSvcProcessUnlock();
1452 #endif /* _DTENV_OPTIMIZATION_LOCALHOST */
1454 targetCache = _DtEnvGetTargetCache( mostRecentRemoteHostG, 0 );
1458 * Install the mapped environment variables.
1460 for ( i = 0; i < targetCache->mapListCnt; i++ ) {
1461 tmpP = targetCache->mapListDetails[i].localEnvVarPtr;
1463 putenv( targetCache->mapListDetails[i].localEnvVarPtr );
1468 freeAndNull( mostRecentRemoteHostG );
1470 _DtSvcProcessUnlock();