Files
SAPFOR/Sapfor/_src/ProjectManipulation/StdCapture.h

196 lines
4.9 KiB
C
Raw Normal View History

2024-02-20 11:12:00 +03:00
#pragma once
#include <string>
#include <mutex>
2024-02-20 13:15:05 +03:00
#include <thread>
2024-02-20 11:12:00 +03:00
#include <fcntl.h>
#ifdef _WIN32
#include <io.h>
#include <direct.h>
#define popen _popen
#define pclose _pclose
#define stat _stat
#define dup _dup
#define dup2 _dup2
#define fileno _fileno
#define close _close
#define pipe _pipe
#define read _read
#define eof _eof
#else
#include <unistd.h>
#endif
#ifndef STD_OUT_FD
#define STD_OUT_FD (fileno(stdout))
#endif
#ifndef STD_ERR_FD
#define STD_ERR_FD (fileno(stderr))
#endif
2024-02-20 12:42:35 +03:00
static int m_pipe[2];
static int m_oldStdOut;
static int m_oldStdErr;
static bool m_capturing;
static std::mutex m_mutex;
static std::string m_captured;
2024-02-20 11:12:00 +03:00
class StdCapture
{
enum PIPES { READ, WRITE };
2024-02-20 12:42:35 +03:00
static int secure_dup(int src)
2024-02-20 11:12:00 +03:00
{
int ret = -1;
bool fd_blocked = false;
do
{
ret = dup(src);
fd_blocked = (errno == EINTR || errno == EBUSY);
if (fd_blocked)
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} while (ret < 0);
return ret;
}
2024-02-20 12:42:35 +03:00
static void secure_pipe(int* pipes)
2024-02-20 11:12:00 +03:00
{
int ret = -1;
bool fd_blocked = false;
do
{
#ifdef _WIN32
ret = pipe(pipes, 1024 * 1024 * 20, O_BINARY); // 20 MB
#else
ret = pipe(pipes) == -1;
2024-10-05 00:21:00 +03:00
#ifdef __APPLE__
fcntl(*pipes, F_PREALLOCATE, 1024 * 1024 * 20);
#else
2024-02-20 11:12:00 +03:00
fcntl(*pipes, F_SETPIPE_SZ, 1024 * 1024 * 20);
2024-10-05 00:21:00 +03:00
#endif
2024-02-20 11:12:00 +03:00
#endif
fd_blocked = (errno == EINTR || errno == EBUSY);
if (fd_blocked)
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} while (ret < 0);
}
2024-02-20 12:42:35 +03:00
static void secure_dup2(int src, int dest)
2024-02-20 11:12:00 +03:00
{
int ret = -1;
bool fd_blocked = false;
do
{
ret = dup2(src, dest);
fd_blocked = (errno == EINTR || errno == EBUSY);
if (fd_blocked)
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} while (ret < 0);
}
2024-02-20 12:42:35 +03:00
static void secure_close(int& fd)
2024-02-20 11:12:00 +03:00
{
int ret = -1;
bool fd_blocked = false;
do
{
ret = close(fd);
fd_blocked = (errno == EINTR);
if (fd_blocked)
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} while (ret < 0);
fd = -1;
}
public:
2024-02-20 12:42:35 +03:00
static void Init()
2024-02-20 11:12:00 +03:00
{
// make stdout & stderr streams unbuffered
// so that we don't need to flush the streams
// before capture and after capture
// (fflush can cause a deadlock if the stream is currently being
std::lock_guard<std::mutex> lock(m_mutex);
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
}
2024-02-20 12:42:35 +03:00
static void BeginCapture()
2024-02-20 11:12:00 +03:00
{
std::lock_guard<std::mutex> lock(m_mutex);
if (m_capturing)
return;
secure_pipe(m_pipe);
m_oldStdOut = secure_dup(STD_OUT_FD);
m_oldStdErr = secure_dup(STD_ERR_FD);
secure_dup2(m_pipe[WRITE], STD_OUT_FD);
secure_dup2(m_pipe[WRITE], STD_ERR_FD);
m_capturing = true;
#ifndef _WIN32
secure_close(m_pipe[WRITE]);
#endif
}
2024-02-20 12:42:35 +03:00
static bool IsCapturing()
2024-02-20 11:12:00 +03:00
{
std::lock_guard<std::mutex> lock(m_mutex);
return m_capturing;
}
2024-02-20 12:42:35 +03:00
static void EndCapture()
2024-02-20 11:12:00 +03:00
{
std::lock_guard<std::mutex> lock(m_mutex);
if (!m_capturing)
return;
m_captured.clear();
secure_dup2(m_oldStdOut, STD_OUT_FD);
secure_dup2(m_oldStdErr, STD_ERR_FD);
const int bufSize = 1025;
char buf[bufSize];
int bytesRead = 0;
bool fd_blocked(false);
do
{
bytesRead = 0;
fd_blocked = false;
#ifdef _WIN32
if (!eof(m_pipe[READ]))
bytesRead = read(m_pipe[READ], buf, bufSize - 1);
#else
bytesRead = read(m_pipe[READ], buf, bufSize - 1);
#endif
if (bytesRead > 0)
{
buf[bytesRead] = 0;
m_captured += buf;
}
else if (bytesRead < 0)
{
fd_blocked = (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR);
if (fd_blocked)
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
} while (fd_blocked || bytesRead == (bufSize - 1));
secure_close(m_oldStdOut);
secure_close(m_oldStdErr);
secure_close(m_pipe[READ]);
#ifdef _WIN32
secure_close(m_pipe[WRITE]);
#endif
m_capturing = false;
}
2024-02-20 12:42:35 +03:00
static std::string GetCapture()
2024-02-20 11:12:00 +03:00
{
std::lock_guard<std::mutex> lock(m_mutex);
return m_captured;
}
};