Apache Mesos
environment.hpp
Go to the documentation of this file.
1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 // http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License.
12 
13 #ifndef __STOUT_OS_RAW_ENVIRONMENT_HPP__
14 #define __STOUT_OS_RAW_ENVIRONMENT_HPP__
15 
16 #include <string.h>
17 
18 #include <string>
19 
20 #include <stout/foreach.hpp>
21 #include <stout/json.hpp>
22 #include <stout/stringify.hpp>
23 
24 #ifdef __APPLE__
25 #include <crt_externs.h> // For _NSGetEnviron().
26 #elif !defined(__WINDOWS__)
27 // Need to declare 'environ' pointer for platforms that are not OS X or Windows.
28 extern char** environ;
29 #endif
30 
31 
32 // NOTE: The raw environment functions have been removed from the Windows API
33 // because the CRT `environ` macro should never be used:
34 // (1) The CRT APIs (like `environ`) are extremely old and essentially
35 // unmaintained; it is virtually always preferable to use the equivalent
36 // Win32 APIs (such as `GetEnvironmentStrings`).
37 // (2) `environ` is not at all compatible with the Win32 APIs; if you
38 // `SetEnvironmentVariable`, it will not appear in `environ`.
39 // (3) The CRT APIs implemented around `environ` (i.e., `setenv`, `getenv`)
40 // have significant differences from almost all POSIX implementations.
41 // For example, calling `setenv` with a blank string as the value will
42 // delete the environment variable on Windows, but not on POSIX.
43 // (4) It is ungainly to implement the `char**` return using the Win32 APIs;
44 // since `GetEnvironmentStrings` returns a completely different type,
45 // we'd have to allocate a char** environment statically, or manage
46 // allocation semantics of the `char**` differently across Unix and
47 // Windows.
48 //
49 // NOTE: the `os::raw` namespace contains a family of simple wrapper functions
50 // for getting environment data from Unix machines. For example,
51 // `os::raw::environment` returns an "unstructured" `char**` that contains the
52 // raw environment variables of the executing process. Accessing "structured"
53 // version of this function, `os::environment`, returns a `map<string, string>`
54 // instead. This family of functions exists in the `os::raw` namespace because
55 // of the unstructured nature of their return values.
56 //
57 // NOTE: These functions were originally called `environment` and not `environ`
58 // because on Windows, `environ` is a macro, and not an `extern char**` as it
59 // is in the POSIX standard. The existence of this macro on Windows makes it
60 // impossible to use a function called `os::environ`.
61 namespace os {
62 namespace raw {
63 
64 // NOTE: It is important this remain disabled on Windows. See first note above.
65 #ifndef __WINDOWS__
66 inline char** environment()
67 {
68  // Accessing the list of environment variables is platform-specific.
69  // On OS X, the 'environ' symbol isn't visible to shared libraries,
70  // so we must use the _NSGetEnviron() function (see 'man environ' on
71  // OS X). On other platforms, it's fine to access 'environ' from
72  // shared libraries.
73 #ifdef __APPLE__
74  return *_NSGetEnviron();
75 #else
76  // NOTE: the correct style for this expression would be `::environ`, but we
77  // leave it out because `environ` is a macro on Windows, and the `::` will
78  // break the build.
79  return environ;
80 #endif
81 }
82 #endif // __WINDOWS__
83 
84 
85 // Returns the address of os::environment().
86 // NOTE: It is important this remain disabled on Windows. See first note above.
87 #ifndef __WINDOWS__
88 inline char*** environmentp()
89 {
90  // Accessing the list of environment variables is platform-specific.
91  // On OS X, the 'environ' symbol isn't visible to shared libraries,
92  // so we must use the _NSGetEnviron() function (see 'man environ' on
93  // OS X). On other platforms, it's fine to access 'environ' from
94  // shared libraries.
95 #ifdef __APPLE__
96  return _NSGetEnviron();
97 #else
98  // NOTE: the correct style for this expression would be `environ`, but we
99  // leave it out because `environ` is a macro on Windows, and the `::` will
100  // break the build.
101  return &environ;
102 #endif
103 }
104 #endif // __WINDOWS__
105 
106 
107 // Represents the environment variable list expected by 'exec'
108 // routines. The environment variable list is an array of pointers
109 // that point to null-terminated strings. The array of pointers must
110 // be terminated by a nullptr. To use this abstraction, see the
111 // following example:
112 //
113 // map<string, string> environment = {
114 // {"key1", "value1"},
115 // {"key2", "value2"}
116 // };
117 // os::raw::Envp envp(environment);
118 // execle("/bin/sh", "sh", "-c", "echo hello", envp);
119 class Envp
120 {
121 public:
122  Envp(Envp&& that)
123  : envp(that.envp),
124  size(that.size),
125  environment(that.environment)
126  {
127  that.envp = nullptr;
128  that.size = 0;
129  that.environment = std::map<std::string, std::string>();
130  }
131 
132  template <typename Map>
133  explicit Envp(const Map& map)
134  {
135  size = map.size();
136 
137  // NOTE: We add 1 to the size for a `nullptr` terminator.
138  envp = new char*[size + 1];
139  size_t index = 0;
140 
141  for (auto it = map.begin(); it != map.end(); ++it) {
142  environment[stringify(it->first)] = stringify(it->second);
143  std::string entry = stringify(it->first) + "=" + stringify(it->second);
144  envp[index] = new char[entry.size() + 1];
145  ::memcpy(envp[index], entry.c_str(), entry.size() + 1);
146  ++index;
147  }
148 
149  envp[index] = nullptr;
150  }
151 
152  explicit Envp(const JSON::Object& object)
153  {
154  size = object.values.size();
155 
156  // NOTE: We add 1 to the size for a `nullptr` terminator.
157  envp = new char*[size + 1];
158  size_t index = 0;
159 
160  foreachpair (const std::string& key,
161  const JSON::Value& value,
162  object.values) {
163  environment[key] = stringify(value.as<JSON::String>().value);
164  std::string entry = key + "=" + value.as<JSON::String>().value;
165  envp[index] = new char[entry.size() + 1];
166  ::memcpy(envp[index], entry.c_str(), entry.size() + 1);
167  ++index;
168  }
169 
170  envp[index] = nullptr;
171  }
172 
174  {
175  if (envp == nullptr) {
176  return;
177  }
178 
179  for (size_t i = 0; i < size; i++) {
180  delete[] envp[i];
181  }
182  delete[] envp;
183  }
184 
185  operator char**() const
186  {
187  return envp;
188  }
189 
190  operator std::map<std::string, std::string>()
191  {
192  return environment;
193  }
194 
195 private:
196  Envp(const Envp&) = delete;
197  Envp& operator=(const Envp&) = delete;
198 
199  char **envp;
200  size_t size;
201  std::map<std::string, std::string> environment;
202 };
203 
204 } // namespace raw {
205 } // namespace os {
206 
207 #endif // __STOUT_OS_RAW_ENVIRONMENT_HPP__
const T & as() const
Definition: json.hpp:342
char *** environmentp()
Definition: environment.hpp:88
Definition: json.hpp:154
Envp(const JSON::Object &object)
Definition: environment.hpp:152
Iterable< V > map(F &&f, const Iterable< U, Us...> &input)
Definition: lambda.hpp:45
Definition: environment.hpp:119
#define foreachpair(KEY, VALUE, ELEMS)
Definition: foreach.hpp:51
Definition: json.hpp:245
Envp(const Map &map)
Definition: environment.hpp:133
Definition: json.hpp:75
char ** environ
Envp(Envp &&that)
Definition: environment.hpp:122
std::string stringify(int flags)
std::string value
Definition: json.hpp:80
~Envp()
Definition: environment.hpp:173
char ** environment()
Definition: environment.hpp:66