326 lines
9.1 KiB
C++
326 lines
9.1 KiB
C++
//
|
|
// 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/stream_behavior.hpp
|
|
*
|
|
* Includes the declaration of stream behavior classes.
|
|
*/
|
|
|
|
#ifndef BOOST_PROCESS_STREAM_BEHAVIOR_HPP
|
|
#define BOOST_PROCESS_STREAM_BEHAVIOR_HPP
|
|
|
|
#include <boost/process/config.hpp>
|
|
|
|
#if defined(BOOST_POSIX_API)
|
|
# include <sys/stat.h>
|
|
# include <fcntl.h>
|
|
# include <unistd.h>
|
|
#elif defined(BOOST_WINDOWS_API)
|
|
# include <windows.h>
|
|
# include <rpc.h>
|
|
#endif
|
|
|
|
#include <boost/process/stream_ends.hpp>
|
|
#include <boost/process/stream_type.hpp>
|
|
#include <boost/process/handle.hpp>
|
|
#include <string>
|
|
#include <algorithm>
|
|
|
|
namespace boost {
|
|
namespace process {
|
|
namespace behavior {
|
|
|
|
/**
|
|
* Stream behavior to close streams of a child process.
|
|
*
|
|
* A child process will not be able to use the stream.
|
|
*/
|
|
class close
|
|
{
|
|
public:
|
|
stream_ends operator()(stream_type) const
|
|
{
|
|
return stream_ends();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Stream behavior to make a child process inherit streams.
|
|
*
|
|
* A child process will use the very same stream of its parent process.
|
|
*/
|
|
class inherit
|
|
{
|
|
public:
|
|
inherit(handle::native_type h)
|
|
: h_(h, handle::dont_close)
|
|
{
|
|
#if defined(BOOST_WINDOWS_API)
|
|
if (h != INVALID_HANDLE_VALUE)
|
|
{
|
|
if (!SetHandleInformation(h_.native(), HANDLE_FLAG_INHERIT,
|
|
HANDLE_FLAG_INHERIT))
|
|
{
|
|
HANDLE proc = GetCurrentProcess();
|
|
HANDLE dup;
|
|
if (!DuplicateHandle(proc, h_.native(), proc, &dup, 0,
|
|
TRUE, DUPLICATE_SAME_ACCESS))
|
|
{
|
|
BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR(
|
|
"DuplicateHandle() failed");
|
|
}
|
|
h_ = dup;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
stream_ends operator()(stream_type) const
|
|
{
|
|
return stream_ends(h_, handle());
|
|
}
|
|
|
|
private:
|
|
handle h_;
|
|
};
|
|
|
|
/**
|
|
* Stream behavior to redirect streams with a pipe.
|
|
*
|
|
* A child process will be able to communicate with its parent process.
|
|
*/
|
|
class pipe
|
|
{
|
|
public:
|
|
#if defined(BOOST_POSIX_API)
|
|
pipe()
|
|
: stype_(unknown_stream)
|
|
{
|
|
}
|
|
|
|
pipe(stream_type stype)
|
|
: stype_(stype)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
stream_ends operator()(stream_type stype) const
|
|
{
|
|
handle::native_type ends[2];
|
|
#if defined(BOOST_POSIX_API)
|
|
if (::pipe(ends) == -1)
|
|
BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("pipe(2) failed");
|
|
if (stype_ != unknown_stream)
|
|
stype = stype_;
|
|
#elif defined(BOOST_WINDOWS_API)
|
|
SECURITY_ATTRIBUTES sa;
|
|
ZeroMemory(&sa, sizeof(sa));
|
|
sa.nLength = sizeof(sa);
|
|
sa.lpSecurityDescriptor = NULL;
|
|
sa.bInheritHandle = FALSE;
|
|
if (!CreatePipe(&ends[0], &ends[1], &sa, 0))
|
|
BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("CreatePipe() failed");
|
|
#endif
|
|
handle child_end = ends[stype == input_stream ? 0 : 1];
|
|
handle parent_end = ends[stype == input_stream ? 1 : 0];
|
|
#if defined(BOOST_WINDOWS_API)
|
|
if (!SetHandleInformation(child_end.native(), HANDLE_FLAG_INHERIT,
|
|
HANDLE_FLAG_INHERIT))
|
|
BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR(
|
|
"SetHandleInformation() failed");
|
|
#endif
|
|
return stream_ends(child_end, parent_end);
|
|
}
|
|
|
|
#if defined(BOOST_POSIX_API)
|
|
private:
|
|
stream_type stype_;
|
|
#endif
|
|
};
|
|
|
|
/**
|
|
* Stream behavior to redirect streams with a named pipe.
|
|
*
|
|
* A child process will be able to communicate with its parent process.
|
|
*/
|
|
class named_pipe
|
|
{
|
|
public:
|
|
named_pipe(const std::string &name)
|
|
: name_(name)
|
|
#if defined(BOOST_POSIX_API)
|
|
, stype_(unknown_stream)
|
|
#endif
|
|
{
|
|
}
|
|
|
|
#if defined(BOOST_POSIX_API)
|
|
named_pipe(const std::string &name, stream_type stype)
|
|
: name_(name),
|
|
stype_(stype)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
stream_ends operator()(stream_type stype) const
|
|
{
|
|
#if defined(BOOST_POSIX_API)
|
|
if (mkfifo(name_.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1)
|
|
BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("mkfifo(3) failed");
|
|
handle child_end = open(name_.c_str(), O_RDONLY | O_NONBLOCK);
|
|
if (!child_end.valid())
|
|
BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("open(2) failed");
|
|
int opts = fcntl(child_end.native(), F_GETFL);
|
|
if (opts == -1)
|
|
BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("fcntl(2) failed");
|
|
opts ^= O_NONBLOCK;
|
|
if (fcntl(child_end.native(), F_SETFL, opts) == -1)
|
|
BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("fcntl(2) failed");
|
|
handle parent_end = open(name_.c_str(), O_WRONLY);
|
|
if (!parent_end.valid())
|
|
BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("open(2) failed");
|
|
if (stype_ != unknown_stream)
|
|
stype = stype_;
|
|
#elif defined(BOOST_WINDOWS_API)
|
|
SECURITY_ATTRIBUTES sa;
|
|
ZeroMemory(&sa, sizeof(sa));
|
|
sa.nLength = sizeof(sa);
|
|
sa.lpSecurityDescriptor = NULL;
|
|
sa.bInheritHandle = TRUE;
|
|
handle child_end = CreateNamedPipeA(name_.c_str(), PIPE_ACCESS_INBOUND |
|
|
FILE_FLAG_OVERLAPPED, 0, 1, 8192, 8192, 0, &sa);
|
|
if (!child_end.valid())
|
|
BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("CreateNamedPipe() failed");
|
|
handle parent_end = CreateFileA(name_.c_str(), GENERIC_WRITE, 0, NULL,
|
|
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
|
|
if (!parent_end.valid())
|
|
BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("CreateFile() failed");
|
|
#endif
|
|
if (stype == output_stream)
|
|
std::swap(child_end, parent_end);
|
|
#if defined(BOOST_WINDOWS_API)
|
|
if (!SetHandleInformation(child_end.native(), HANDLE_FLAG_INHERIT,
|
|
HANDLE_FLAG_INHERIT))
|
|
BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR(
|
|
"SetHandleInformation() failed");
|
|
#endif
|
|
return stream_ends(child_end, parent_end);
|
|
}
|
|
|
|
private:
|
|
std::string name_;
|
|
|
|
#if defined(BOOST_POSIX_API)
|
|
stream_type stype_;
|
|
#endif
|
|
};
|
|
|
|
/**
|
|
* Stream behavior to redirect streams with a pipe which supports asynchronous
|
|
* I/O.
|
|
*
|
|
* As platforms require different types of pipes for asynchronous I/O this
|
|
* stream behavior is provided for convenience. It uses the minimum required
|
|
* pipe type on a platform in order to be able to use asynchronous I/O.
|
|
*/
|
|
#if defined(BOOST_POSIX_API) || defined(BOOST_PROCESS_DOXYGEN)
|
|
typedef pipe async_pipe;
|
|
#elif defined(BOOST_WINDOWS_API)
|
|
class async_pipe
|
|
{
|
|
public:
|
|
stream_ends operator()(stream_type stype) const
|
|
{
|
|
UUID uuid;
|
|
RPC_STATUS s = UuidCreateSequential(&uuid);
|
|
if (s != RPC_S_OK && s != RPC_S_UUID_LOCAL_ONLY)
|
|
BOOST_PROCESS_THROW_ERROR(s, "UuidCreateSequential() failed");
|
|
unsigned char *c;
|
|
s = UuidToStringA(&uuid, &c);
|
|
if (s != RPC_S_OK)
|
|
BOOST_PROCESS_THROW_ERROR(s, "UuidToString() failed");
|
|
std::string name;
|
|
try
|
|
{
|
|
name = reinterpret_cast<char*>(c);
|
|
}
|
|
catch (...)
|
|
{
|
|
RpcStringFreeA(&c);
|
|
throw;
|
|
}
|
|
RpcStringFreeA(&c);
|
|
named_pipe p("\\\\.\\pipe\\boost_process_" + name);
|
|
return p(stype);
|
|
}
|
|
};
|
|
#endif
|
|
|
|
/**
|
|
* Stream behavior to mute streams.
|
|
*
|
|
* A child process will be able to use streams. But data written to an
|
|
* output stream is discarded and data read from an input stream is 0.
|
|
*/
|
|
class null
|
|
{
|
|
public:
|
|
#if defined(BOOST_POSIX_API)
|
|
null()
|
|
: stype_(unknown_stream)
|
|
{
|
|
}
|
|
|
|
null(stream_type stype)
|
|
: stype_(stype)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
stream_ends operator()(stream_type stype) const
|
|
{
|
|
#if defined(BOOST_POSIX_API)
|
|
if (stype_ != unknown_stream)
|
|
stype = stype_;
|
|
std::string filename = (stype == input_stream) ? "/dev/zero" :
|
|
"/dev/null";
|
|
int flag = (stype == input_stream) ? O_RDONLY : O_WRONLY;
|
|
handle child_end = open(filename.c_str(), flag);
|
|
if (!child_end.valid())
|
|
BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("open(2) failed");
|
|
#elif defined(BOOST_WINDOWS_API)
|
|
DWORD access = (stype == input_stream) ? GENERIC_READ : GENERIC_WRITE;
|
|
handle child_end = CreateFileA("NUL", access, 0, NULL, OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (!child_end.valid())
|
|
BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR("CreateFile() failed");
|
|
if (!SetHandleInformation(child_end.native(), HANDLE_FLAG_INHERIT,
|
|
HANDLE_FLAG_INHERIT))
|
|
BOOST_PROCESS_THROW_LAST_SYSTEM_ERROR(
|
|
"SetHandleInformation() failed");
|
|
#endif
|
|
return stream_ends(child_end, handle());
|
|
}
|
|
|
|
#if defined(BOOST_POSIX_API)
|
|
private:
|
|
stream_type stype_;
|
|
#endif
|
|
};
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|