Mercurial > hg > gpsynth
comparison third_party/boost/process/operations.hpp @ 0:add35537fdbb tip
Initial import
author | irh <ian.r.hobson@gmail.com> |
---|---|
date | Thu, 25 Aug 2011 11:05:55 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:add35537fdbb |
---|---|
1 // | |
2 // Boost.Process | |
3 // ~~~~~~~~~~~~~ | |
4 // | |
5 // Copyright (c) 2006, 2007 Julio M. Merino Vidal | |
6 // Copyright (c) 2008 Ilya Sokolov, Boris Schaeling | |
7 // Copyright (c) 2009 Boris Schaeling | |
8 // Copyright (c) 2010 Felipe Tanus, Boris Schaeling | |
9 // | |
10 // Distributed under the Boost Software License, Version 1.0. (See accompanying | |
11 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
12 // | |
13 | |
14 /** | |
15 * \file boost/process/operations.hpp | |
16 * | |
17 * Provides miscellaneous free functions. | |
18 */ | |
19 | |
20 #ifndef BOOST_PROCESS_OPERATIONS_HPP | |
21 #define BOOST_PROCESS_OPERATIONS_HPP | |
22 | |
23 #include <boost/process/config.hpp> | |
24 | |
25 #if defined(BOOST_POSIX_API) | |
26 # include <boost/process/detail/posix_helpers.hpp> | |
27 # include <utility> | |
28 # include <cstddef> | |
29 # include <stdlib.h> | |
30 # include <unistd.h> | |
31 # include <fcntl.h> | |
32 # if defined(__CYGWIN__) | |
33 # include <boost/scoped_array.hpp> | |
34 # include <sys/cygwin.h> | |
35 # endif | |
36 #elif defined(BOOST_WINDOWS_API) | |
37 # include <boost/process/detail/windows_helpers.hpp> | |
38 # include <boost/scoped_array.hpp> | |
39 # include <boost/shared_array.hpp> | |
40 # include <windows.h> | |
41 #else | |
42 # error "Unsupported platform." | |
43 #endif | |
44 | |
45 #include <boost/process/child.hpp> | |
46 #include <boost/process/context.hpp> | |
47 #include <boost/process/stream_id.hpp> | |
48 #include <boost/process/stream_ends.hpp> | |
49 #include <boost/process/handle.hpp> | |
50 #include <boost/filesystem/path.hpp> | |
51 #include <boost/algorithm/string/predicate.hpp> | |
52 #include <boost/system/system_error.hpp> | |
53 #include <boost/throw_exception.hpp> | |
54 #include <boost/assert.hpp> | |
55 #include <string> | |
56 #include <vector> | |
57 #include <map> | |
58 #include <utility> | |
59 | |
60 namespace boost { | |
61 namespace process { | |
62 | |
63 /** | |
64 * Locates the executable program \a file in all the directory components | |
65 * specified in \a path. If \a path is empty, the value of the PATH | |
66 * environment variable is used. | |
67 * | |
68 * The path variable is interpreted following the same conventions used | |
69 * to parse the PATH environment variable in the underlying platform. | |
70 * | |
71 * \throw boost::filesystem::filesystem_error If the file cannot be found | |
72 * in the path. | |
73 */ | |
74 inline std::string find_executable_in_path(const std::string &file, | |
75 std::string path = "") | |
76 { | |
77 #if defined(BOOST_POSIX_API) | |
78 BOOST_ASSERT(file.find('/') == std::string::npos); | |
79 #elif defined(BOOST_WINDOWS_API) | |
80 BOOST_ASSERT(file.find_first_of("\\/") == std::string::npos); | |
81 #endif | |
82 | |
83 std::string result; | |
84 | |
85 #if defined(BOOST_POSIX_API) | |
86 if (path.empty()) | |
87 { | |
88 const char *envpath = getenv("PATH"); | |
89 if (!envpath) | |
90 boost::throw_exception(boost::filesystem::filesystem_error( | |
91 BOOST_PROCESS_SOURCE_LOCATION "file not found", file, | |
92 boost::system::errc::make_error_code( | |
93 boost::system::errc::no_such_file_or_directory))); | |
94 path = envpath; | |
95 } | |
96 BOOST_ASSERT(!path.empty()); | |
97 | |
98 #if defined(__CYGWIN__) | |
99 if (!cygwin_posix_path_list_p(path.c_str())) | |
100 { | |
101 int size = cygwin_win32_to_posix_path_list_buf_size(path.c_str()); | |
102 boost::scoped_array<char> cygpath(new char[size]); | |
103 cygwin_win32_to_posix_path_list(path.c_str(), cygpath.get()); | |
104 path = cygpath.get(); | |
105 } | |
106 #endif | |
107 | |
108 std::string::size_type pos1 = 0, pos2; | |
109 do | |
110 { | |
111 pos2 = path.find(':', pos1); | |
112 std::string dir = (pos2 != std::string::npos) ? | |
113 path.substr(pos1, pos2 - pos1) : path.substr(pos1); | |
114 std::string f = dir + | |
115 (boost::algorithm::ends_with(dir, "/") ? "" : "/") + file; | |
116 if (!access(f.c_str(), X_OK)) | |
117 result = f; | |
118 pos1 = pos2 + 1; | |
119 } while (pos2 != std::string::npos && result.empty()); | |
120 #elif defined(BOOST_WINDOWS_API) | |
121 const char *exts[] = { "", ".exe", ".com", ".bat", NULL }; | |
122 const char **ext = exts; | |
123 while (*ext) | |
124 { | |
125 char buf[MAX_PATH]; | |
126 char *dummy; | |
127 DWORD size = SearchPathA(path.empty() ? NULL : path.c_str(), | |
128 file.c_str(), *ext, MAX_PATH, buf, &dummy); | |
129 BOOST_ASSERT(size < MAX_PATH); | |
130 if (size > 0) | |
131 { | |
132 result = buf; | |
133 break; | |
134 } | |
135 ++ext; | |
136 } | |
137 #endif | |
138 | |
139 if (result.empty()) | |
140 boost::throw_exception(boost::filesystem::filesystem_error( | |
141 BOOST_PROCESS_SOURCE_LOCATION "file not found", file, | |
142 boost::system::errc::make_error_code( | |
143 boost::system::errc::no_such_file_or_directory))); | |
144 | |
145 return result; | |
146 } | |
147 | |
148 /** | |
149 * Extracts the program name from a given executable. | |
150 * | |
151 * \return The program name. On Windows the program name | |
152 * is returned without a file extension. | |
153 */ | |
154 inline std::string executable_to_progname(const std::string &exe) | |
155 { | |
156 std::string::size_type begin = 0; | |
157 std::string::size_type end = std::string::npos; | |
158 | |
159 #if defined(BOOST_POSIX_API) | |
160 std::string::size_type slash = exe.rfind('/'); | |
161 #elif defined(BOOST_WINDOWS_API) | |
162 std::string::size_type slash = exe.find_last_of("/\\"); | |
163 #endif | |
164 if (slash != std::string::npos) | |
165 begin = slash + 1; | |
166 | |
167 #if defined(BOOST_WINDOWS_API) | |
168 if (exe.size() > 4 && (boost::algorithm::iends_with(exe, ".exe") || | |
169 boost::algorithm::iends_with(exe, ".com") || | |
170 boost::algorithm::iends_with(exe, ".bat"))) | |
171 end = exe.size() - 4; | |
172 #endif | |
173 | |
174 return exe.substr(begin, end - begin); | |
175 } | |
176 | |
177 /** | |
178 * Starts a new child process. | |
179 * | |
180 * Launches a new process based on the binary image specified by the | |
181 * executable, the set of arguments passed to it and the execution context. | |
182 * | |
183 * \remark Blocking remarks: This function may block if the device holding the | |
184 * executable blocks when loading the image. This might happen if, e.g., | |
185 * the binary is being loaded from a network share. | |
186 * | |
187 * \return A handle to the new child process. | |
188 */ | |
189 template <typename Arguments, typename Context> | |
190 inline child create_child(const std::string &executable, Arguments args, | |
191 Context ctx) | |
192 { | |
193 typedef std::map<stream_id, stream_ends> handles_t; | |
194 handles_t handles; | |
195 typename Context::streams_t::iterator it = ctx.streams.begin(); | |
196 for (; it != ctx.streams.end(); ++it) | |
197 { | |
198 if (it->first == stdin_id) | |
199 handles[it->first] = it->second(input_stream); | |
200 else if (it->first == stdout_id) | |
201 handles[it->first] = it->second(output_stream); | |
202 else if (it->first == stderr_id) | |
203 handles[it->first] = it->second(output_stream); | |
204 #if defined(BOOST_POSIX_API) | |
205 else | |
206 handles[it->first] = it->second(unknown_stream); | |
207 #endif | |
208 } | |
209 | |
210 std::string p_name = ctx.process_name.empty() ? | |
211 executable_to_progname(executable) : ctx.process_name; | |
212 args.insert(args.begin(), p_name); | |
213 | |
214 #if defined(BOOST_POSIX_API) | |
215 // Between fork() and execve() only async-signal-safe functions | |
216 // must be called if multithreaded applications should be supported. | |
217 // That's why the following code is executed before fork() is called. | |
218 #if defined(F_MAXFD) | |
219 int maxdescs = fcntl(-1, F_MAXFD, 0); | |
220 if (maxdescs == -1) | |
221 maxdescs = sysconf(_SC_OPEN_MAX); | |
222 #else | |
223 int maxdescs = static_cast<int>(sysconf(_SC_OPEN_MAX)); | |
224 #endif | |
225 if (maxdescs == -1) | |
226 maxdescs = 1024; | |
227 std::vector<bool> closeflags(maxdescs, true); | |
228 std::pair<std::size_t, char**> argv = detail::collection_to_argv(args); | |
229 std::pair<std::size_t, char**> envp = | |
230 detail::environment_to_envp(ctx.env); | |
231 | |
232 const char *work_dir = ctx.work_dir.c_str(); | |
233 | |
234 pid_t pid = fork(); | |
235 if (pid == -1) | |
236 BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("fork(2) failed"); | |
237 else if (pid == 0) | |
238 { | |
239 if (chdir(work_dir) == -1) | |
240 { | |
241 write(STDERR_FILENO, "chdir() failed\n", 15); | |
242 _exit(127); | |
243 } | |
244 | |
245 for (handles_t::iterator it = handles.begin(); it != handles.end(); | |
246 ++it) | |
247 { | |
248 if (it->second.child.valid()) | |
249 { | |
250 handles_t::iterator it2 = it; | |
251 ++it2; | |
252 for (; it2 != handles.end(); ++it2) | |
253 { | |
254 if (it2->second.child.native() == it->first) | |
255 { | |
256 int fd = fcntl(it2->second.child.native(), F_DUPFD, | |
257 it->first + 1); | |
258 if (fd == -1) | |
259 { | |
260 write(STDERR_FILENO, "fcntl() failed\n", 15); | |
261 _exit(127); | |
262 } | |
263 it2->second.child = fd; | |
264 } | |
265 } | |
266 | |
267 if (dup2(it->second.child.native(), it->first) == -1) | |
268 { | |
269 write(STDERR_FILENO, "dup2() failed\n", 14); | |
270 _exit(127); | |
271 } | |
272 closeflags[it->first] = false; | |
273 } | |
274 } | |
275 | |
276 if (ctx.setup) | |
277 ctx.setup(); | |
278 | |
279 for (std::size_t i = 0; i < closeflags.size(); ++i) | |
280 { | |
281 if (closeflags[i]) | |
282 close(static_cast<int>(i)); | |
283 } | |
284 | |
285 execve(executable.c_str(), argv.second, envp.second); | |
286 | |
287 // Actually we should delete argv and envp data. As we must not | |
288 // call any non-async-signal-safe functions though we simply exit. | |
289 write(STDERR_FILENO, "execve() failed\n", 16); | |
290 _exit(127); | |
291 } | |
292 else | |
293 { | |
294 BOOST_ASSERT(pid > 0); | |
295 | |
296 for (std::size_t i = 0; i < argv.first; ++i) | |
297 delete[] argv.second[i]; | |
298 delete[] argv.second; | |
299 | |
300 for (std::size_t i = 0; i < envp.first; ++i) | |
301 delete[] envp.second[i]; | |
302 delete[] envp.second; | |
303 | |
304 std::map<stream_id, handle> parent_ends; | |
305 for (handles_t::iterator it = handles.begin(); it != handles.end(); | |
306 ++it) | |
307 parent_ends[it->first] = it->second.parent; | |
308 | |
309 return child(pid, parent_ends); | |
310 } | |
311 #elif defined(BOOST_WINDOWS_API) | |
312 STARTUPINFOA startup_info; | |
313 ZeroMemory(&startup_info, sizeof(startup_info)); | |
314 startup_info.cb = sizeof(startup_info); | |
315 startup_info.dwFlags |= STARTF_USESTDHANDLES; | |
316 startup_info.hStdInput = handles[stdin_id].child.native(); | |
317 startup_info.hStdOutput = handles[stdout_id].child.native(); | |
318 startup_info.hStdError = handles[stderr_id].child.native(); | |
319 | |
320 if (ctx.setup) | |
321 ctx.setup(startup_info); | |
322 | |
323 PROCESS_INFORMATION pi; | |
324 ZeroMemory(&pi, sizeof(pi)); | |
325 | |
326 boost::shared_array<char> cmdline = | |
327 detail::collection_to_windows_cmdline(args); | |
328 | |
329 boost::scoped_array<char> exe(new char[executable.size() + 1]); | |
330 #if (BOOST_MSVC >= 1400) | |
331 strcpy_s(exe.get(), executable.size() + 1, executable.c_str()); | |
332 #else | |
333 strcpy(exe.get(), executable.c_str()); | |
334 #endif | |
335 | |
336 boost::scoped_array<char> workdir(new char[ctx.work_dir.size() + 1]); | |
337 #if (BOOST_MSVC >= 1400) | |
338 strcpy_s(workdir.get(), ctx.work_dir.size() + 1, ctx.work_dir.c_str()); | |
339 #else | |
340 strcpy(workdir.get(), ctx.work_dir.c_str()); | |
341 #endif | |
342 | |
343 boost::shared_array<char> envstrs = | |
344 detail::environment_to_windows_strings(ctx.env); | |
345 | |
346 if (CreateProcessA(exe.get(), cmdline.get(), NULL, NULL, TRUE, 0, | |
347 envstrs.get(), workdir.get(), &startup_info, &pi) == 0) | |
348 BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("CreateProcess() failed"); | |
349 | |
350 handle hprocess(pi.hProcess); | |
351 | |
352 if (!CloseHandle(pi.hThread)) | |
353 BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("CloseHandle() failed"); | |
354 | |
355 std::map<stream_id, handle> parent_ends; | |
356 parent_ends[stdin_id] = handles[stdin_id].parent; | |
357 parent_ends[stdout_id] = handles[stdout_id].parent; | |
358 parent_ends[stderr_id] = handles[stderr_id].parent; | |
359 | |
360 return child(hprocess, parent_ends); | |
361 #endif | |
362 } | |
363 | |
364 /** | |
365 * \overload | |
366 */ | |
367 inline child create_child(const std::string &executable) | |
368 { | |
369 return create_child(executable, std::vector<std::string>(), context()); | |
370 } | |
371 | |
372 /** | |
373 * \overload | |
374 */ | |
375 template <typename Arguments> | |
376 inline child create_child(const std::string &executable, Arguments args) | |
377 { | |
378 return create_child(executable, args, context()); | |
379 } | |
380 | |
381 /** | |
382 * Starts a shell-based command. | |
383 * | |
384 * Executes the given command through the default system shell. The | |
385 * command is subject to pattern expansion, redirection and pipelining. | |
386 * The shell is launched as described by the parameters in the context. | |
387 * | |
388 * This function behaves similarly to the system(3) system call. In a | |
389 * POSIX system, the command is fed to /bin/sh whereas under a Windows | |
390 * system, it is fed to cmd.exe. It is difficult to write portable | |
391 * commands, but this function comes in handy in multiple situations. | |
392 * | |
393 * \remark Blocking remarks: This function may block if the device holding the | |
394 * executable blocks when loading the image. This might happen if, e.g., | |
395 * the binary is being loaded from a network share. | |
396 * | |
397 * \return A handle to the new child process. | |
398 */ | |
399 template <typename Context> | |
400 inline child shell(const std::string &command, Context ctx) | |
401 { | |
402 #if defined(BOOST_POSIX_API) | |
403 std::string executable = "/bin/sh"; | |
404 std::vector<std::string> args; | |
405 args.push_back("-c"); | |
406 args.push_back(command); | |
407 #elif defined(BOOST_WINDOWS_API) | |
408 char sysdir[MAX_PATH]; | |
409 UINT size = GetSystemDirectoryA(sysdir, sizeof(sysdir)); | |
410 if (!size) | |
411 BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("GetSystemDirectory() failed"); | |
412 std::string executable = std::string(sysdir) + | |
413 (sysdir[size - 1] != '\\' ? "\\cmd.exe" : "cmd.exe"); | |
414 std::vector<std::string> args; | |
415 args.push_back("/c"); | |
416 args.push_back(command); | |
417 #endif | |
418 return create_child(executable, args, ctx); | |
419 } | |
420 | |
421 /** | |
422 * \overload | |
423 */ | |
424 inline child shell(const std::string &command) | |
425 { | |
426 return shell(command, context()); | |
427 } | |
428 | |
429 } | |
430 } | |
431 | |
432 #endif |