Fix typo in license headers
[oweals/cde.git] / cde / lib / DtSvc / DtUtil2 / DtEnvMap.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
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)
10  * any later version.
11  *
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
16  * details.
17  *
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
22  */
23 /*
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.                                *
28  */
29 /******************************************************************************
30  ******************************************************************************
31  **
32  ** DtEnvMap.c
33  **
34  ** $TOG: DtEnvMap.c /main/7 1998/07/30 12:11:59 mgreess $
35  **
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.
39  **
40  ** DTENVMAPFORREMOTE="NAME1[:NAME2[...]]"
41  **
42  ** NAME1=path1:path2
43  ** NAME2=path3:path4:...
44  **
45  ******************************************************************************
46  *****************************************************************************/
47
48 #define X_INCLUDE_STRING_H
49 #define XOS_USE_XT_LOCKING
50 #include <X11/Xos_r.h>
51
52 #include <DtNlUtils.h>
53 #include <Tt/tt_c.h>
54 #include "DtSvcLock.h"
55
56 /******************************************************************************
57  ******************************************************************************
58  **
59  ** Data Structures and Defines.
60  **
61  **/
62
63 /**********************************************************
64  *
65  * What to map...
66  *
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
73  * environments).
74  */
75
76 #define _DTENV_MAP_RESOURCE_NAME   "dtEnvMapForRemote"
77 #define _DTENV_MAP_RESOURCE_CLASS  "DtEnvMapForRemote"
78 #define _DTENV_MAP_ENV_VAR         "DTENVMAPFORREMOTE"
79
80 /**********************************************************
81  *
82  * The primary caching/mapping data structures.
83  */
84
85 typedef struct {
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 */
89 } cachedEnvVar;
90
91 typedef struct {
92     char          *remoteHost;          /* host to map env vars to */
93     int            cacheHit;            /* usage rate for this mapping info */
94     char          *mapListStr;          /* copy of DTENVMAPFORREMOTE=... */
95
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 */
99 } cacheForTargetHost; 
100
101 /*
102  * Cache information.  Keep mapping information for MAX_HOSTS_CACHED
103  * targetHosts.
104  */
105 #define MAX_HOSTS_CACHED 8
106 static cacheForTargetHost cachePoolG[MAX_HOSTS_CACHED];
107
108 /*
109  * Track the most recent targetHost.
110  */
111 static char *mostRecentRemoteHostG;
112
113 /**********************************************************
114  *
115  * Misc defines.
116  *
117  * To reduce malloc calls when creating lists, malloc
118  * "list" in blocks:
119  *
120  *      char *list[0..15 , 16..31, 32..63, ...]
121  */
122 #define MALLOC_BUMP_SIZE 16
123
124 #define freeAndNull(ptr)   if (ptr) free(ptr);    ptr = NULL;
125 #define ttfreeAndNull(ptr) if (ptr) tt_free(ptr); ptr = NULL;
126
127 /**********************************************************
128  *
129  * Functionality hooks.
130  *
131  *    - _DTENV_SUPPORT_COMMA_SEPARATED - turn on code that
132  *           special cases some environment variables and
133  *           treats them as comma separated and possibly
134  *           host qualified.
135  *
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.
144  *
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.
148  */           
149 #define _DTENV_SUPPORT_COMMA_SEPARATED  1
150 #undef  _DTENV_SUPPORT_MAPERROR_CACHING
151 #undef  _DTENV_OPTIMIZATION_LOCALHOST
152
153
154 /**********************************************************
155  *
156  * Performance hooks.
157  *
158  *    - DTENV_PERF_HOOK - instrunment file mapping code for
159  *                        performance measuring.
160  */
161 #undef DTENV_PERF_HOOK
162
163 #ifdef DTENV_PERF_HOOK
164 #include <time.h>
165
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 */
170
171
172 /******************************************************************************
173  ******************************************************************************
174  **
175  ** _DtEnv_tt_file_netfile()
176  ** _DtEnv_tt_netfile_file()
177  **
178  ** Both are caching versions built on the non-caching Tooltalk versions.
179  **
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
185  ** gettimeofday().
186  **
187  **     ******************* XXX Alert **************************
188  **
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.
193  **
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
197  **     their ptr value.
198  **
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.
202  **/
203
204 /*
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.
209  */
210 #define CACHE_FILEFRAG_SIZE_START     15
211 #define CACHE_FILEFRAG_SIZE_BUMP      15
212 #define CACHE_FILEFRAG_SIZE_MAX       45
213
214 /*
215  * After a cached mapping is used REMAP_AFTER times, age it out
216  * and force a recomputation of the mapping from scratch.
217  */
218 #define CACHE_FILEFRAG_REMAP_AFTER    25
219
220 /*
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
224  * ago.
225  */
226 #define CACHE_FILEFRAG_RESET_PRI     200
227
228
229 /******************************************************************************
230  *
231  * _DtEnv_tt_file_netfile()
232  *
233  * A caching version of tt_file_netfile().
234  */
235 typedef struct {
236     char          *pathFragOrig;        /* eg, /usr/dt */
237     char          *pathFragMapped;      /* eg, <from tt_file_netfile> */
238     int           cacheHit;             /* usage of */
239 } cachedFileFrag;
240
241 char *_DtEnv_tt_file_netfile(
242     const char *filename)
243 {
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;
250
251     char           *netpath;
252     int             hitval, hitIdx, i;
253     cachedFileFrag *tmpCffP;
254     char           *tmpStr;
255     int                    newCount = fragListCnt;
256
257     _DtSvcProcessLock();
258
259     if (first_time) {
260         fragList = (cachedFileFrag *) calloc( fragListAvail,
261                                                 sizeof(cachedFileFrag) );
262         first_time = 0;
263     }
264
265     /*
266      * Take care of the obvious.
267      */
268     if (!filename) {
269         _DtSvcProcessUnlock();
270         return( (char *) NULL );
271     }
272
273     /*
274      * Look for existing answer in cache.
275      *
276      * While at it, also look for least used entry just in case.
277      */
278     if (fragListCnt)
279         hitIdxStart = (hitIdxStart + 7) % fragListCnt;
280     else
281         hitIdxStart = 0;
282
283     hitIdx = hitIdxStart;
284     hitval = fragList[hitIdx].cacheHit;
285     tmpCffP = fragList;                         /* walk rather than index */
286
287     for ( i = 0; i < fragListCnt; i++ ) {
288         if (tmpCffP->cacheHit && !strcmp( filename, tmpCffP->pathFragOrig ) ) {
289             break;
290         }
291
292         if (tmpCffP->cacheHit < hitval) {
293             hitIdx = i;
294             hitval = tmpCffP->cacheHit;
295         }
296         tmpCffP++;
297     }
298
299     /*
300      * Decide what was found.
301      */
302     if ( i != fragListCnt ) {
303         /*
304          * Found a cached entry.
305          */
306         hitIdx = i;
307         if ( fragList[hitIdx].cacheHit++ > CACHE_FILEFRAG_REMAP_AFTER ) {
308             /*
309              * This looks like an old entry, so re-compute it.
310              */
311             freeAndNull( fragList[hitIdx].pathFragOrig );
312             ttfreeAndNull( fragList[hitIdx].pathFragMapped );
313             fragList[hitIdx].cacheHit = 0;      /* 0 means remap below */
314         }
315     }
316     else {
317         /*
318          * Did not find a cache entry, so scrounge around for
319          * a new entry.
320          */
321         if ( fragListCnt < fragListAvail ) {
322             /*
323              * Use next already-malloc'ed cacheEntry.
324              */
325             hitIdx = fragListCnt;
326             newCount = fragListCnt + 1;
327         }
328         else if ( fragListCnt < CACHE_FILEFRAG_SIZE_MAX ) {
329             /*
330              * Can grow fragList[]
331              */
332             fragListAvail += CACHE_FILEFRAG_SIZE_BUMP;
333             fragList = (cachedFileFrag *) realloc( (char *) fragList,
334                                         sizeof(cachedFileFrag) * fragListAvail);
335             /*
336              * Zero out new memory.
337              */
338             memset( fragList + (fragListAvail-CACHE_FILEFRAG_SIZE_BUMP),
339                     0, CACHE_FILEFRAG_SIZE_BUMP*sizeof(cachedFileFrag) );
340             hitIdx = fragListCnt;
341             newCount = fragListCnt + 1;
342         }
343         else {
344             /*
345              * Last resort - bump out the least used entry.
346              */
347             freeAndNull( fragList[hitIdx].pathFragOrig );
348             ttfreeAndNull( fragList[hitIdx].pathFragMapped );
349
350             /*
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.
354              */
355             if ( cacheGen++ > CACHE_FILEFRAG_RESET_PRI ) {
356                 cacheGen = 0;
357                 tmpCffP = fragList;
358                 for ( i = 0; i < fragListCnt; i++ ) {
359                     tmpCffP->cacheHit = 1;
360                     tmpCffP++;
361                 }
362             }
363         }
364
365         fragList[hitIdx].cacheHit = 0;          /* 0 means remap below */
366     }
367
368     if ( ! fragList[hitIdx].cacheHit ) {
369         /*
370          * Need to perform mapping.
371          */
372         netpath = tt_file_netfile( filename );
373
374 #ifdef _DTENV_SUPPORT_MAPERROR_CACHING
375         fragList[hitIdx].pathFragOrig = strdup( filename );
376         fragList[hitIdx].cacheHit = 1;
377         fragList[hitIdx].pathFragMapped = netpath;
378
379         fragListCnt = newCount;
380 #else
381         if ( tt_ptr_error(netpath) == TT_OK ) {
382             fragList[hitIdx].pathFragOrig = strdup( filename );
383             fragList[hitIdx].cacheHit = 1;
384             fragList[hitIdx].pathFragMapped = netpath;
385  
386             /*
387              * Only change the count if we are successful in adding 
388              * a new entry.
389              */
390             fragListCnt = newCount;
391         }
392         else {
393             /*
394              * Don't cache errors.   Leave this cache slot empty
395              * and it will be rediscovered and used in the future.
396              */
397             fragList[hitIdx].cacheHit = 0;
398             /*
399              * Do not change the fragListCount since we don't want to 
400              * add in error entries.
401              */
402         }
403 #endif /* _DTENV_SUPPORT_MAPERROR_CACHING */
404     }
405
406     /*
407      * Dig out answer and return it.
408      */
409 #ifdef _DTENV_SUPPORT_MAPERROR_CACHING
410     if ( tt_ptr_error(netpath) == TT_OK )
411 #else
412     if ( fragList[hitIdx].cacheHit )
413 #endif /* _DTENV_SUPPORT_MAPERROR_CACHING */
414     {
415         /*
416          * Return a tt_free-able copy of the answer.
417          */
418         tmpStr = tt_malloc( strlen(fragList[hitIdx].pathFragMapped) + 1 );
419         strcpy( tmpStr, fragList[hitIdx].pathFragMapped );
420
421         _DtSvcProcessUnlock();
422         return(tmpStr);
423     }
424     else {
425         /*
426          * See XXX comment.
427          *
428          * Since netpath is an error code, return as is.
429          */ 
430         _DtSvcProcessUnlock();
431         return(netpath);
432     }
433 }
434
435
436 /******************************************************************************
437  *
438  * _DtEnv_tt_netfile_file()
439  *
440  * A caching version of tt_netfile_file().
441  */
442 typedef struct {
443     char          *targetHost;
444     char          *pathFragOrig;        /* eg, <from tt_file_netfile> */
445     char          *pathFragMapped;      /* eg, /nfs/hostb/usr/dt */
446     int           cacheHit;             /* usage of */
447 } cachedNetfileFrag;
448
449 char *_DtEnv_tt_host_netfile_file(
450     const char *host,
451     const char *filename)
452 {
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;
459
460     char           *newfile;
461     int             hitval, hitIdx, i;
462     cachedNetfileFrag *tmpCffP;
463     char           *tmpStr;
464     int            newCount = fragListCnt;
465
466     _DtSvcProcessLock();
467     if (first_time) {
468         fragList = (cachedNetfileFrag *) calloc( fragListAvail,
469                                                 sizeof(cachedNetfileFrag) );
470         first_time = 0;
471     }
472
473     /*
474      * Take care of the obvious.
475      */
476     if (!filename) {
477         _DtSvcProcessUnlock();
478         return( (char *) NULL );
479     }
480
481     if (!host) {
482         /*
483          * Return a tt_free-able un-mapped copy.
484          */
485         tmpStr = tt_malloc( strlen(filename) + 1 );
486         strcpy( tmpStr, filename );
487
488         _DtSvcProcessUnlock();
489         return(tmpStr);
490     }
491
492     /*
493      * Look for existing answer in cache.
494      *
495      * While at it, also look for least used entry just in case.
496      */
497     if (fragListCnt)
498         hitIdxStart = (hitIdxStart + 7) % fragListCnt;
499     else
500         hitIdxStart = 0;
501
502     hitIdx = hitIdxStart;
503     hitval = fragList[hitIdx].cacheHit;
504     tmpCffP = fragList;                         /* walk rather than index */
505
506     for ( i = 0; i < fragListCnt; i++ ) {
507         if (tmpCffP->cacheHit && !strcmp( filename, tmpCffP->pathFragOrig ) ) {
508             if (!strcmp( host, tmpCffP->targetHost ) ) {
509                 break;
510             }
511         }
512
513         /*
514          * Save index of least used entry
515          */
516         if (tmpCffP->cacheHit < hitval) {
517             hitIdx = i;
518             hitval = tmpCffP->cacheHit;
519         }
520         tmpCffP++;
521     }
522
523     /*
524      * Decide what was found.
525      */
526     if ( i != fragListCnt ) {
527         /*
528          * Found a cached entry.
529          */
530         hitIdx = i;
531         if ( fragList[hitIdx].cacheHit++ > CACHE_FILEFRAG_REMAP_AFTER ) {
532             /*
533              * This looks like an old entry, so re-compute it.
534              */
535             freeAndNull( fragList[hitIdx].targetHost );
536             freeAndNull( fragList[hitIdx].pathFragOrig );
537             ttfreeAndNull( fragList[hitIdx].pathFragMapped );
538             fragList[hitIdx].cacheHit = 0;      /* 0 means remap below */
539         }
540     }
541     else {
542         /*
543          * Did not find a cache entry, so scrounge around for
544          * a new entry.
545          */
546         if ( fragListCnt < fragListAvail ) {
547             /*
548              * Use next already-malloc'ed cacheEntry.
549              */
550             hitIdx = fragListCnt;
551             newCount = fragListCnt + 1;
552         }
553         else if ( fragListCnt < CACHE_FILEFRAG_SIZE_MAX ) {
554             /*
555              * Can grow fragList[]
556              */
557             fragListAvail += CACHE_FILEFRAG_SIZE_BUMP;
558             fragList = (cachedNetfileFrag *) realloc( (char *) fragList,
559                                         sizeof(cachedNetfileFrag) * fragListAvail);
560             /*
561              * Zero out new memory.
562              */
563             memset( fragList + (fragListAvail-CACHE_FILEFRAG_SIZE_BUMP),
564                     0, CACHE_FILEFRAG_SIZE_BUMP*sizeof(cachedNetfileFrag) );
565             hitIdx = fragListCnt;
566             newCount = fragListCnt + 1;
567         }
568         else {
569             /*
570              * Last resort - bump out the least used entry.
571              */
572             freeAndNull( fragList[hitIdx].targetHost );
573             freeAndNull( fragList[hitIdx].pathFragOrig );
574             ttfreeAndNull( fragList[hitIdx].pathFragMapped );
575
576             /*
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.
580              */
581             if ( cacheGen++ > CACHE_FILEFRAG_RESET_PRI ) {
582                 cacheGen = 0;
583                 tmpCffP = fragList;
584                 for ( i = 0; i < fragListCnt; i++ ) {
585                     tmpCffP->cacheHit = 1;
586                     tmpCffP++;
587                 }
588             }
589         }
590
591         fragList[hitIdx].cacheHit = 0;          /* 0 means remap below */
592     }
593
594     if ( ! fragList[hitIdx].cacheHit ) {
595         /*
596          * Need to perform mapping.
597          */
598         newfile = tt_host_netfile_file( host, filename );
599
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;
605
606         fragListCnt = newCount;
607 #else
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;
613             /*
614              * Only change the count if we are successful in adding 
615              * a new entry.
616              */
617             fragListCnt = newCount;
618         }
619         else {
620             /*
621              * Don't cache errors.   Leave this cache slot empty
622              * and it will be rediscovered and used in the future.
623              */
624             fragList[hitIdx].cacheHit = 0;
625
626             /*
627              * Do not change the fragListCount since we are not saving
628              * error entries.
629              */
630         }
631 #endif /* _DTENV_SUPPORT_MAPERROR_CACHING */
632     }
633
634     /*
635      * Dig out answer and return it.
636      */
637 #ifdef _DTENV_SUPPORT_MAPERROR_CACHING
638     if ( tt_ptr_error(newfile) == TT_OK )
639 #else
640     if ( fragList[hitIdx].cacheHit )
641 #endif /* _DTENV_SUPPORT_MAPERROR_CACHING */
642     {
643         /*
644          * Return a tt_free-able copy of the answer.
645          */
646         tmpStr = tt_malloc( strlen(fragList[hitIdx].pathFragMapped) + 1 );
647         strcpy( tmpStr, fragList[hitIdx].pathFragMapped );
648
649         _DtSvcProcessUnlock();
650         return(tmpStr);
651     }
652     else {
653         /*
654          * See XXX comment.
655          *
656          * Since newfile is an error code, return as is.
657          */ 
658         _DtSvcProcessUnlock();
659         return(newfile);
660     }
661 }
662
663
664 /******************************************************************************
665  ******************************************************************************
666  **
667  ** Environment variable mapping code.
668  **
669  */
670
671 /******************************************************************************
672  *
673  * _DtEnvGetMapList()
674  *
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.
678  */
679 static char **_DtEnvGetMapList(
680     char  *mapListStr,
681     int   *mapListCount)
682 {
683     char **mapList;             /* decomposition of above */
684     char  *tmpPtr, *tmpPtr2;
685     int    availListSize;
686     int    firstTime;
687     _Xstrtokparams      strtok_buf;
688
689     /*
690      * Handle NULL mapListStr.
691      */
692     *mapListCount = 0;
693     if ( !mapListStr ) {
694         return( (char **) NULL );
695     }
696
697     /*
698      * Create a block of string pointers - remalloc() as needed.
699      */
700     availListSize = MALLOC_BUMP_SIZE;
701     mapList = (char **) malloc( sizeof(char *) * (availListSize) );
702
703     /*
704      * Break up the colon seperated string into an indexable array.
705      */
706     tmpPtr = strdup(mapListStr);                /* work copy for strtok */
707
708     firstTime = 1;
709     while (1) {
710         if (firstTime) {
711             tmpPtr2 = _XStrtok( tmpPtr, ":", strtok_buf );
712             firstTime = 0;
713         }
714         else
715             tmpPtr2 = _XStrtok( (char *) NULL, ":", strtok_buf );
716
717         if (tmpPtr2) {
718             /*
719              * Have possible env var name to map - make sure it exists.
720              */
721             if ( getenv(tmpPtr2) ) {
722                 (*mapListCount)++;
723                 if (*mapListCount > availListSize) {
724                     availListSize += MALLOC_BUMP_SIZE;
725                     mapList = (char **) realloc( (char *) mapList,
726                                              sizeof(char *) * (availListSize) );
727                 }
728                 mapList[*mapListCount-1] = strdup(tmpPtr2);
729             }
730         }
731         else {
732             break;
733         }
734     }
735
736     free(tmpPtr);
737
738     return( (char **) mapList );
739 }
740
741
742 /******************************************************************************
743  *
744  * _DtEnvCleanCacheSlot()
745  *
746  * Free up all memory associated with a cache slot for a targetHost.
747  */
748 static void _DtEnvCleanCacheSlot( cacheForTargetHost *targetCache )
749 {
750     int i;
751
752
753     freeAndNull( targetCache->remoteHost );
754
755     targetCache->cacheHit = 1;
756
757     freeAndNull( targetCache->mapListStr );
758
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 */
764     }
765     freeAndNull( targetCache->mapList );
766     targetCache->mapListCnt = 0;
767 }
768
769
770 /******************************************************************************
771  *
772  * _DtEnvGetTargetCache()
773  *
774  * For a specified targetHost, find existing cache information and
775  * optionally create a cache for a targetHost if one doesn't exist.
776  *
777  * The define MAX_HOSTS_CACHED controls how many targetHosts can
778  * be cached.
779  */
780 static cacheForTargetHost *_DtEnvGetTargetCache(
781     char *targetHost,
782     int   createIfNeeded)
783 {
784     static int cacheHitGen = 0;
785     static int hitIdxStart;
786     int i, hitidx, hitval;
787
788
789     /*
790      * Handle obvious.
791      */
792     if (!targetHost) {
793         return( (cacheForTargetHost *) NULL );
794     }
795
796     _DtSvcProcessLock();
797     /*
798      * Look for targetHost in current cache pool.
799      */
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++;
804                 break;
805             }
806         }
807     }
808
809     if ( i != MAX_HOSTS_CACHED ) {
810         /*
811          * targetHost is in a cache slot already.
812          */
813         _DtSvcProcessUnlock();
814         return( &cachePoolG[i] );
815     }
816     else if ( !createIfNeeded ) {
817         /*
818          * No cache slot for, and we shouldn't create one either.
819          */
820         _DtSvcProcessUnlock();
821         return( (cacheForTargetHost *) NULL );
822     }
823     else {
824         /*
825          * Find an empty cache slot or take over a rarely used slot.
826          */
827         hitIdxStart = (hitIdxStart + 7) % MAX_HOSTS_CACHED;
828
829         hitidx = hitIdxStart;
830         hitval = cachePoolG[hitidx].cacheHit;
831
832         for ( i = 0; i < MAX_HOSTS_CACHED; i++ ) {
833             if ( ! cachePoolG[i].remoteHost ) {
834                 /*
835                  * Empty slot - take it.
836                  */
837                 hitidx = i;
838                 break;
839             }
840             else if ( cachePoolG[i].cacheHit < hitval ) {
841                 hitidx = i;
842                 hitval = cachePoolG[i].cacheHit;
843             }
844         }
845
846         if ( cachePoolG[hitidx].remoteHost ) {
847             /*
848              * Cache was in use, clean first.
849              */
850             _DtEnvCleanCacheSlot( &cachePoolG[hitidx] );
851             cachePoolG[hitidx].remoteHost = strdup( targetHost );
852
853             /*
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.
857              */
858             if ( cacheHitGen++ > 50 ) {
859                 for ( i = 0; i < MAX_HOSTS_CACHED; i++ ) {
860                     cachePoolG[i].cacheHit = 1;
861                 }
862                 cacheHitGen = 1;
863             }
864         }
865         _DtSvcProcessUnlock();
866         return( &cachePoolG[hitidx] );
867     }
868 }
869
870
871 /******************************************************************************
872  *
873  * _DtEnvMapIt()
874  *
875  * Fill out a map cache for a single environment variable.
876  */
877 static void _DtEnvMapIt(
878     char *envVar,
879     cachedEnvVar *envVarCache,
880     char *targetHost)
881 {
882     char  *separator, *tmpPtr, *tmpPtr2, swapout, *netpath;
883     char  *prePend, *postPend, *newPrePend;
884
885     char **pathList;
886
887     int    availPathListSize, pathListCount, availEnvStrSize, len, tmpi, i;
888     int    considerMapping;
889     _Xstrtokparams      strtok_buf;
890
891     /*
892      * Information Layout:
893      *
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"
897      */
898     if ( (envVarCache->localEnvVarPtr = getenv( envVar )) ) {
899         envVarCache->localEnvVarCopy = strdup( envVarCache->localEnvVarPtr );
900
901         /* sneak back past "NAME=" portion. */
902         envVarCache->localEnvVarPtr -= strlen( envVar ) + 1;
903     }
904     else {
905         /*
906          * Nothing to map.   Punt.
907          */
908         envVarCache->localEnvVarCopy = (char *) NULL;
909         envVarCache->localEnvVarPtr  = (char *) NULL;
910         return;
911     }
912
913 #ifdef _DTENV_SUPPORT_COMMA_SEPARATED
914     /*
915      * Pick between colon-separated and comma-separated host-qualified
916      * mapping code.
917      */
918     if ( !strcmp(envVar, "DTDATABASESEARCHPATH") ) {
919         /*
920          * comma-separated and host-qualified mapping.
921          */
922         separator = ",";
923     }
924     else {
925         /*
926          * colon-separated mapping.
927          */
928         separator = ":";
929     }
930 #else
931     separator = ":";
932 #endif /* _DTENV_SUPPORT_COMMA_SEPARATED */
933
934     /*
935      * Break path list into elements
936      */
937     availPathListSize = MALLOC_BUMP_SIZE;
938     pathListCount = 0;
939     pathList = (char **) malloc( sizeof(char *) * availPathListSize );
940
941     /*
942      * Break up path list into an array of path elements.
943      */
944     tmpPtr = strdup( envVarCache->localEnvVarCopy );            /* work copy */
945
946     while (1) {
947         if (!pathListCount)
948             tmpPtr2 = _XStrtok( tmpPtr, separator, strtok_buf );
949         else
950             tmpPtr2 = _XStrtok( (char *) NULL, separator, strtok_buf );
951
952         if (tmpPtr2) {
953             pathListCount++;
954             if (pathListCount > availPathListSize) {
955                 availPathListSize += MALLOC_BUMP_SIZE;
956                 pathList = (char **) realloc( (char *) pathList,
957                                         sizeof(char *) * availPathListSize );
958             }
959             pathList[pathListCount-1] = strdup( tmpPtr2 );
960         }
961         else {
962             break;
963         }
964     }
965     free( tmpPtr );
966
967     /*
968      * Setup new "NAME=....." string.
969      */
970     availEnvStrSize = strlen( envVar ) + 64;
971     envVarCache->mappedEnvVarPtr = (char *) calloc( availEnvStrSize, sizeof(char) );
972     strcpy( envVarCache->mappedEnvVarPtr, envVar );
973     strcat( envVarCache->mappedEnvVarPtr, "=" );
974
975     /*
976      * Start mapping each path element.
977      */
978     for ( i = 0; i < pathListCount; i++ ) {
979         prePend  = pathList[i];
980         postPend = (char *) NULL;
981         newPrePend = (char *) NULL;
982
983         /*
984          * Assume we need to map this path element.
985          */
986         considerMapping = 1;
987
988 #ifdef _DTENV_SUPPORT_COMMA_SEPARATED
989         if ( !strcmp( separator, "," ) ) {
990             if ( DtStrchr(prePend, ':' ) ) {
991                 /*
992                  * Host qualified elements in a comma separated list
993                  * will NOT be mapped.
994                  */
995                 considerMapping = 0;
996             }
997         }
998 #endif /* _DTENV_SUPPORT_COMMA_SEPARATED */
999
1000         if (considerMapping) {
1001             /*
1002              * Tear apart and check for so called substitution characters.
1003              */
1004             if (( tmpPtr = DtStrchr(prePend, '%') )) {
1005                 /*
1006                  * Temporarly shorten path up to substitution character.
1007                  */
1008                 swapout = *tmpPtr;
1009                 *tmpPtr = '\0';
1010
1011                 /*
1012                  * Move the dividing point back to a directory element.
1013                  */
1014                 tmpPtr2 = DtStrrchr( prePend, '/' );
1015
1016                 /*
1017                  * Restore the send half of the string.
1018                  */
1019                 *tmpPtr = swapout;
1020
1021                 if (tmpPtr2) {
1022                     /*
1023                      * Can do a split around the "/".
1024                      *
1025                      * Will have "<prePath>/" and "/<postPath>".
1026                      */
1027                     postPend = strdup( tmpPtr2 );
1028                     *(tmpPtr2 + mblen(tmpPtr2, MB_CUR_MAX)) = '\0';
1029                 }
1030             }
1031
1032 #ifdef DTENV_PERF_HOOK
1033             {
1034                 int tpi;
1035                 extern unsigned long stopwatch_tt_file_netfile;
1036                 extern int stopwatch_repeat_rate;
1037
1038                 struct timeval  start, stop;
1039                 struct timezone junk;
1040
1041                 gettimeofday( &start, &junk );
1042
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 );
1047                 }
1048                 netpath = _DtEnv_tt_file_netfile( prePend );
1049
1050                 gettimeofday( &stop, &junk );
1051
1052                 if (start.tv_usec > stop.tv_usec) {
1053                     stop.tv_usec += 1000000;
1054                     stop.tv_sec--;
1055                 }
1056
1057                 stopwatch_tt_file_netfile += (stop.tv_usec - start.tv_usec);
1058                 stopwatch_tt_file_netfile += (stop.tv_sec  - start.tv_sec) * 1000000;
1059             }
1060 #else
1061             netpath = _DtEnv_tt_file_netfile( prePend );
1062 #endif /* DTENV_PERF_HOOK */
1063             if ( tt_ptr_error(netpath) != TT_OK ) {
1064                 newPrePend = (char *) NULL;
1065             }
1066             else {
1067 #ifdef DTENV_PERF_HOOK
1068                 {
1069                     int tpi;
1070                     extern unsigned long stopwatch_tt_netfile_file;
1071                     extern int stopwatch_repeat_rate;
1072
1073                     struct timeval  start, stop;
1074                     struct timezone junk;
1075
1076                     gettimeofday( &start, &junk );
1077
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 );
1082
1083                     }
1084                     newPrePend = _DtEnv_tt_host_netfile_file (targetHost, netpath);
1085
1086                     gettimeofday( &stop, &junk );
1087
1088                     if (start.tv_usec > stop.tv_usec) {
1089                         stop.tv_usec += 1000000;
1090                         stop.tv_sec--;
1091                     }
1092
1093                     stopwatch_tt_netfile_file += (stop.tv_usec - start.tv_usec);
1094                     stopwatch_tt_netfile_file += (stop.tv_sec  - start.tv_sec) * 1000000;
1095                 }
1096 #else
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;
1101                 }
1102                 ttfreeAndNull( netpath );
1103             }
1104         }
1105
1106         /*
1107          * Calculate length of the new path element to the new path list.
1108          */
1109         tmpi = strlen(envVarCache->mappedEnvVarPtr)+1;  /* current list + ... */
1110         if ( i != 0 )
1111             tmpi += 1;                                  /* separator */
1112         if (newPrePend)
1113                 tmpi += strlen(newPrePend);             /* new prePend or ... */
1114         else
1115                 tmpi += strlen(prePend);                /* ... old prePend */
1116         if (postPend)
1117                 tmpi += strlen(postPend);               /* new postPend */
1118
1119         if ( tmpi > availEnvStrSize ) {
1120             /*
1121              * Grow new mappedEnvVar space.
1122              */
1123             availEnvStrSize = tmpi + 64;
1124             envVarCache->mappedEnvVarPtr = (char *) realloc(
1125                                 (char *) envVarCache->mappedEnvVarPtr,
1126                                 availEnvStrSize );
1127         }
1128
1129         /*
1130          * Add the new path element.
1131          */
1132         if ( i != 0 )
1133             strcat( envVarCache->mappedEnvVarPtr, separator );
1134
1135         if (newPrePend)
1136             strcat( envVarCache->mappedEnvVarPtr, newPrePend );
1137         else
1138             strcat( envVarCache->mappedEnvVarPtr, prePend );
1139
1140         if (postPend)
1141             strcat( envVarCache->mappedEnvVarPtr, postPend );
1142
1143         freeAndNull( prePend );         /* aka pathList[i] */
1144         ttfreeAndNull( newPrePend );
1145         freeAndNull( postPend );
1146     }
1147     freeAndNull( pathList );
1148 }
1149
1150 /******************************************************************************
1151  *
1152  * _DtEnvGetMapInformation()
1153  */
1154 #define _DtEnv_MAX_BUF_SIZE 1024
1155 #define _DtEnv_NULL_GUARD(s) ((s) ? (s) : "")
1156
1157 static char *_DtEnvGetMapInformation( void )
1158 {
1159    char        *mapInfo;
1160    char         nameBuf[_DtEnv_MAX_BUF_SIZE];
1161    char         classBuf[_DtEnv_MAX_BUF_SIZE];
1162    XrmValue     resource_value;
1163    XrmDatabase  db;
1164    char         *rep_type;
1165    int          bytesNeeded;
1166    char        *name;
1167    char        *class;
1168
1169    extern char    *_DtApplicationName;          /* set in DtUtil.c */
1170    extern char    *_DtApplicationClass;
1171    extern Display *_DtDisplay;
1172
1173
1174    /*
1175     * See if an environment variable has been set.   If so, get
1176     * the map info from there.
1177     */
1178    mapInfo = getenv( _DTENV_MAP_ENV_VAR );
1179
1180    if (mapInfo)
1181         return( XtNewString( mapInfo ) );
1182
1183    /*
1184     * Try to get map info from the resource database.
1185     */
1186    bytesNeeded = strlen(_DTENV_MAP_RESOURCE_NAME)
1187                        + strlen(_DtApplicationName) + 4;
1188    if ( bytesNeeded > _DtEnv_MAX_BUF_SIZE )
1189         name = XtMalloc(bytesNeeded);
1190    else
1191         name = nameBuf;
1192
1193    sprintf (name, "%s*%s",
1194         _DtEnv_NULL_GUARD( _DtApplicationName) , _DTENV_MAP_RESOURCE_NAME);
1195
1196    bytesNeeded = strlen(_DTENV_MAP_RESOURCE_CLASS)
1197                        + strlen(_DtApplicationClass) + 4;
1198    if ( bytesNeeded > _DtEnv_MAX_BUF_SIZE )
1199         class = XtMalloc(bytesNeeded);
1200    else
1201         class = classBuf;
1202    sprintf (class, "%s*%s",
1203         _DtEnv_NULL_GUARD(_DtApplicationClass) , _DTENV_MAP_RESOURCE_CLASS);
1204
1205    db = XtDatabase (_DtDisplay);
1206    if (XrmGetResource (db, nameBuf, classBuf, &rep_type, &resource_value))
1207       mapInfo = (char *) resource_value.addr;
1208    else
1209       mapInfo = (char *) NULL;
1210
1211    if ( name != nameBuf )
1212         XtFree(name);
1213    if ( class != classBuf )
1214         XtFree(class);
1215
1216    if (mapInfo)
1217         return( XtNewString( mapInfo ) );
1218    else
1219         return( (char *) NULL );
1220 }
1221
1222 /******************************************************************************
1223  *
1224  * _DtEnvMapForRemote()
1225  *
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
1229  * computations.
1230  */
1231 void _DtEnvMapForRemote (char *targetHost)
1232 {
1233     int    i, cacheRegen;
1234     char  *mapListStr, *tmpPtr;
1235     char **mapList;
1236
1237     cacheForTargetHost *targetCache;
1238     int ttMark = 0;
1239
1240     extern char *mostRecentRemoteHostG;
1241
1242     _DtSvcProcessLock();
1243     if (mostRecentRemoteHostG) {
1244         /*
1245          * Warning - a _DtEnvRestoreLocal() was not called for
1246          * the most recent _DtEnvMapForRemote().  Tossing.
1247          */
1248         freeAndNull(mostRecentRemoteHostG);
1249     }
1250
1251     if (!targetHost) {
1252         /*
1253          * No target host to cache.
1254          */
1255         _DtSvcProcessUnlock();
1256         return;
1257     }
1258
1259     /*
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.
1264      */
1265     ttMark = tt_mark();
1266     switch ( tt_ptr_error(
1267                 _DtEnv_tt_host_netfile_file(targetHost, 
1268                     _DtEnv_tt_file_netfile(getenv("HOME")) ) ) )
1269     {
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 */
1273             /*
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.
1276              */
1277             tt_release(ttMark); /* free up tooltalk memory used for test */
1278             _DtSvcProcessUnlock();
1279             return;  
1280             break;
1281          default:
1282             tt_release(ttMark); /* free up tooltalk memory used for test */
1283             break;
1284     }
1285
1286
1287 #ifdef _DTENV_OPTIMIZATION_LOCALHOST
1288     /*
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.
1293      */
1294     if ( <unimplemented - targetHost is local test> ) {
1295         mostRecentRemoteHostG = "localhost";
1296         _DtSvcProcessUnlock();
1297         return;
1298     }
1299 #endif /* _DTENV_OPTIMIZATION_LOCALHOST */
1300
1301     /*
1302      * Get list of env vars to be mapped.
1303      */
1304     mapListStr = _DtEnvGetMapInformation();
1305
1306     if (!mapListStr) {
1307         /*
1308          * Nothing to map.
1309          */
1310         _DtSvcProcessUnlock();
1311         return;
1312     }
1313
1314     /*
1315      * We have a targetHost that needs some mapping done.   Start
1316      * stashing data away.
1317      */
1318     mostRecentRemoteHostG = strdup( targetHost );
1319     _DtSvcProcessUnlock();
1320
1321     /*
1322      * Find or allocate a cache entry.
1323      */
1324     targetCache = _DtEnvGetTargetCache( targetHost, 1 );
1325
1326     /*
1327      * See if cache information for targetHost is available, and
1328      * if so, if it still looks valid.
1329      *
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
1334      * fairly static.
1335      */
1336     if (targetCache->mapListStr) {
1337         if ( !strcmp( targetCache->mapListStr, mapListStr ) ) {
1338             /*
1339              * Atleast the list of environment variables that need
1340              * to be mapped is the same as previous.
1341              */
1342             cacheRegen = 0;
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 */
1348                         break;
1349                     }
1350                 }
1351                 else {
1352                     /*
1353                      * Env Var does not exist, but maybe it never did.
1354                      */
1355                     if (targetCache->mapListDetails[i].localEnvVarCopy) {
1356                         /*
1357                          * Was in cache, but now no longer exists.
1358                          */
1359                         cacheRegen = 1; /* env var no longer exists */
1360                         break;
1361                     }
1362                 }
1363             }
1364         }
1365         else {
1366             cacheRegen = 1;     /* map list changed - need to regen cache */
1367         }
1368     }
1369     else {
1370         cacheRegen = 1;         /* need to create cache */
1371     }
1372
1373     if (cacheRegen) {
1374         /*
1375          * Toss out the old.
1376          */
1377         _DtEnvCleanCacheSlot(targetCache);
1378
1379         /*
1380          * Bring in the new.
1381          */
1382         targetCache->remoteHost = strdup( targetHost );
1383         targetCache->mapListStr = strdup( mapListStr );
1384         targetCache->mapList = _DtEnvGetMapList( mapListStr,
1385                                         &(targetCache->mapListCnt) );
1386
1387         targetCache->mapListDetails = (cachedEnvVar *)
1388                                         malloc( sizeof(cachedEnvVar) *
1389                                             targetCache->mapListCnt );
1390
1391         for ( i = 0; i < targetCache->mapListCnt; i++ ) {
1392             _DtEnvMapIt( targetCache->mapList[i],
1393                          &(targetCache->mapListDetails[i]),
1394                          targetHost );
1395         }
1396     }
1397     else {
1398         /*
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.
1403          */
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;
1409             }
1410         }
1411     }
1412
1413     /*
1414      * Install the mapped environment variables.
1415      */
1416     for ( i = 0; i < targetCache->mapListCnt; i++ ) {
1417         putenv( targetCache->mapListDetails[i].mappedEnvVarPtr );
1418     }
1419
1420     XtFree(mapListStr);
1421 }
1422
1423
1424 /******************************************************************************
1425  *
1426  * _DtEnvRestoreLocal()
1427  *
1428  * Presuming a _DtEnvMapForRemote() was called, _DtEnvRestoreLocal()
1429  * restores the original envirnment settings for a number of
1430  * environment variables.
1431  */
1432 void _DtEnvRestoreLocal ()
1433 {
1434     extern char   *mostRecentRemoteHostG;
1435     cacheForTargetHost  *targetCache;
1436     char          *tmpP;
1437     int            i;
1438
1439     _DtSvcProcessLock();
1440     if (mostRecentRemoteHostG) {
1441 #ifdef _DTENV_OPTIMIZATION_LOCALHOST
1442         /*
1443          * See comment with the other ifdef'ed block.
1444          *
1445          * If localhost, then nothing to restore.
1446          */
1447         if ( !strcmp(mostRecentRemoteHostG, "localhost") ) {
1448             mostRecentRemoteHostG = (char *) NULL;
1449             _DtSvcProcessUnlock();
1450             return;
1451         }
1452 #endif /* _DTENV_OPTIMIZATION_LOCALHOST */
1453
1454         targetCache = _DtEnvGetTargetCache( mostRecentRemoteHostG, 0 );
1455
1456         if (targetCache) {
1457             /*
1458              * Install the mapped environment variables.
1459              */
1460             for ( i = 0; i < targetCache->mapListCnt; i++ ) {
1461                 tmpP = targetCache->mapListDetails[i].localEnvVarPtr;
1462                 if ( tmpP ) {
1463                     putenv( targetCache->mapListDetails[i].localEnvVarPtr );
1464                 }
1465             }
1466         }
1467
1468         freeAndNull( mostRecentRemoteHostG );
1469     }
1470     _DtSvcProcessUnlock();
1471 }
1472