1 /*
2  * Copyright (C) by Argonne National Laboratory
3  *     See COPYRIGHT in top-level directory
4  */
5 
6 #include "mpichconf.h"
7 
8 #include <stdio.h>
9 #ifdef HAVE_UNISTD_H
10 #include <unistd.h>
11 #endif
12 #ifdef HAVE_STDLIB_H
13 #include <stdlib.h>
14 #endif
15 #ifdef HAVE_STRING_H
16 #include <string.h>
17 #endif
18 #if defined(HAVE_PUTENV) && defined(NEEDS_PUTENV_DECL)
19 extern int putenv(char *string);
20 #endif
21 
22 #include "pmutil.h"
23 #include "process.h"
24 #include "env.h"
25 #include "cmnargs.h"    /* for mpiexec_usage */
26 
27 /*
28  *
29  */
30 
31 /*
32  * This routine may be called by MPIE_Args to handle any environment arguments
33  * Returns the number of arguments to skip (0 if argument is not recognized
34  * as an environment control)
35  */
MPIE_ArgsCheckForEnv(int argc,char * argv[],ProcessWorld * pWorld,EnvInfo ** appEnv)36 int MPIE_ArgsCheckForEnv(int argc, char *argv[], ProcessWorld * pWorld, EnvInfo ** appEnv)
37 {
38     int i, incr = 0;
39     EnvInfo *env;
40     char *cmd;
41 
42     if (strncmp(argv[0], "-env", 4) == 0) {
43         if (!*appEnv) {
44             env = (EnvInfo *) MPL_malloc(sizeof(EnvInfo), MPL_MEM_PM);
45             env->includeAll = 1;
46             env->envPairs = 0;
47             env->envNames = 0;
48             *appEnv = env;
49         } else
50             env = *appEnv;
51         cmd = argv[0] + 4;
52     } else if (strncmp(argv[0], "-genv", 5) == 0) {
53         if (!pWorld->genv) {
54             env = (EnvInfo *) MPL_malloc(sizeof(EnvInfo), MPL_MEM_PM);
55             env->includeAll = 1;
56             env->envPairs = 0;
57             env->envNames = 0;
58             pWorld->genv = env;
59         }
60         env = pWorld->genv;
61         cmd = argv[0] + 5;
62     } else
63         return 0;
64 
65     /* genv and env commands have the same form, just affect different
66      * env structures.  We handle this by identifying which structure,
67      * then checkout the remaining command */
68     if (!cmd[0]) {
69         /* A basic name value command */
70         EnvData *p;
71         if (!argv[1] || !argv[2]) {
72             mpiexec_usage("Missing arguments to -env or -genv");
73         }
74         p = (EnvData *) MPL_malloc(sizeof(EnvData), MPL_MEM_PM);
75         p->name = (const char *) MPL_strdup(argv[1]);
76         p->value = (const char *) MPL_strdup(argv[2]);
77         p->envvalue = 0;
78         p->nextData = env->envPairs;
79         env->envPairs = p;
80 
81         incr = 3;
82     } else if (strcmp(cmd, "none") == 0) {
83         env->includeAll = 0;
84         incr = 1;
85     } else if (strcmp(cmd, "list") == 0) {
86         /* argv[1] has a list of names, separated by commas */
87         EnvData *p;
88         char *lPtr = argv[1], *name;
89         int namelen;
90 
91         if (!argv[1]) {
92             mpiexec_usage("Missing argument to -envlist or -genvlist");
93         }
94         while (*lPtr) {
95             name = lPtr++;
96             while (*lPtr && *lPtr != ',')
97                 lPtr++;
98             /* The length of any environment string will fit in an int */
99             namelen = (int) (lPtr - name);
100             p = (EnvData *) MPL_malloc(sizeof(EnvData), MPL_MEM_PM);
101             p->value = 0;
102             p->name = (const char *) MPL_malloc(namelen + 1, MPL_MEM_PM);
103             p->envvalue = 0;
104             for (i = 0; i < namelen; i++)
105                 ((char *) p->name)[i] = name[i];
106             ((char *) p->name)[namelen] = 0;
107 
108             p->nextData = env->envNames;
109             env->envNames = p;
110             if (*lPtr == ',')
111                 lPtr++;
112         }
113         incr = 2;
114     } else {
115         /* Unrecognized env argument. */
116         incr = 0;
117     }
118 
119     return incr;
120 }
121 
122 /*
123   Setup the environment of a process for a given process state.
124   This handles the options for the process world and app
125 
126   Input Arguments:
127   pState - process state structure
128   envp   - Base (pre-existing) environment.  Note that this should
129            be the envp from main() (see below)
130   maxclient - size of client_envp array
131 
132   Output Arguments:
133   client_envp -
134 
135   Side Effects:
136   If envnone or genvnone was selected, the environment variables in envp
137   will be removed with unsetenv() or by direct manipulation of the envp
138   array (for systems that do not support unsetenv, envp must be the
139   array pass into main()).
140 
141   Returns the number of items set in client_envp, or -1 on error.
142  */
MPIE_EnvSetup(ProcessState * pState,char * envp[],char * client_envp[],int maxclient)143 int MPIE_EnvSetup(ProcessState * pState, char *envp[], char *client_envp[], int maxclient)
144 {
145     ProcessWorld *pWorld;
146     ProcessApp *app;
147     EnvInfo *env;
148     EnvData *wPairs = 0, *wNames = 0, *aPairs = 0, *aNames = 0;
149     int includeAll = 1;
150     int irc = 0;
151     int debug = 1;
152 
153     app = pState->app;
154     pWorld = app->pWorld;
155 
156     /* Get the world defaults */
157     env = pWorld->genv;
158     if (env) {
159         includeAll = env->includeAll;
160         wPairs = env->envPairs;
161         wNames = env->envNames;
162     }
163 
164     /* Get the app values (overrides includeAll) */
165     env = app->env;
166     if (env) {
167         if (includeAll) {
168             /* Let the local env set envnone (there is no way to undo
169              * -genvnone) */
170             includeAll = env->includeAll;
171         }
172         aPairs = env->envPairs;
173         aNames = env->envNames;
174     }
175 
176     if (includeAll) {
177         if (envp) {
178             int j;
179             for (j = 0; envp[j] && j < maxclient; j++) {
180                 putenv(envp[j]);
181                 client_envp[j] = envp[j];
182             }
183             irc = j;
184         } else
185             irc = 0;
186     } else {
187         MPIE_UnsetAllEnv(envp);
188         irc = 0;
189     }
190 
191     while (wPairs) {
192         if (putenv((char *) (wPairs->envvalue))) {
193             irc = -1;
194             if (debug)
195                 perror("putenv(wPairs) failed: ");
196         }
197         wPairs = wPairs->nextData;
198     }
199 
200     while (wNames) {
201         if (putenv((char *) (wNames->envvalue))) {
202             irc = -1;
203             if (debug)
204                 perror("putenv(wNames) failed: ");
205         }
206         wNames = wNames->nextData;
207     }
208 
209     while (aPairs) {
210         if (putenv((char *) (aPairs->envvalue))) {
211             irc = -1;
212             if (debug)
213                 perror("putenv(aPairs) failed: ");
214         }
215         aPairs = aPairs->nextData;
216     }
217 
218     while (aNames) {
219         if (putenv((char *) (aNames->envvalue))) {
220             irc = -1;
221             if (debug) {
222                 perror("putenv(aNames) failed: ");
223             }
224         }
225         aNames = aNames->nextData;
226     }
227 
228     return irc;
229 }
230 
231 /*
232   Initialize the environment data
233 
234   Builds the envvalue version of the data, using the given data.
235   if getValue is true, get the value for the name with getenv .
236  */
MPIE_EnvInitData(EnvData * elist,int getValue)237 int MPIE_EnvInitData(EnvData * elist, int getValue)
238 {
239     const char *value;
240     char *str;
241     int rc;
242     size_t slen;
243 
244     while (elist) {
245         /* Skip variables that already have value strings */
246         if (!elist->envvalue) {
247             if (getValue) {
248                 value = (const char *) getenv(elist->name);
249             } else {
250                 value = elist->value;
251             }
252             if (!value) {
253                 /* Special case for an empty value */
254                 value = "";
255             }
256             slen = strlen(elist->name) + strlen(value) + 2;
257             str = (char *) MPL_malloc(slen, MPL_MEM_PM);
258             if (!str) {
259                 return 1;
260             }
261             MPL_strncpy(str, elist->name, slen);
262             if (value && *value) {
263                 rc = MPL_strnapp(str, "=", slen);
264                 rc += MPL_strnapp(str, value, slen);
265                 if (rc) {
266                     return 1;
267                 }
268             }
269             elist->envvalue = (const char *) str;
270         }
271         elist = elist->nextData;
272     }
273     return 0;
274 }
275 
276 /*
277  * Add an enviroinment variable to the global list of variables
278  */
MPIE_Putenv(ProcessWorld * pWorld,const char * env_string)279 int MPIE_Putenv(ProcessWorld * pWorld, const char *env_string)
280 {
281     EnvInfo *genv;
282     EnvData *p;
283 
284     /* FIXME: This should be getGenv (so allocation/init in one place) */
285     if (!pWorld->genv) {
286         genv = (EnvInfo *) MPL_malloc(sizeof(EnvInfo), MPL_MEM_PM);
287         genv->includeAll = 1;
288         genv->envPairs = 0;
289         genv->envNames = 0;
290         pWorld->genv = genv;
291     }
292     genv = pWorld->genv;
293 
294     p = (EnvData *) MPL_malloc(sizeof(EnvData), MPL_MEM_PM);
295     if (!p)
296         return 1;
297     p->name = 0;
298     p->value = 0;
299     p->envvalue = (const char *) MPL_strdup(env_string);
300     if (!p->envvalue)
301         return 1;
302     p->nextData = genv->envPairs;
303     genv->envPairs = p;
304 
305     return 0;
306 }
307 
308 
309 /* Unset all environment variables.
310 
311    Not all systems support unsetenv (e.g., System V derived systems such
312    as Solaris), so we have to provide our own implementation.
313 
314    In addition, there are various ways to determine the "current" environment
315    variables.  One is to pass a third arg to main; the other is a global
316    variable.
317 
318    Also, we prefer the environ variable over envp, because exec often
319    prefers what is in environ rather than the envp (envp appears to be a
320    copy, and unsetting the env doesn't always change the environment
321    that exec creates.  This seems wrong, but it was what was observed
322    on Linux).
323 
324 */
325 #ifdef HAVE_EXTERN_ENVIRON
326 #ifdef NEEDS_ENVIRON_DECL
327 extern char **environ;
328 #endif
MPIE_UnsetAllEnv(char * envp[])329 int MPIE_UnsetAllEnv(char *envp[])
330 {
331     /* Ignore envp because environ is the real array that controls
332      * the environment used by getenv/putenv/etc */
333     char **ep = environ;
334 
335     while (*ep)
336         *ep++ = 0;
337     return 0;
338 }
339 #elif defined(HAVE_UNSETENV)
MPIE_UnsetAllEnv(char * envp[])340 int MPIE_UnsetAllEnv(char *envp[])
341 {
342     int j;
343 
344     for (j = 0; envp[j]; j++) {
345         unsetenv(envp[j]);
346     }
347     return 0;
348 }
349 #else
350 /* No known way to unset the environment.  Return failure */
MPIE_UnsetAllEnv(char * envp[])351 int MPIE_UnsetAllEnv(char *envp[])
352 {
353     return 1;
354 }
355 #endif
356