|
|
|
|
@ -1,8 +1,14 @@
|
|
|
|
|
#include <string>
|
|
|
|
|
#include <mysql.h>
|
|
|
|
|
|
|
|
|
|
#include "tap.h"
|
|
|
|
|
#include "utils.h"
|
|
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <sys/wait.h>
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <iostream>
|
|
|
|
|
|
|
|
|
|
int show_variable(MYSQL *mysql, const std::string& var_name, std::string& var_value) {
|
|
|
|
|
char query[128];
|
|
|
|
|
|
|
|
|
|
@ -102,3 +108,200 @@ int get_server_version(MYSQL *mysql, std::string& version) {
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Pipes definition
|
|
|
|
|
constexpr uint8_t NUM_PIPES = 3;
|
|
|
|
|
constexpr uint8_t PARENT_WRITE_PIPE = 0;
|
|
|
|
|
constexpr uint8_t PARENT_READ_PIPE = 1;
|
|
|
|
|
constexpr uint8_t PARENT_ERR_PIPE = 2;
|
|
|
|
|
int pipes[NUM_PIPES][2];
|
|
|
|
|
// Pipe selection
|
|
|
|
|
constexpr uint8_t READ_FD = 0;
|
|
|
|
|
constexpr uint8_t WRITE_FD = 1;
|
|
|
|
|
// Parent pipes
|
|
|
|
|
const auto& PARENT_READ_FD = pipes[PARENT_READ_PIPE][READ_FD];
|
|
|
|
|
const auto& PARENT_READ_ERR = pipes[PARENT_ERR_PIPE][READ_FD];
|
|
|
|
|
const auto& PARENT_WRITE_FD = pipes[PARENT_WRITE_PIPE][WRITE_FD];
|
|
|
|
|
// Child pipes
|
|
|
|
|
const auto& CHILD_READ_FD = pipes[PARENT_WRITE_PIPE][READ_FD];
|
|
|
|
|
const auto& CHILD_WRITE_FD = pipes[PARENT_READ_PIPE][WRITE_FD];
|
|
|
|
|
const auto& CHILD_WRITE_ERR = pipes[PARENT_ERR_PIPE][WRITE_FD];
|
|
|
|
|
|
|
|
|
|
int kill_child_proc(pid_t child_pid, const uint timeout_us, const uint it_sleep_us) {
|
|
|
|
|
uint err = 0;
|
|
|
|
|
uint waited = 0;
|
|
|
|
|
int child_status = 0;
|
|
|
|
|
|
|
|
|
|
err = kill(child_pid, SIGTERM);
|
|
|
|
|
|
|
|
|
|
while (waitpid(child_pid, &child_status, WNOHANG) > 0) {
|
|
|
|
|
if (waited >= timeout_us) {
|
|
|
|
|
kill(child_pid, SIGKILL);
|
|
|
|
|
waited = 0;
|
|
|
|
|
} else {
|
|
|
|
|
waited += it_sleep_us;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
usleep(it_sleep_us);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int read_pipe(int pipe_fd, std::string& sbuffer) {
|
|
|
|
|
char buffer[128];
|
|
|
|
|
char stderr_buffer[128];
|
|
|
|
|
ssize_t count = 0;
|
|
|
|
|
int res = 1;
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
count = read(pipe_fd, buffer, sizeof(buffer));
|
|
|
|
|
if (count > 0) {
|
|
|
|
|
buffer[count] = 0;
|
|
|
|
|
sbuffer += buffer;
|
|
|
|
|
} else if (count == 0){
|
|
|
|
|
res = 0;
|
|
|
|
|
break;
|
|
|
|
|
} else {
|
|
|
|
|
if (errno != EWOULDBLOCK && errno != EINTR) {
|
|
|
|
|
res = -1;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int wexecvp(const std::string& file, const std::vector<const char*>& argv, const to_opts* opts, std::string& s_stdout, std::string& s_stderr) {
|
|
|
|
|
int err = 0;
|
|
|
|
|
int pipe_err = 0;
|
|
|
|
|
std::string stdout_ = "";
|
|
|
|
|
std::string stderr_ = "";
|
|
|
|
|
std::vector<const char*> _argv = argv;
|
|
|
|
|
to_opts to_opts { 1000*1000, 1000*100 };
|
|
|
|
|
|
|
|
|
|
// Append null to end of _argv for extra safety
|
|
|
|
|
_argv.push_back(nullptr);
|
|
|
|
|
// Duplicate file argument to avoid manual duplication
|
|
|
|
|
_argv.insert(_argv.begin(), file.c_str());
|
|
|
|
|
|
|
|
|
|
if (opts) {
|
|
|
|
|
to_opts.timeout_us = opts->timeout_us;
|
|
|
|
|
to_opts.it_delay_us = opts->it_delay_us;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int outfd[2];
|
|
|
|
|
int infd[2];
|
|
|
|
|
|
|
|
|
|
// Pipes for parent to write and read
|
|
|
|
|
pipe(pipes[PARENT_READ_PIPE]);
|
|
|
|
|
pipe(pipes[PARENT_WRITE_PIPE]);
|
|
|
|
|
pipe(pipes[PARENT_ERR_PIPE]);
|
|
|
|
|
|
|
|
|
|
pid_t child_pid = fork();
|
|
|
|
|
if(child_pid == 0) {
|
|
|
|
|
// Copy the pipe descriptors
|
|
|
|
|
dup2(CHILD_READ_FD, STDIN_FILENO);
|
|
|
|
|
dup2(CHILD_WRITE_FD, STDOUT_FILENO);
|
|
|
|
|
dup2(CHILD_WRITE_ERR, STDERR_FILENO);
|
|
|
|
|
|
|
|
|
|
// Close no longer needed pipes
|
|
|
|
|
close(CHILD_READ_FD);
|
|
|
|
|
close(CHILD_WRITE_FD);
|
|
|
|
|
close(CHILD_WRITE_ERR);
|
|
|
|
|
|
|
|
|
|
close(PARENT_READ_FD);
|
|
|
|
|
close(PARENT_READ_ERR);
|
|
|
|
|
close(PARENT_WRITE_FD);
|
|
|
|
|
|
|
|
|
|
char** args = const_cast<char**>(_argv.data());
|
|
|
|
|
err = execvp(file.c_str(), args);
|
|
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
|
exit(errno);
|
|
|
|
|
} else {
|
|
|
|
|
exit(0);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
int errno_cpy = 0;
|
|
|
|
|
char stdout_buffer[128];
|
|
|
|
|
char stderr_buffer[128];
|
|
|
|
|
ssize_t stdout_count = 0;
|
|
|
|
|
ssize_t stderr_count = 0;
|
|
|
|
|
|
|
|
|
|
// Close no longer needed pipes
|
|
|
|
|
close(CHILD_READ_FD);
|
|
|
|
|
close(CHILD_WRITE_FD);
|
|
|
|
|
close(CHILD_WRITE_ERR);
|
|
|
|
|
|
|
|
|
|
// Set the pipes in non-blocking mode
|
|
|
|
|
fcntl(PARENT_READ_FD, F_SETFL, fcntl(PARENT_READ_FD, F_GETFL) | O_NONBLOCK);
|
|
|
|
|
fcntl(PARENT_READ_ERR, F_SETFL, fcntl(PARENT_READ_ERR, F_GETFL) | O_NONBLOCK);
|
|
|
|
|
|
|
|
|
|
fd_set read_fds;
|
|
|
|
|
uint read_fds_sz = 2;
|
|
|
|
|
int maxfd = PARENT_READ_FD > PARENT_READ_ERR ? PARENT_READ_FD : PARENT_READ_ERR;
|
|
|
|
|
|
|
|
|
|
bool stdout_eof = false;
|
|
|
|
|
bool stderr_eof = false;
|
|
|
|
|
|
|
|
|
|
while (!stdout_eof && !stderr_eof) {
|
|
|
|
|
FD_ZERO(&read_fds);
|
|
|
|
|
FD_SET(PARENT_READ_FD, &read_fds);
|
|
|
|
|
FD_SET(PARENT_READ_ERR, &read_fds);
|
|
|
|
|
|
|
|
|
|
// Wait for the pipes to be ready
|
|
|
|
|
select(maxfd + 1, &read_fds, NULL, NULL, NULL);
|
|
|
|
|
|
|
|
|
|
if (FD_ISSET(PARENT_READ_FD, &read_fds)) {
|
|
|
|
|
int read_res = read_pipe(PARENT_READ_FD, stdout_);
|
|
|
|
|
|
|
|
|
|
if (read_res == 0) {
|
|
|
|
|
stdout_eof = true;
|
|
|
|
|
}
|
|
|
|
|
// Unexpected error while reading pipe
|
|
|
|
|
if (read_res < 0) {
|
|
|
|
|
pipe_err = -1;
|
|
|
|
|
// Backup read errno
|
|
|
|
|
errno_cpy = errno;
|
|
|
|
|
// Kill child and return error
|
|
|
|
|
kill_child_proc(child_pid, to_opts.timeout_us, to_opts.it_delay_us);
|
|
|
|
|
// Recover errno before return
|
|
|
|
|
errno = errno_cpy;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(FD_ISSET(PARENT_READ_ERR, &read_fds)) {
|
|
|
|
|
int read_res = read_pipe(PARENT_READ_ERR, stderr_);
|
|
|
|
|
|
|
|
|
|
if (read_res == 0) {
|
|
|
|
|
stderr_eof = true;
|
|
|
|
|
}
|
|
|
|
|
// Unexpected error while reading pipe
|
|
|
|
|
if (read_res < 0) {
|
|
|
|
|
pipe_err = -1;
|
|
|
|
|
// Backup read errno
|
|
|
|
|
errno_cpy = errno;
|
|
|
|
|
// Kill child and return error
|
|
|
|
|
kill_child_proc(child_pid, to_opts.timeout_us, to_opts.it_delay_us);
|
|
|
|
|
// Recover errno before return
|
|
|
|
|
errno = errno_cpy;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pipe_err == 0) {
|
|
|
|
|
waitpid(child_pid, &err, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pipe_err == 0) {
|
|
|
|
|
s_stdout = stdout_;
|
|
|
|
|
s_stderr = stderr_;
|
|
|
|
|
} else {
|
|
|
|
|
err = pipe_err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|