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