Mercurial > hg > gpsynth
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/third_party/boost/process/operations.hpp Thu Aug 25 11:05:55 2011 +0100 @@ -0,0 +1,432 @@ +// +// Boost.Process +// ~~~~~~~~~~~~~ +// +// Copyright (c) 2006, 2007 Julio M. Merino Vidal +// Copyright (c) 2008 Ilya Sokolov, Boris Schaeling +// Copyright (c) 2009 Boris Schaeling +// Copyright (c) 2010 Felipe Tanus, Boris Schaeling +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +/** + * \file boost/process/operations.hpp + * + * Provides miscellaneous free functions. + */ + +#ifndef BOOST_PROCESS_OPERATIONS_HPP +#define BOOST_PROCESS_OPERATIONS_HPP + +#include <boost/process/config.hpp> + +#if defined(BOOST_POSIX_API) +# include <boost/process/detail/posix_helpers.hpp> +# include <utility> +# include <cstddef> +# include <stdlib.h> +# include <unistd.h> +# include <fcntl.h> +# if defined(__CYGWIN__) +# include <boost/scoped_array.hpp> +# include <sys/cygwin.h> +# endif +#elif defined(BOOST_WINDOWS_API) +# include <boost/process/detail/windows_helpers.hpp> +# include <boost/scoped_array.hpp> +# include <boost/shared_array.hpp> +# include <windows.h> +#else +# error "Unsupported platform." +#endif + +#include <boost/process/child.hpp> +#include <boost/process/context.hpp> +#include <boost/process/stream_id.hpp> +#include <boost/process/stream_ends.hpp> +#include <boost/process/handle.hpp> +#include <boost/filesystem/path.hpp> +#include <boost/algorithm/string/predicate.hpp> +#include <boost/system/system_error.hpp> +#include <boost/throw_exception.hpp> +#include <boost/assert.hpp> +#include <string> +#include <vector> +#include <map> +#include <utility> + +namespace boost { +namespace process { + +/** + * Locates the executable program \a file in all the directory components + * specified in \a path. If \a path is empty, the value of the PATH + * environment variable is used. + * + * The path variable is interpreted following the same conventions used + * to parse the PATH environment variable in the underlying platform. + * + * \throw boost::filesystem::filesystem_error If the file cannot be found + * in the path. + */ +inline std::string find_executable_in_path(const std::string &file, + std::string path = "") +{ +#if defined(BOOST_POSIX_API) + BOOST_ASSERT(file.find('/') == std::string::npos); +#elif defined(BOOST_WINDOWS_API) + BOOST_ASSERT(file.find_first_of("\\/") == std::string::npos); +#endif + + std::string result; + +#if defined(BOOST_POSIX_API) + if (path.empty()) + { + const char *envpath = getenv("PATH"); + if (!envpath) + boost::throw_exception(boost::filesystem::filesystem_error( + BOOST_PROCESS_SOURCE_LOCATION "file not found", file, + boost::system::errc::make_error_code( + boost::system::errc::no_such_file_or_directory))); + path = envpath; + } + BOOST_ASSERT(!path.empty()); + +#if defined(__CYGWIN__) + if (!cygwin_posix_path_list_p(path.c_str())) + { + int size = cygwin_win32_to_posix_path_list_buf_size(path.c_str()); + boost::scoped_array<char> cygpath(new char[size]); + cygwin_win32_to_posix_path_list(path.c_str(), cygpath.get()); + path = cygpath.get(); + } +#endif + + std::string::size_type pos1 = 0, pos2; + do + { + pos2 = path.find(':', pos1); + std::string dir = (pos2 != std::string::npos) ? + path.substr(pos1, pos2 - pos1) : path.substr(pos1); + std::string f = dir + + (boost::algorithm::ends_with(dir, "/") ? "" : "/") + file; + if (!access(f.c_str(), X_OK)) + result = f; + pos1 = pos2 + 1; + } while (pos2 != std::string::npos && result.empty()); +#elif defined(BOOST_WINDOWS_API) + const char *exts[] = { "", ".exe", ".com", ".bat", NULL }; + const char **ext = exts; + while (*ext) + { + char buf[MAX_PATH]; + char *dummy; + DWORD size = SearchPathA(path.empty() ? NULL : path.c_str(), + file.c_str(), *ext, MAX_PATH, buf, &dummy); + BOOST_ASSERT(size < MAX_PATH); + if (size > 0) + { + result = buf; + break; + } + ++ext; + } +#endif + + if (result.empty()) + boost::throw_exception(boost::filesystem::filesystem_error( + BOOST_PROCESS_SOURCE_LOCATION "file not found", file, + boost::system::errc::make_error_code( + boost::system::errc::no_such_file_or_directory))); + + return result; +} + +/** + * Extracts the program name from a given executable. + * + * \return The program name. On Windows the program name + * is returned without a file extension. + */ +inline std::string executable_to_progname(const std::string &exe) +{ + std::string::size_type begin = 0; + std::string::size_type end = std::string::npos; + +#if defined(BOOST_POSIX_API) + std::string::size_type slash = exe.rfind('/'); +#elif defined(BOOST_WINDOWS_API) + std::string::size_type slash = exe.find_last_of("/\\"); +#endif + if (slash != std::string::npos) + begin = slash + 1; + +#if defined(BOOST_WINDOWS_API) + if (exe.size() > 4 && (boost::algorithm::iends_with(exe, ".exe") || + boost::algorithm::iends_with(exe, ".com") || + boost::algorithm::iends_with(exe, ".bat"))) + end = exe.size() - 4; +#endif + + return exe.substr(begin, end - begin); +} + +/** + * Starts a new child process. + * + * Launches a new process based on the binary image specified by the + * executable, the set of arguments passed to it and the execution context. + * + * \remark Blocking remarks: This function may block if the device holding the + * executable blocks when loading the image. This might happen if, e.g., + * the binary is being loaded from a network share. + * + * \return A handle to the new child process. + */ +template <typename Arguments, typename Context> +inline child create_child(const std::string &executable, Arguments args, + Context ctx) +{ + typedef std::map<stream_id, stream_ends> handles_t; + handles_t handles; + typename Context::streams_t::iterator it = ctx.streams.begin(); + for (; it != ctx.streams.end(); ++it) + { + if (it->first == stdin_id) + handles[it->first] = it->second(input_stream); + else if (it->first == stdout_id) + handles[it->first] = it->second(output_stream); + else if (it->first == stderr_id) + handles[it->first] = it->second(output_stream); +#if defined(BOOST_POSIX_API) + else + handles[it->first] = it->second(unknown_stream); +#endif + } + + std::string p_name = ctx.process_name.empty() ? + executable_to_progname(executable) : ctx.process_name; + args.insert(args.begin(), p_name); + +#if defined(BOOST_POSIX_API) + // Between fork() and execve() only async-signal-safe functions + // must be called if multithreaded applications should be supported. + // That's why the following code is executed before fork() is called. +#if defined(F_MAXFD) + int maxdescs = fcntl(-1, F_MAXFD, 0); + if (maxdescs == -1) + maxdescs = sysconf(_SC_OPEN_MAX); +#else + int maxdescs = static_cast<int>(sysconf(_SC_OPEN_MAX)); +#endif + if (maxdescs == -1) + maxdescs = 1024; + std::vector<bool> closeflags(maxdescs, true); + std::pair<std::size_t, char**> argv = detail::collection_to_argv(args); + std::pair<std::size_t, char**> envp = + detail::environment_to_envp(ctx.env); + + const char *work_dir = ctx.work_dir.c_str(); + + pid_t pid = fork(); + if (pid == -1) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("fork(2) failed"); + else if (pid == 0) + { + if (chdir(work_dir) == -1) + { + write(STDERR_FILENO, "chdir() failed\n", 15); + _exit(127); + } + + for (handles_t::iterator it = handles.begin(); it != handles.end(); + ++it) + { + if (it->second.child.valid()) + { + handles_t::iterator it2 = it; + ++it2; + for (; it2 != handles.end(); ++it2) + { + if (it2->second.child.native() == it->first) + { + int fd = fcntl(it2->second.child.native(), F_DUPFD, + it->first + 1); + if (fd == -1) + { + write(STDERR_FILENO, "fcntl() failed\n", 15); + _exit(127); + } + it2->second.child = fd; + } + } + + if (dup2(it->second.child.native(), it->first) == -1) + { + write(STDERR_FILENO, "dup2() failed\n", 14); + _exit(127); + } + closeflags[it->first] = false; + } + } + + if (ctx.setup) + ctx.setup(); + + for (std::size_t i = 0; i < closeflags.size(); ++i) + { + if (closeflags[i]) + close(static_cast<int>(i)); + } + + execve(executable.c_str(), argv.second, envp.second); + + // Actually we should delete argv and envp data. As we must not + // call any non-async-signal-safe functions though we simply exit. + write(STDERR_FILENO, "execve() failed\n", 16); + _exit(127); + } + else + { + BOOST_ASSERT(pid > 0); + + for (std::size_t i = 0; i < argv.first; ++i) + delete[] argv.second[i]; + delete[] argv.second; + + for (std::size_t i = 0; i < envp.first; ++i) + delete[] envp.second[i]; + delete[] envp.second; + + std::map<stream_id, handle> parent_ends; + for (handles_t::iterator it = handles.begin(); it != handles.end(); + ++it) + parent_ends[it->first] = it->second.parent; + + return child(pid, parent_ends); + } +#elif defined(BOOST_WINDOWS_API) + STARTUPINFOA startup_info; + ZeroMemory(&startup_info, sizeof(startup_info)); + startup_info.cb = sizeof(startup_info); + startup_info.dwFlags |= STARTF_USESTDHANDLES; + startup_info.hStdInput = handles[stdin_id].child.native(); + startup_info.hStdOutput = handles[stdout_id].child.native(); + startup_info.hStdError = handles[stderr_id].child.native(); + + if (ctx.setup) + ctx.setup(startup_info); + + PROCESS_INFORMATION pi; + ZeroMemory(&pi, sizeof(pi)); + + boost::shared_array<char> cmdline = + detail::collection_to_windows_cmdline(args); + + boost::scoped_array<char> exe(new char[executable.size() + 1]); +#if (BOOST_MSVC >= 1400) + strcpy_s(exe.get(), executable.size() + 1, executable.c_str()); +#else + strcpy(exe.get(), executable.c_str()); +#endif + + boost::scoped_array<char> workdir(new char[ctx.work_dir.size() + 1]); +#if (BOOST_MSVC >= 1400) + strcpy_s(workdir.get(), ctx.work_dir.size() + 1, ctx.work_dir.c_str()); +#else + strcpy(workdir.get(), ctx.work_dir.c_str()); +#endif + + boost::shared_array<char> envstrs = + detail::environment_to_windows_strings(ctx.env); + + if (CreateProcessA(exe.get(), cmdline.get(), NULL, NULL, TRUE, 0, + envstrs.get(), workdir.get(), &startup_info, &pi) == 0) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("CreateProcess() failed"); + + handle hprocess(pi.hProcess); + + if (!CloseHandle(pi.hThread)) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("CloseHandle() failed"); + + std::map<stream_id, handle> parent_ends; + parent_ends[stdin_id] = handles[stdin_id].parent; + parent_ends[stdout_id] = handles[stdout_id].parent; + parent_ends[stderr_id] = handles[stderr_id].parent; + + return child(hprocess, parent_ends); +#endif +} + +/** + * \overload + */ +inline child create_child(const std::string &executable) +{ + return create_child(executable, std::vector<std::string>(), context()); +} + +/** + * \overload + */ +template <typename Arguments> +inline child create_child(const std::string &executable, Arguments args) +{ + return create_child(executable, args, context()); +} + +/** + * Starts a shell-based command. + * + * Executes the given command through the default system shell. The + * command is subject to pattern expansion, redirection and pipelining. + * The shell is launched as described by the parameters in the context. + * + * This function behaves similarly to the system(3) system call. In a + * POSIX system, the command is fed to /bin/sh whereas under a Windows + * system, it is fed to cmd.exe. It is difficult to write portable + * commands, but this function comes in handy in multiple situations. + * + * \remark Blocking remarks: This function may block if the device holding the + * executable blocks when loading the image. This might happen if, e.g., + * the binary is being loaded from a network share. + * + * \return A handle to the new child process. + */ +template <typename Context> +inline child shell(const std::string &command, Context ctx) +{ +#if defined(BOOST_POSIX_API) + std::string executable = "/bin/sh"; + std::vector<std::string> args; + args.push_back("-c"); + args.push_back(command); +#elif defined(BOOST_WINDOWS_API) + char sysdir[MAX_PATH]; + UINT size = GetSystemDirectoryA(sysdir, sizeof(sysdir)); + if (!size) + BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("GetSystemDirectory() failed"); + std::string executable = std::string(sysdir) + + (sysdir[size - 1] != '\\' ? "\\cmd.exe" : "cmd.exe"); + std::vector<std::string> args; + args.push_back("/c"); + args.push_back(command); +#endif + return create_child(executable, args, ctx); +} + +/** + * \overload + */ +inline child shell(const std::string &command) +{ + return shell(command, context()); +} + +} +} + +#endif