00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00018 #ifndef BOOST_PROCESS_OPERATIONS_HPP
00019 #define BOOST_PROCESS_OPERATIONS_HPP
00020
00021 #include <boost/process/config.hpp>
00022
00023 #if defined(BOOST_POSIX_API)
00024 # include <boost/process/detail/posix_ops.hpp>
00025 # include <stdlib.h>
00026 # include <unistd.h>
00027 # if defined(__CYGWIN__)
00028 # include <boost/scoped_array.hpp>
00029 # include <sys/cygwin.h>
00030 # endif
00031 #elif defined(BOOST_WINDOWS_API)
00032 # include <boost/process/detail/win32_ops.hpp>
00033 # include <boost/algorithm/string/predicate.hpp>
00034 # include <windows.h>
00035 #else
00036 # error "Unsupported platform."
00037 #endif
00038
00039 #include <boost/process/child.hpp>
00040 #include <boost/process/stream_behavior.hpp>
00041 #include <boost/process/status.hpp>
00042 #include <boost/process/detail/file_handle.hpp>
00043 #include <boost/process/detail/pipe.hpp>
00044 #include <boost/process/detail/stream_info.hpp>
00045 #include <boost/filesystem/path.hpp>
00046 #include <boost/algorithm/string/predicate.hpp>
00047 #include <boost/system/system_error.hpp>
00048 #include <boost/throw_exception.hpp>
00049 #include <boost/scoped_array.hpp>
00050 #include <boost/assert.hpp>
00051 #include <string>
00052 #include <vector>
00053 #include <stdexcept>
00054 #include <cstddef>
00055
00056 namespace boost {
00057 namespace process {
00058
00070 inline std::string find_executable_in_path(const std::string &file, std::string path = "")
00071 {
00072 #if defined(BOOST_POSIX_API)
00073 BOOST_ASSERT(file.find('/') == std::string::npos);
00074 #elif defined(BOOST_WINDOWS_API)
00075 BOOST_ASSERT(file.find_first_of("\\/") == std::string::npos);
00076 #endif
00077
00078 std::string result;
00079
00080 #if defined(BOOST_POSIX_API)
00081 if (path.empty())
00082 {
00083 const char *envpath = ::getenv("PATH");
00084 if (!envpath)
00085 boost::throw_exception(boost::filesystem::filesystem_error("boost::process::find_executable_in_path: retrieving PATH failed", file, boost::system::errc::make_error_code(boost::system::errc::no_such_file_or_directory)));
00086
00087 path = envpath;
00088 }
00089 BOOST_ASSERT(!path.empty());
00090
00091 #if defined(__CYGWIN__)
00092 if (!::cygwin_posix_path_list_p(path.c_str()))
00093 {
00094 int size = ::cygwin_win32_to_posix_path_list_buf_size(path.c_str());
00095 boost::scoped_array<char> cygpath(new char[size]);
00096 ::cygwin_win32_to_posix_path_list(path.c_str(), cygpath.get());
00097 path = cygpath.get();
00098 }
00099 #endif
00100
00101 std::string::size_type pos1 = 0, pos2;
00102 do
00103 {
00104 pos2 = path.find(':', pos1);
00105 std::string dir = (pos2 != std::string::npos) ? path.substr(pos1, pos2 - pos1) : path.substr(pos1);
00106 std::string f = dir + (boost::algorithm::ends_with(dir, "/") ? "" : "/") + file;
00107 if (!::access(f.c_str(), X_OK))
00108 result = f;
00109 pos1 = pos2 + 1;
00110 } while (pos2 != std::string::npos && result.empty());
00111 #elif defined(BOOST_WINDOWS_API)
00112 const char *exts[] = { "", ".exe", ".com", ".bat", NULL };
00113 const char **ext = exts;
00114 while (*ext)
00115 {
00116 char buf[MAX_PATH];
00117 char *dummy;
00118 DWORD size = ::SearchPathA(path.empty() ? NULL : path.c_str(), file.c_str(), *ext, MAX_PATH, buf, &dummy);
00119 BOOST_ASSERT(size < MAX_PATH);
00120 if (size > 0)
00121 {
00122 result = buf;
00123 break;
00124 }
00125 ++ext;
00126 }
00127 #endif
00128
00129 if (result.empty())
00130 boost::throw_exception(boost::filesystem::filesystem_error("boost::process::find_executable_in_path: file not found", file, boost::system::errc::make_error_code(boost::system::errc::no_such_file_or_directory)));
00131
00132 return result;
00133 }
00134
00141 inline std::string executable_to_progname(const std::string &exe)
00142 {
00143 std::string::size_type begin = 0;
00144 std::string::size_type end = std::string::npos;
00145
00146 #if defined(BOOST_POSIX_API)
00147 std::string::size_type slash = exe.rfind('/');
00148 #elif defined(BOOST_WINDOWS_API)
00149 std::string::size_type slash = exe.find_last_of("/\\");
00150 #endif
00151 if (slash != std::string::npos)
00152 begin = slash + 1;
00153
00154 #if defined(BOOST_WINDOWS_API)
00155 if (exe.size() > 4 &&
00156 (boost::algorithm::iends_with(exe, ".exe") || boost::algorithm::iends_with(exe, ".com") || boost::algorithm::iends_with(exe, ".bat")))
00157 end = exe.size() - 4;
00158 #endif
00159
00160 return exe.substr(begin, end - begin);
00161 }
00162
00176 template <class Executable, class Arguments, class Context>
00177 inline child launch(const Executable &exe, const Arguments &args, const Context &ctx)
00178 {
00179 detail::file_handle fhstdin, fhstdout, fhstderr;
00180
00181 BOOST_ASSERT(!args.empty());
00182 BOOST_ASSERT(!ctx.work_directory.empty());
00183
00184 #if defined(BOOST_POSIX_API)
00185 detail::info_map infoin, infoout;
00186
00187 if (ctx.stdin_behavior.get_type() != stream_behavior::close)
00188 {
00189 detail::stream_info si = detail::stream_info(ctx.stdin_behavior, false);
00190 infoin.insert(detail::info_map::value_type(STDIN_FILENO, si));
00191 }
00192
00193 if (ctx.stdout_behavior.get_type() != stream_behavior::close)
00194 {
00195 detail::stream_info si = detail::stream_info(ctx.stdout_behavior, true);
00196 infoout.insert(detail::info_map::value_type(STDOUT_FILENO, si));
00197 }
00198
00199 if (ctx.stderr_behavior.get_type() != stream_behavior::close)
00200 {
00201 detail::stream_info si = detail::stream_info(ctx.stderr_behavior, true);
00202 infoout.insert(detail::info_map::value_type(STDERR_FILENO, si));
00203 }
00204
00205 detail::posix_setup s;
00206 s.work_directory = ctx.work_directory;
00207
00208 child::id_type pid = detail::posix_start(exe, args, ctx.environment, infoin, infoout, s);
00209
00210 if (ctx.stdin_behavior.get_type() == stream_behavior::capture)
00211 {
00212 fhstdin = detail::posix_info_locate_pipe(infoin, STDIN_FILENO, false);
00213 BOOST_ASSERT(fhstdin.valid());
00214 }
00215
00216 if (ctx.stdout_behavior.get_type() == stream_behavior::capture)
00217 {
00218 fhstdout = detail::posix_info_locate_pipe(infoout, STDOUT_FILENO, true);
00219 BOOST_ASSERT(fhstdout.valid());
00220 }
00221
00222 if (ctx.stderr_behavior.get_type() == stream_behavior::capture)
00223 {
00224 fhstderr = detail::posix_info_locate_pipe(infoout, STDERR_FILENO, true);
00225 BOOST_ASSERT(fhstderr.valid());
00226 }
00227
00228 return child(pid, fhstdin, fhstdout, fhstderr);
00229 #elif defined(BOOST_WINDOWS_API)
00230 detail::stream_info behin = detail::stream_info(ctx.stdin_behavior, false);
00231 if (behin.type_ == detail::stream_info::use_pipe)
00232 fhstdin = behin.pipe_->wend();
00233 detail::stream_info behout = detail::stream_info(ctx.stdout_behavior, true);
00234 if (behout.type_ == detail::stream_info::use_pipe)
00235 fhstdout = behout.pipe_->rend();
00236 detail::stream_info beherr = detail::stream_info(ctx.stderr_behavior, true);
00237 if (beherr.type_ == detail::stream_info::use_pipe)
00238 fhstderr = beherr.pipe_->rend();
00239
00240 STARTUPINFOA si;
00241 ::ZeroMemory(&si, sizeof(si));
00242 si.cb = sizeof(si);
00243
00244 detail::win32_setup s;
00245 s.work_directory = ctx.work_directory;
00246 s.startupinfo = &si;
00247
00248 PROCESS_INFORMATION pi = detail::win32_start(exe, args, ctx.environment, behin, behout, beherr, s);
00249
00250 if (!::CloseHandle(pi.hThread))
00251 boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::launch: CloseHandle failed"));
00252
00253 return child(pi.dwProcessId, fhstdin, fhstdout, fhstderr, detail::file_handle(pi.hProcess));
00254 #endif
00255 }
00256
00271 template <class Context>
00272 inline child launch_shell(const std::string &command, const Context &ctx)
00273 {
00274 std::string exe;
00275 std::vector<std::string> args;
00276
00277 #if defined(BOOST_POSIX_API)
00278 exe = "/bin/sh";
00279 args.push_back("sh");
00280 args.push_back("-c");
00281 args.push_back(command);
00282 #elif defined(BOOST_WINDOWS_API)
00283 char sysdir[MAX_PATH];
00284 UINT size = ::GetSystemDirectoryA(sysdir, sizeof(sysdir));
00285 if (!size)
00286 boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::launch_shell: GetWindowsDirectory failed"));
00287 BOOST_ASSERT(size < MAX_PATH);
00288
00289 exe = std::string(sysdir) + (sysdir[size - 1] != '\\' ? "\\cmd.exe" : "cmd.exe");
00290 args.push_back("cmd");
00291 args.push_back("/c");
00292 args.push_back(command);
00293 #endif
00294
00295 return launch(exe, args, ctx);
00296 }
00297
00317 template <class Entries>
00318 inline children launch_pipeline(const Entries &entries)
00319 {
00320 BOOST_ASSERT(entries.size() >= 2);
00321
00322 children cs;
00323 detail::file_handle fhinvalid;
00324
00325 boost::scoped_array<detail::pipe> pipes(new detail::pipe[entries.size() - 1]);
00326
00327 #if defined(BOOST_POSIX_API)
00328 {
00329 typename Entries::size_type i = 0;
00330 const typename Entries::value_type::context_type &ctx = entries[i].context;
00331
00332 detail::info_map infoin, infoout;
00333
00334 if (ctx.stdin_behavior.get_type() != stream_behavior::close)
00335 {
00336 detail::stream_info si = detail::stream_info(ctx.stdin_behavior, false);
00337 infoin.insert(detail::info_map::value_type(STDIN_FILENO, si));
00338 }
00339
00340 BOOST_ASSERT(ctx.stdout_behavior.get_type() == stream_behavior::close);
00341 detail::stream_info si2(close_stream(), true);
00342 si2.type_ = detail::stream_info::use_handle;
00343 si2.handle_ = pipes[i].wend().release();
00344 infoout.insert(detail::info_map::value_type(STDOUT_FILENO, si2));
00345
00346 if (ctx.stderr_behavior.get_type() != stream_behavior::close)
00347 {
00348 detail::stream_info si = detail::stream_info(ctx.stderr_behavior, true);
00349 infoout.insert(detail::info_map::value_type(STDERR_FILENO, si));
00350 }
00351
00352 detail::posix_setup s;
00353 s.work_directory = ctx.work_directory;
00354
00355 pid_t pid = detail::posix_start(entries[i].executable, entries[i].arguments, ctx.environment, infoin, infoout, s);
00356
00357 detail::file_handle fhstdin;
00358
00359 if (ctx.stdin_behavior.get_type() == stream_behavior::capture)
00360 {
00361 fhstdin = detail::posix_info_locate_pipe(infoin, STDIN_FILENO, false);
00362 BOOST_ASSERT(fhstdin.valid());
00363 }
00364
00365 cs.push_back(child(pid, fhstdin, fhinvalid, fhinvalid));
00366 }
00367
00368 for (typename Entries::size_type i = 1; i < entries.size() - 1; ++i)
00369 {
00370 const typename Entries::value_type::context_type &ctx = entries[i].context;
00371 detail::info_map infoin, infoout;
00372
00373 BOOST_ASSERT(ctx.stdin_behavior.get_type() == stream_behavior::close);
00374 detail::stream_info si1(close_stream(), false);
00375 si1.type_ = detail::stream_info::use_handle;
00376 si1.handle_ = pipes[i - 1].rend().release();
00377 infoin.insert(detail::info_map::value_type(STDIN_FILENO, si1));
00378
00379 BOOST_ASSERT(ctx.stdout_behavior.get_type() == stream_behavior::close);
00380 detail::stream_info si2(close_stream(), true);
00381 si2.type_ = detail::stream_info::use_handle;
00382 si2.handle_ = pipes[i].wend().release();
00383 infoout.insert(detail::info_map::value_type(STDOUT_FILENO, si2));
00384
00385 if (ctx.stderr_behavior.get_type() != stream_behavior::close)
00386 {
00387 detail::stream_info si = detail::stream_info(ctx.stderr_behavior, true);
00388 infoout.insert(detail::info_map::value_type(STDERR_FILENO, si));
00389 }
00390
00391 detail::posix_setup s;
00392 s.work_directory = ctx.work_directory;
00393
00394 pid_t pid = detail::posix_start(entries[i].executable, entries[i].arguments, ctx.environment, infoin, infoout, s);
00395
00396 cs.push_back(child(pid, fhinvalid, fhinvalid, fhinvalid));
00397 }
00398
00399 {
00400 typename Entries::size_type i = entries.size() - 1;
00401 const typename Entries::value_type::context_type &ctx = entries[i].context;
00402
00403 detail::info_map infoin, infoout;
00404
00405 BOOST_ASSERT(ctx.stdin_behavior.get_type() == stream_behavior::close);
00406 detail::stream_info si1(close_stream(), false);
00407 si1.type_ = detail::stream_info::use_handle;
00408 si1.handle_ = pipes[i - 1].rend().release();
00409 infoin.insert(detail::info_map::value_type(STDIN_FILENO, si1));
00410
00411 if (ctx.stdout_behavior.get_type() != stream_behavior::close)
00412 {
00413 detail::stream_info si = detail::stream_info(ctx.stdout_behavior, true);
00414 infoout.insert(detail::info_map::value_type(STDOUT_FILENO, si));
00415 }
00416
00417 if (ctx.stderr_behavior.get_type() != stream_behavior::close)
00418 {
00419 detail::stream_info si = detail::stream_info(ctx.stderr_behavior, true);
00420 infoout.insert(detail::info_map::value_type(STDERR_FILENO, si));
00421 }
00422
00423 detail::posix_setup s;
00424 s.work_directory = ctx.work_directory;
00425
00426 pid_t pid = detail::posix_start(entries[i].executable, entries[i].arguments, ctx.environment, infoin, infoout, s);
00427
00428 detail::file_handle fhstdout, fhstderr;
00429
00430 if (ctx.stdout_behavior.get_type() == stream_behavior::capture)
00431 {
00432 fhstdout = detail::posix_info_locate_pipe(infoout, STDOUT_FILENO, true);
00433 BOOST_ASSERT(fhstdout.valid());
00434 }
00435
00436 if (ctx.stderr_behavior.get_type() == stream_behavior::capture)
00437 {
00438 fhstderr = detail::posix_info_locate_pipe(infoout, STDERR_FILENO, true);
00439 BOOST_ASSERT(fhstderr.valid());
00440 }
00441
00442 cs.push_back(child(pid, fhinvalid, fhstdout, fhstderr));
00443 }
00444 #elif defined(BOOST_WINDOWS_API)
00445 STARTUPINFO si;
00446 detail::win32_setup s;
00447 s.startupinfo = &si;
00448
00449 {
00450 typename Entries::size_type i = 0;
00451 const typename Entries::value_type::context_type &ctx = entries[i].context;
00452
00453 detail::stream_info sii = detail::stream_info(ctx.stdin_behavior, false);
00454 detail::file_handle fhstdin;
00455 if (sii.type_ == detail::stream_info::use_pipe)
00456 fhstdin = sii.pipe_->wend();
00457
00458 BOOST_ASSERT(ctx.stdout_behavior.get_type() == stream_behavior::close);
00459 detail::stream_info sio(close_stream(), true);
00460 sio.type_ = detail::stream_info::use_handle;
00461 sio.handle_ = pipes[i].wend().release();
00462
00463 detail::stream_info sie(ctx.stderr_behavior, true);
00464
00465 s.work_directory = ctx.work_directory;
00466
00467 ::ZeroMemory(&si, sizeof(si));
00468 si.cb = sizeof(si);
00469 PROCESS_INFORMATION pi = detail::win32_start(entries[i].executable, entries[i].arguments, ctx.environment, sii, sio, sie, s);
00470
00471 if (!::CloseHandle(pi.hThread))
00472 boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::launch_pipeline: CloseHandle failed"));
00473
00474 cs.push_back(child(pi.dwProcessId, fhstdin, fhinvalid, fhinvalid, detail::file_handle(pi.hProcess)));
00475 }
00476
00477 for (typename Entries::size_type i = 1; i < entries.size() - 1; ++i)
00478 {
00479 const typename Entries::value_type::context_type &ctx = entries[i].context;
00480
00481 BOOST_ASSERT(ctx.stdin_behavior.get_type() == stream_behavior::close);
00482 detail::stream_info sii(close_stream(), false);
00483 sii.type_ = detail::stream_info::use_handle;
00484 sii.handle_ = pipes[i - 1].rend().release();
00485
00486 detail::stream_info sio(close_stream(), true);
00487 sio.type_ = detail::stream_info::use_handle;
00488 sio.handle_ = pipes[i].wend().release();
00489
00490 detail::stream_info sie(ctx.stderr_behavior, true);
00491
00492 s.work_directory = ctx.work_directory;
00493
00494 ::ZeroMemory(&si, sizeof(si));
00495 si.cb = sizeof(si);
00496 PROCESS_INFORMATION pi = detail::win32_start(entries[i].executable, entries[i].arguments, ctx.environment, sii, sio, sie, s);
00497
00498 if (!::CloseHandle(pi.hThread))
00499 boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::launch_pipeline: CloseHandle failed"));
00500
00501 cs.push_back(child(pi.dwProcessId, fhinvalid, fhinvalid, fhinvalid, detail::file_handle(pi.hProcess)));
00502 }
00503
00504 {
00505 typename Entries::size_type i = entries.size() - 1;
00506 const typename Entries::value_type::context_type &ctx = entries[i].context;
00507
00508 BOOST_ASSERT(ctx.stdin_behavior.get_type() == stream_behavior::close);
00509 detail::stream_info sii(close_stream(), false);
00510 sii.type_ = detail::stream_info::use_handle;
00511 sii.handle_ = pipes[i - 1].rend().release();
00512
00513 detail::file_handle fhstdout, fhstderr;
00514
00515 detail::stream_info sio(ctx.stdout_behavior, true);
00516 if (sio.type_ == detail::stream_info::use_pipe)
00517 fhstdout = sio.pipe_->rend();
00518 detail::stream_info sie(ctx.stderr_behavior, true);
00519 if (sie.type_ == detail::stream_info::use_pipe)
00520 fhstderr = sie.pipe_->rend();
00521
00522 s.work_directory = ctx.work_directory;
00523
00524 ::ZeroMemory(&si, sizeof(si));
00525 si.cb = sizeof(si);
00526 PROCESS_INFORMATION pi = detail::win32_start(entries[i].executable, entries[i].arguments, ctx.environment, sii, sio, sie, s);
00527
00528 if (!::CloseHandle(pi.hThread))
00529 boost::throw_exception(boost::system::system_error(boost::system::error_code(::GetLastError(), boost::system::get_system_category()), "boost::process::launch_pipeline: CloseHandle failed"));
00530
00531 cs.push_back(child(pi.dwProcessId, fhinvalid, fhstdout, fhstderr, detail::file_handle(pi.hProcess)));
00532 }
00533 #endif
00534
00535 return cs;
00536 }
00537
00553 template <class Children>
00554 inline status wait_children(Children &cs)
00555 {
00556 BOOST_ASSERT(cs.size() >= 2);
00557
00558 typename Children::iterator it = cs.begin();
00559 while (it != cs.end())
00560 {
00561 const status s = it->wait();
00562 ++it;
00563 if (it == cs.end())
00564 return s;
00565 else if (!s.exited() || s.exit_status() != EXIT_SUCCESS)
00566 {
00567 while (it != cs.end())
00568 {
00569 it->wait();
00570 ++it;
00571 }
00572 return s;
00573 }
00574 }
00575
00576 BOOST_ASSERT(false);
00577 return cs.begin()->wait();
00578 }
00579
00580 }
00581 }
00582
00583 #endif