mirror of
https://github.com/MaskRay/ccls.git
synced 2025-02-21 16:09:40 +00:00
Add tiny-process-library
This commit is contained in:
parent
a9bac06b83
commit
6518f8b79a
22
src/tiny-process-library/LICENSE
Normal file
22
src/tiny-process-library/LICENSE
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015-2016 Ole Christian Eidheim
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
42
src/tiny-process-library/README.md
Normal file
42
src/tiny-process-library/README.md
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# tiny-process-library [](https://travis-ci.org/eidheim/tiny-process-library)
|
||||||
|
A small platform independent library making it simple to create and stop new processes in C++, as well as writing to stdin and reading from stdout and stderr of a new process.
|
||||||
|
|
||||||
|
This library was created for, and is used by the C++ IDE project [juCi++](https://github.com/cppit/jucipp).
|
||||||
|
|
||||||
|
### Features
|
||||||
|
* No external dependencies
|
||||||
|
* Simple to use
|
||||||
|
* Platform independent
|
||||||
|
* Creating processes using executables is supported on all platforms
|
||||||
|
* Creating processes using functions is only possible on Unix-like systems
|
||||||
|
* Read separately from stdout and stderr using anonymous functions
|
||||||
|
* Write to stdin
|
||||||
|
* Kill a running process (SIGTERM is supported on Unix-like systems)
|
||||||
|
* Correctly closes file descriptors/handles
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
See [examples.cpp](https://github.com/eidheim/tiny-process-library/blob/master/examples.cpp).
|
||||||
|
|
||||||
|
### Get, compile and run
|
||||||
|
|
||||||
|
#### Unix-like systems
|
||||||
|
```sh
|
||||||
|
git clone http://github.com/eidheim/tiny-process-library
|
||||||
|
cd tiny-process-library
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
./examples
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Windows with MSYS2 (https://msys2.github.io/)
|
||||||
|
```sh
|
||||||
|
git clone http://github.com/eidheim/tiny-process-library
|
||||||
|
cd tiny-process-library
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake -G"MSYS Makefiles" ..
|
||||||
|
make
|
||||||
|
./examples
|
||||||
|
```
|
26
src/tiny-process-library/process.cpp
Normal file
26
src/tiny-process-library/process.cpp
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#include "process.hpp"
|
||||||
|
|
||||||
|
namespace TinyProcessLib {
|
||||||
|
|
||||||
|
Process::Process(const string_type &command, const string_type &path,
|
||||||
|
std::function<void(const char* bytes, size_t n)> read_stdout,
|
||||||
|
std::function<void(const char* bytes, size_t n)> read_stderr,
|
||||||
|
bool open_stdin, size_t buffer_size) noexcept:
|
||||||
|
closed(true), read_stdout(read_stdout), read_stderr(read_stderr), open_stdin(open_stdin), buffer_size(buffer_size) {
|
||||||
|
open(command, path);
|
||||||
|
async_read();
|
||||||
|
}
|
||||||
|
|
||||||
|
Process::~Process() noexcept {
|
||||||
|
close_fds();
|
||||||
|
}
|
||||||
|
|
||||||
|
Process::id_type Process::get_id() const noexcept {
|
||||||
|
return data.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Process::write(const std::string &data) {
|
||||||
|
return write(data.c_str(), data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // TinyProsessLib
|
99
src/tiny-process-library/process.hpp
Normal file
99
src/tiny-process-library/process.hpp
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
#ifndef TINY_PROCESS_LIBRARY_HPP_
|
||||||
|
#define TINY_PROCESS_LIBRARY_HPP_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <functional>
|
||||||
|
#include <vector>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
#include <memory>
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace TinyProcessLib {
|
||||||
|
|
||||||
|
///Platform independent class for creating processes
|
||||||
|
class Process {
|
||||||
|
public:
|
||||||
|
#ifdef _WIN32
|
||||||
|
typedef unsigned long id_type; //Process id type
|
||||||
|
typedef void *fd_type; //File descriptor type
|
||||||
|
#ifdef UNICODE
|
||||||
|
typedef std::wstring string_type;
|
||||||
|
#else
|
||||||
|
typedef std::string string_type;
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
typedef pid_t id_type;
|
||||||
|
typedef int fd_type;
|
||||||
|
typedef std::string string_type;
|
||||||
|
#endif
|
||||||
|
private:
|
||||||
|
class Data {
|
||||||
|
public:
|
||||||
|
Data() noexcept ;
|
||||||
|
id_type id;
|
||||||
|
#ifdef _WIN32
|
||||||
|
void *handle;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
public:
|
||||||
|
///Note on Windows: it seems not possible to specify which pipes to redirect.
|
||||||
|
///Thus, at the moment, if read_stdout==nullptr, read_stderr==nullptr and open_stdin==false,
|
||||||
|
///the stdout, stderr and stdin are sent to the parent process instead.
|
||||||
|
Process(const string_type &command, const string_type &path=string_type(),
|
||||||
|
std::function<void(const char *bytes, size_t n)> read_stdout=nullptr,
|
||||||
|
std::function<void(const char *bytes, size_t n)> read_stderr=nullptr,
|
||||||
|
bool open_stdin=false,
|
||||||
|
size_t buffer_size=131072) noexcept;
|
||||||
|
#ifndef _WIN32
|
||||||
|
/// Supported on Unix-like systems only.
|
||||||
|
Process(std::function<void()> function,
|
||||||
|
std::function<void(const char *bytes, size_t n)> read_stdout=nullptr,
|
||||||
|
std::function<void(const char *bytes, size_t n)> read_stderr=nullptr,
|
||||||
|
bool open_stdin=false,
|
||||||
|
size_t buffer_size=131072) noexcept;
|
||||||
|
#endif
|
||||||
|
~Process() noexcept;
|
||||||
|
|
||||||
|
///Get the process id of the started process.
|
||||||
|
id_type get_id() const noexcept;
|
||||||
|
///Wait until process is finished, and return exit status.
|
||||||
|
int get_exit_status() noexcept;
|
||||||
|
///Write to stdin.
|
||||||
|
bool write(const char *bytes, size_t n);
|
||||||
|
///Write to stdin. Convenience function using write(const char *, size_t).
|
||||||
|
bool write(const std::string &data);
|
||||||
|
///Close stdin. If the process takes parameters from stdin, use this to notify that all parameters have been sent.
|
||||||
|
void close_stdin() noexcept;
|
||||||
|
|
||||||
|
///Kill the process. force=true is only supported on Unix-like systems.
|
||||||
|
void kill(bool force=false) noexcept;
|
||||||
|
///Kill a given process id. Use kill(bool force) instead if possible. force=true is only supported on Unix-like systems.
|
||||||
|
static void kill(id_type id, bool force=false) noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Data data;
|
||||||
|
bool closed;
|
||||||
|
std::mutex close_mutex;
|
||||||
|
std::function<void(const char* bytes, size_t n)> read_stdout;
|
||||||
|
std::function<void(const char* bytes, size_t n)> read_stderr;
|
||||||
|
std::thread stdout_thread, stderr_thread;
|
||||||
|
bool open_stdin;
|
||||||
|
std::mutex stdin_mutex;
|
||||||
|
size_t buffer_size;
|
||||||
|
|
||||||
|
std::unique_ptr<fd_type> stdout_fd, stderr_fd, stdin_fd;
|
||||||
|
|
||||||
|
id_type open(const string_type &command, const string_type &path) noexcept;
|
||||||
|
#ifndef _WIN32
|
||||||
|
id_type open(std::function<void()> function) noexcept;
|
||||||
|
#endif
|
||||||
|
void async_read() noexcept;
|
||||||
|
void close_fds() noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // TinyProsessLib
|
||||||
|
|
||||||
|
#endif // TINY_PROCESS_LIBRARY_HPP_
|
204
src/tiny-process-library/process_unix.cpp
Normal file
204
src/tiny-process-library/process_unix.cpp
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
#include "process.hpp"
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace TinyProcessLib {
|
||||||
|
|
||||||
|
Process::Data::Data() noexcept : id(-1) {}
|
||||||
|
|
||||||
|
Process::Process(std::function<void()> function,
|
||||||
|
std::function<void (const char *, size_t)> read_stdout,
|
||||||
|
std::function<void (const char *, size_t)> read_stderr,
|
||||||
|
bool open_stdin, size_t buffer_size) noexcept :
|
||||||
|
closed(true), read_stdout(read_stdout), read_stderr(read_stderr), open_stdin(open_stdin), buffer_size(buffer_size) {
|
||||||
|
open(function);
|
||||||
|
async_read();
|
||||||
|
}
|
||||||
|
|
||||||
|
Process::id_type Process::open(std::function<void()> function) noexcept {
|
||||||
|
if(open_stdin)
|
||||||
|
stdin_fd=std::unique_ptr<fd_type>(new fd_type);
|
||||||
|
if(read_stdout)
|
||||||
|
stdout_fd=std::unique_ptr<fd_type>(new fd_type);
|
||||||
|
if(read_stderr)
|
||||||
|
stderr_fd=std::unique_ptr<fd_type>(new fd_type);
|
||||||
|
|
||||||
|
int stdin_p[2], stdout_p[2], stderr_p[2];
|
||||||
|
|
||||||
|
if(stdin_fd && pipe(stdin_p)!=0)
|
||||||
|
return -1;
|
||||||
|
if(stdout_fd && pipe(stdout_p)!=0) {
|
||||||
|
if(stdin_fd) {close(stdin_p[0]);close(stdin_p[1]);}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(stderr_fd && pipe(stderr_p)!=0) {
|
||||||
|
if(stdin_fd) {close(stdin_p[0]);close(stdin_p[1]);}
|
||||||
|
if(stdout_fd) {close(stdout_p[0]);close(stdout_p[1]);}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
id_type pid = fork();
|
||||||
|
|
||||||
|
if (pid < 0) {
|
||||||
|
if(stdin_fd) {close(stdin_p[0]);close(stdin_p[1]);}
|
||||||
|
if(stdout_fd) {close(stdout_p[0]);close(stdout_p[1]);}
|
||||||
|
if(stderr_fd) {close(stderr_p[0]);close(stderr_p[1]);}
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
else if (pid == 0) {
|
||||||
|
if(stdin_fd) dup2(stdin_p[0], 0);
|
||||||
|
if(stdout_fd) dup2(stdout_p[1], 1);
|
||||||
|
if(stderr_fd) dup2(stderr_p[1], 2);
|
||||||
|
if(stdin_fd) {close(stdin_p[0]);close(stdin_p[1]);}
|
||||||
|
if(stdout_fd) {close(stdout_p[0]);close(stdout_p[1]);}
|
||||||
|
if(stderr_fd) {close(stderr_p[0]);close(stderr_p[1]);}
|
||||||
|
|
||||||
|
//Based on http://stackoverflow.com/a/899533/3808293
|
||||||
|
int fd_max=static_cast<int>(sysconf(_SC_OPEN_MAX)); // truncation is safe
|
||||||
|
for(int fd=3;fd<fd_max;fd++)
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
setpgid(0, 0);
|
||||||
|
//TODO: See here on how to emulate tty for colors: http://stackoverflow.com/questions/1401002/trick-an-application-into-thinking-its-stdin-is-interactive-not-a-pipe
|
||||||
|
//TODO: One solution is: echo "command;exit"|script -q /dev/null
|
||||||
|
|
||||||
|
if(function)
|
||||||
|
function();
|
||||||
|
|
||||||
|
_exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(stdin_fd) close(stdin_p[0]);
|
||||||
|
if(stdout_fd) close(stdout_p[1]);
|
||||||
|
if(stderr_fd) close(stderr_p[1]);
|
||||||
|
|
||||||
|
if(stdin_fd) *stdin_fd = stdin_p[1];
|
||||||
|
if(stdout_fd) *stdout_fd = stdout_p[0];
|
||||||
|
if(stderr_fd) *stderr_fd = stderr_p[0];
|
||||||
|
|
||||||
|
closed=false;
|
||||||
|
data.id=pid;
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
Process::id_type Process::open(const std::string &command, const std::string &path) noexcept {
|
||||||
|
return open([&command, &path] {
|
||||||
|
if(!path.empty()) {
|
||||||
|
auto path_escaped=path;
|
||||||
|
size_t pos=0;
|
||||||
|
//Based on https://www.reddit.com/r/cpp/comments/3vpjqg/a_new_platform_independent_process_library_for_c11/cxsxyb7
|
||||||
|
while((pos=path_escaped.find('\'', pos))!=std::string::npos) {
|
||||||
|
path_escaped.replace(pos, 1, "'\\''");
|
||||||
|
pos+=4;
|
||||||
|
}
|
||||||
|
execl("/bin/sh", "sh", "-c", ("cd '"+path_escaped+"' && "+command).c_str(), NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
execl("/bin/sh", "sh", "-c", command.c_str(), NULL);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void Process::async_read() noexcept {
|
||||||
|
if(data.id<=0)
|
||||||
|
return;
|
||||||
|
if(stdout_fd) {
|
||||||
|
stdout_thread=std::thread([this](){
|
||||||
|
auto buffer = std::unique_ptr<char[]>( new char[buffer_size] );
|
||||||
|
ssize_t n;
|
||||||
|
while ((n=read(*stdout_fd, buffer.get(), buffer_size)) > 0)
|
||||||
|
read_stdout(buffer.get(), static_cast<size_t>(n));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(stderr_fd) {
|
||||||
|
stderr_thread=std::thread([this](){
|
||||||
|
auto buffer = std::unique_ptr<char[]>( new char[buffer_size] );
|
||||||
|
ssize_t n;
|
||||||
|
while ((n=read(*stderr_fd, buffer.get(), buffer_size)) > 0)
|
||||||
|
read_stderr(buffer.get(), static_cast<size_t>(n));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Process::get_exit_status() noexcept {
|
||||||
|
if(data.id<=0)
|
||||||
|
return -1;
|
||||||
|
int exit_status;
|
||||||
|
waitpid(data.id, &exit_status, 0);
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(close_mutex);
|
||||||
|
closed=true;
|
||||||
|
}
|
||||||
|
close_fds();
|
||||||
|
|
||||||
|
if(exit_status>=256)
|
||||||
|
exit_status=exit_status>>8;
|
||||||
|
return exit_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Process::close_fds() noexcept {
|
||||||
|
if(stdout_thread.joinable())
|
||||||
|
stdout_thread.join();
|
||||||
|
if(stderr_thread.joinable())
|
||||||
|
stderr_thread.join();
|
||||||
|
|
||||||
|
if(stdin_fd)
|
||||||
|
close_stdin();
|
||||||
|
if(stdout_fd) {
|
||||||
|
if(data.id>0)
|
||||||
|
close(*stdout_fd);
|
||||||
|
stdout_fd.reset();
|
||||||
|
}
|
||||||
|
if(stderr_fd) {
|
||||||
|
if(data.id>0)
|
||||||
|
close(*stderr_fd);
|
||||||
|
stderr_fd.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Process::write(const char *bytes, size_t n) {
|
||||||
|
if(!open_stdin)
|
||||||
|
throw std::invalid_argument("Can't write to an unopened stdin pipe. Please set open_stdin=true when constructing the process.");
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(stdin_mutex);
|
||||||
|
if(stdin_fd) {
|
||||||
|
if(::write(*stdin_fd, bytes, n)>=0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Process::close_stdin() noexcept {
|
||||||
|
std::lock_guard<std::mutex> lock(stdin_mutex);
|
||||||
|
if(stdin_fd) {
|
||||||
|
if(data.id>0)
|
||||||
|
close(*stdin_fd);
|
||||||
|
stdin_fd.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Process::kill(bool force) noexcept {
|
||||||
|
std::lock_guard<std::mutex> lock(close_mutex);
|
||||||
|
if(data.id>0 && !closed) {
|
||||||
|
if(force)
|
||||||
|
::kill(-data.id, SIGTERM);
|
||||||
|
else
|
||||||
|
::kill(-data.id, SIGINT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Process::kill(id_type id, bool force) noexcept {
|
||||||
|
if(id<=0)
|
||||||
|
return;
|
||||||
|
if(force)
|
||||||
|
::kill(-id, SIGTERM);
|
||||||
|
else
|
||||||
|
::kill(-id, SIGINT);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // TinyProsessLib
|
270
src/tiny-process-library/process_win.cpp
Normal file
270
src/tiny-process-library/process_win.cpp
Normal file
@ -0,0 +1,270 @@
|
|||||||
|
#include "process.hpp"
|
||||||
|
#include <windows.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <TlHelp32.h>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace TinyProcessLib {
|
||||||
|
|
||||||
|
Process::Data::Data() noexcept : id(0), handle(NULL) {}
|
||||||
|
|
||||||
|
// Simple HANDLE wrapper to close it automatically from the destructor.
|
||||||
|
class Handle {
|
||||||
|
public:
|
||||||
|
Handle() : handle(INVALID_HANDLE_VALUE) { }
|
||||||
|
~Handle() {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
void close() {
|
||||||
|
if (handle != INVALID_HANDLE_VALUE)
|
||||||
|
CloseHandle(handle);
|
||||||
|
}
|
||||||
|
HANDLE detach() {
|
||||||
|
HANDLE old_handle = handle;
|
||||||
|
handle = INVALID_HANDLE_VALUE;
|
||||||
|
return old_handle;
|
||||||
|
}
|
||||||
|
operator HANDLE() const { return handle; }
|
||||||
|
HANDLE* operator&() { return &handle; }
|
||||||
|
private:
|
||||||
|
HANDLE handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
//Based on the discussion thread: https://www.reddit.com/r/cpp/comments/3vpjqg/a_new_platform_independent_process_library_for_c11/cxq1wsj
|
||||||
|
std::mutex create_process_mutex;
|
||||||
|
|
||||||
|
//Based on the example at https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx.
|
||||||
|
Process::id_type Process::open(const string_type &command, const string_type &path) noexcept {
|
||||||
|
if(open_stdin)
|
||||||
|
stdin_fd=std::unique_ptr<fd_type>(new fd_type(NULL));
|
||||||
|
if(read_stdout)
|
||||||
|
stdout_fd=std::unique_ptr<fd_type>(new fd_type(NULL));
|
||||||
|
if(read_stderr)
|
||||||
|
stderr_fd=std::unique_ptr<fd_type>(new fd_type(NULL));
|
||||||
|
|
||||||
|
Handle stdin_rd_p;
|
||||||
|
Handle stdin_wr_p;
|
||||||
|
Handle stdout_rd_p;
|
||||||
|
Handle stdout_wr_p;
|
||||||
|
Handle stderr_rd_p;
|
||||||
|
Handle stderr_wr_p;
|
||||||
|
|
||||||
|
SECURITY_ATTRIBUTES security_attributes;
|
||||||
|
|
||||||
|
security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||||
|
security_attributes.bInheritHandle = TRUE;
|
||||||
|
security_attributes.lpSecurityDescriptor = nullptr;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(create_process_mutex);
|
||||||
|
if(stdin_fd) {
|
||||||
|
if (!CreatePipe(&stdin_rd_p, &stdin_wr_p, &security_attributes, 0) ||
|
||||||
|
!SetHandleInformation(stdin_wr_p, HANDLE_FLAG_INHERIT, 0))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if(stdout_fd) {
|
||||||
|
if (!CreatePipe(&stdout_rd_p, &stdout_wr_p, &security_attributes, 0) ||
|
||||||
|
!SetHandleInformation(stdout_rd_p, HANDLE_FLAG_INHERIT, 0)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(stderr_fd) {
|
||||||
|
if (!CreatePipe(&stderr_rd_p, &stderr_wr_p, &security_attributes, 0) ||
|
||||||
|
!SetHandleInformation(stderr_rd_p, HANDLE_FLAG_INHERIT, 0)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PROCESS_INFORMATION process_info;
|
||||||
|
STARTUPINFO startup_info;
|
||||||
|
|
||||||
|
ZeroMemory(&process_info, sizeof(PROCESS_INFORMATION));
|
||||||
|
|
||||||
|
ZeroMemory(&startup_info, sizeof(STARTUPINFO));
|
||||||
|
startup_info.cb = sizeof(STARTUPINFO);
|
||||||
|
startup_info.hStdInput = stdin_rd_p;
|
||||||
|
startup_info.hStdOutput = stdout_wr_p;
|
||||||
|
startup_info.hStdError = stderr_wr_p;
|
||||||
|
if(stdin_fd || stdout_fd || stderr_fd)
|
||||||
|
startup_info.dwFlags |= STARTF_USESTDHANDLES;
|
||||||
|
|
||||||
|
string_type process_command=command;
|
||||||
|
#ifdef MSYS_PROCESS_USE_SH
|
||||||
|
size_t pos=0;
|
||||||
|
while((pos=process_command.find('\\', pos))!=string_type::npos) {
|
||||||
|
process_command.replace(pos, 1, "\\\\\\\\");
|
||||||
|
pos+=4;
|
||||||
|
}
|
||||||
|
pos=0;
|
||||||
|
while((pos=process_command.find('\"', pos))!=string_type::npos) {
|
||||||
|
process_command.replace(pos, 1, "\\\"");
|
||||||
|
pos+=2;
|
||||||
|
}
|
||||||
|
process_command.insert(0, "sh -c \"");
|
||||||
|
process_command+="\"";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
BOOL bSuccess = CreateProcess(nullptr, process_command.empty()?nullptr:&process_command[0], nullptr, nullptr, TRUE, 0,
|
||||||
|
nullptr, path.empty()?nullptr:path.c_str(), &startup_info, &process_info);
|
||||||
|
|
||||||
|
if(!bSuccess) {
|
||||||
|
CloseHandle(process_info.hProcess);
|
||||||
|
CloseHandle(process_info.hThread);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
CloseHandle(process_info.hThread);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(stdin_fd) *stdin_fd=stdin_wr_p.detach();
|
||||||
|
if(stdout_fd) *stdout_fd=stdout_rd_p.detach();
|
||||||
|
if(stderr_fd) *stderr_fd=stderr_rd_p.detach();
|
||||||
|
|
||||||
|
closed=false;
|
||||||
|
data.id=process_info.dwProcessId;
|
||||||
|
data.handle=process_info.hProcess;
|
||||||
|
return process_info.dwProcessId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Process::async_read() noexcept {
|
||||||
|
if(data.id==0)
|
||||||
|
return;
|
||||||
|
if(stdout_fd) {
|
||||||
|
stdout_thread=std::thread([this](){
|
||||||
|
DWORD n;
|
||||||
|
std::unique_ptr<char[]> buffer(new char[buffer_size]);
|
||||||
|
for (;;) {
|
||||||
|
BOOL bSuccess = ReadFile(*stdout_fd, static_cast<CHAR*>(buffer.get()), static_cast<DWORD>(buffer_size), &n, nullptr);
|
||||||
|
if(!bSuccess || n == 0)
|
||||||
|
break;
|
||||||
|
read_stdout(buffer.get(), static_cast<size_t>(n));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(stderr_fd) {
|
||||||
|
stderr_thread=std::thread([this](){
|
||||||
|
DWORD n;
|
||||||
|
std::unique_ptr<char[]> buffer(new char[buffer_size]);
|
||||||
|
for (;;) {
|
||||||
|
BOOL bSuccess = ReadFile(*stderr_fd, static_cast<CHAR*>(buffer.get()), static_cast<DWORD>(buffer_size), &n, nullptr);
|
||||||
|
if(!bSuccess || n == 0)
|
||||||
|
break;
|
||||||
|
read_stderr(buffer.get(), static_cast<size_t>(n));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int Process::get_exit_status() noexcept {
|
||||||
|
if(data.id==0)
|
||||||
|
return -1;
|
||||||
|
DWORD exit_status;
|
||||||
|
WaitForSingleObject(data.handle, INFINITE);
|
||||||
|
if(!GetExitCodeProcess(data.handle, &exit_status))
|
||||||
|
exit_status=-1;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(close_mutex);
|
||||||
|
CloseHandle(data.handle);
|
||||||
|
closed=true;
|
||||||
|
}
|
||||||
|
close_fds();
|
||||||
|
|
||||||
|
return static_cast<int>(exit_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Process::close_fds() noexcept {
|
||||||
|
if(stdout_thread.joinable())
|
||||||
|
stdout_thread.join();
|
||||||
|
if(stderr_thread.joinable())
|
||||||
|
stderr_thread.join();
|
||||||
|
|
||||||
|
if(stdin_fd)
|
||||||
|
close_stdin();
|
||||||
|
if(stdout_fd) {
|
||||||
|
if(*stdout_fd!=NULL) CloseHandle(*stdout_fd);
|
||||||
|
stdout_fd.reset();
|
||||||
|
}
|
||||||
|
if(stderr_fd) {
|
||||||
|
if(*stderr_fd!=NULL) CloseHandle(*stderr_fd);
|
||||||
|
stderr_fd.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Process::write(const char *bytes, size_t n) {
|
||||||
|
if(!open_stdin)
|
||||||
|
throw std::invalid_argument("Can't write to an unopened stdin pipe. Please set open_stdin=true when constructing the process.");
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(stdin_mutex);
|
||||||
|
if(stdin_fd) {
|
||||||
|
DWORD written;
|
||||||
|
BOOL bSuccess=WriteFile(*stdin_fd, bytes, static_cast<DWORD>(n), &written, nullptr);
|
||||||
|
if(!bSuccess || written==0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Process::close_stdin() noexcept {
|
||||||
|
std::lock_guard<std::mutex> lock(stdin_mutex);
|
||||||
|
if(stdin_fd) {
|
||||||
|
if(*stdin_fd!=NULL) CloseHandle(*stdin_fd);
|
||||||
|
stdin_fd.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Based on http://stackoverflow.com/a/1173396
|
||||||
|
void Process::kill(bool force) noexcept {
|
||||||
|
std::lock_guard<std::mutex> lock(close_mutex);
|
||||||
|
if(data.id>0 && !closed) {
|
||||||
|
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||||
|
if(snapshot) {
|
||||||
|
PROCESSENTRY32 process;
|
||||||
|
ZeroMemory(&process, sizeof(process));
|
||||||
|
process.dwSize = sizeof(process);
|
||||||
|
if(Process32First(snapshot, &process)) {
|
||||||
|
do {
|
||||||
|
if(process.th32ParentProcessID==data.id) {
|
||||||
|
HANDLE process_handle = OpenProcess(PROCESS_TERMINATE, FALSE, process.th32ProcessID);
|
||||||
|
if(process_handle) {
|
||||||
|
TerminateProcess(process_handle, 2);
|
||||||
|
CloseHandle(process_handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (Process32Next(snapshot, &process));
|
||||||
|
}
|
||||||
|
CloseHandle(snapshot);
|
||||||
|
}
|
||||||
|
TerminateProcess(data.handle, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Based on http://stackoverflow.com/a/1173396
|
||||||
|
void Process::kill(id_type id, bool force) noexcept {
|
||||||
|
if(id==0)
|
||||||
|
return;
|
||||||
|
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||||
|
if(snapshot) {
|
||||||
|
PROCESSENTRY32 process;
|
||||||
|
ZeroMemory(&process, sizeof(process));
|
||||||
|
process.dwSize = sizeof(process);
|
||||||
|
if(Process32First(snapshot, &process)) {
|
||||||
|
do {
|
||||||
|
if(process.th32ParentProcessID==id) {
|
||||||
|
HANDLE process_handle = OpenProcess(PROCESS_TERMINATE, FALSE, process.th32ProcessID);
|
||||||
|
if(process_handle) {
|
||||||
|
TerminateProcess(process_handle, 2);
|
||||||
|
CloseHandle(process_handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (Process32Next(snapshot, &process));
|
||||||
|
}
|
||||||
|
CloseHandle(snapshot);
|
||||||
|
}
|
||||||
|
HANDLE process_handle = OpenProcess(PROCESS_TERMINATE, FALSE, id);
|
||||||
|
if(process_handle) TerminateProcess(process_handle, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // TinyProsessLib
|
Loading…
Reference in New Issue
Block a user