6 changed files with 809 additions and 0 deletions
-
9core/components/process/.clang-format
-
8core/components/process/README.md
-
40core/components/process/process.cpp
-
133core/components/process/process.hpp
-
614core/components/process/process_unix.cpp
-
5module.cmake
@ -0,0 +1,9 @@ |
|||
IndentWidth: 2 |
|||
AccessModifierOffset: -2 |
|||
UseTab: Never |
|||
ColumnLimit: 0 |
|||
MaxEmptyLinesToKeep: 2 |
|||
SpaceBeforeParens: Never |
|||
BreakBeforeBraces: Custom |
|||
BraceWrapping: {BeforeElse: true, BeforeCatch: true} |
|||
NamespaceIndentation: None |
@ -0,0 +1,8 @@ |
|||
# tiny-process-library |
|||
|
|||
使用说明: |
|||
``` |
|||
|
|||
|
|||
|
|||
``` |
@ -0,0 +1,40 @@ |
|||
#include "process.hpp"
|
|||
|
|||
namespace iflytop { |
|||
namespace process { |
|||
|
|||
|
|||
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, const Config &config) noexcept |
|||
: closed(true), read_stdout(std::move(read_stdout)), read_stderr(std::move(read_stderr)), open_stdin(open_stdin), config(config) { |
|||
open(command, path); |
|||
async_read(); |
|||
} |
|||
|
|||
|
|||
Process::Process(const string_type &command, const string_type &path, |
|||
const environment_type &environment, |
|||
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, const Config &config) noexcept |
|||
: closed(true), read_stdout(std::move(read_stdout)), read_stderr(std::move(read_stderr)), open_stdin(open_stdin), config(config) { |
|||
open(command, path, &environment); |
|||
async_read(); |
|||
} |
|||
|
|||
Process::~Process() noexcept { |
|||
close_fds(); |
|||
} |
|||
|
|||
Process::id_type Process::get_id() const noexcept { |
|||
return data.id; |
|||
} |
|||
|
|||
bool Process::write(const std::string &str) { |
|||
return write(str.c_str(), str.size()); |
|||
} |
|||
|
|||
} // namespace process
|
|||
} // namespace iflytop
|
@ -0,0 +1,133 @@ |
|||
#ifndef TINY_PROCESS_LIBRARY_HPP_
|
|||
#define TINY_PROCESS_LIBRARY_HPP_
|
|||
#include <functional>
|
|||
#include <memory>
|
|||
#include <mutex>
|
|||
#include <string>
|
|||
#include <sys/wait.h>
|
|||
#include <thread>
|
|||
#include <unordered_map>
|
|||
#include <vector>
|
|||
|
|||
namespace iflytop { |
|||
namespace process { |
|||
/// Additional parameters to Process constructors.
|
|||
struct Config { |
|||
/// Buffer size for reading stdout and stderr. Default is 131072 (128 kB).
|
|||
std::size_t buffer_size = 131072; |
|||
/// Set to true to inherit file descriptors from parent process. Default is false.
|
|||
/// On Windows: has no effect unless read_stdout==nullptr, read_stderr==nullptr and open_stdin==false.
|
|||
bool inherit_file_descriptors = false; |
|||
|
|||
/// On Windows only: controls how the process is started, mimics STARTUPINFO's wShowWindow.
|
|||
/// See: https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/ns-processthreadsapi-startupinfoa
|
|||
/// and https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-showwindow
|
|||
enum class ShowWindow { |
|||
hide = 0, |
|||
show_normal = 1, |
|||
show_minimized = 2, |
|||
maximize = 3, |
|||
show_maximized = 3, |
|||
show_no_activate = 4, |
|||
show = 5, |
|||
minimize = 6, |
|||
show_min_no_active = 7, |
|||
show_na = 8, |
|||
restore = 9, |
|||
show_default = 10, |
|||
force_minimize = 11 |
|||
}; |
|||
/// On Windows only: controls how the window is shown.
|
|||
ShowWindow show_window{ShowWindow::show_default}; |
|||
}; |
|||
|
|||
/// Platform independent class for creating processes.
|
|||
/// 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.
|
|||
class Process { |
|||
public: |
|||
typedef pid_t id_type; |
|||
typedef int fd_type; |
|||
typedef std::string string_type; |
|||
typedef std::unordered_map<string_type, string_type> environment_type; |
|||
|
|||
private: |
|||
class Data { |
|||
public: |
|||
Data() noexcept; |
|||
id_type id; |
|||
int exit_status{-1}; |
|||
}; |
|||
|
|||
public: |
|||
/// Starts a process with the environment of the calling process.
|
|||
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 = true, /*默认打开标准输入,使得其行为更接近于system()*/ |
|||
const Config &config = {}) noexcept; |
|||
|
|||
/// Starts a process with specified environment.
|
|||
Process(const string_type &command, |
|||
const string_type &path, |
|||
const environment_type &environment, |
|||
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 = true, |
|||
const Config &config = {}) noexcept; /// Starts a process with specified environment.
|
|||
/// Starts a process with the environment of the calling process.
|
|||
/// Supported on Unix-like systems only.
|
|||
Process(const 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 = true, |
|||
const Config &config = {}) noexcept; |
|||
~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; |
|||
/// If process is finished, returns true and sets the exit status. Returns false otherwise.
|
|||
bool try_get_exit_status(int &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 &str); |
|||
/// 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; |
|||
void signal(int signum) noexcept; |
|||
|
|||
// void show_all_child_process();
|
|||
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_stderr_thread; |
|||
|
|||
bool open_stdin; |
|||
std::mutex stdin_mutex; |
|||
|
|||
Config config; |
|||
|
|||
std::unique_ptr<fd_type> stdout_fd, stderr_fd, stdin_fd; |
|||
|
|||
id_type open(const string_type &command, const string_type &path, const environment_type *environment = nullptr) noexcept; |
|||
id_type open(const std::function<void()> &function) noexcept; |
|||
void async_read() noexcept; |
|||
void close_fds() noexcept; |
|||
}; |
|||
// 为了确保所有子进程被安全回收,在主进程退出时,调用下面的方法,确保子进程的退出。
|
|||
void killallsubprocess(int, void *); |
|||
|
|||
} // namespace process
|
|||
} // namespace iflytop
|
|||
#endif // TINY_PROCESS_LIBRARY_HPP_
|
@ -0,0 +1,614 @@ |
|||
#include "process.hpp"
|
|||
#include <algorithm>
|
|||
#include <bitset>
|
|||
#include <cstdlib>
|
|||
#include <fcntl.h>
|
|||
#include <poll.h>
|
|||
#include <set>
|
|||
#include <signal.h>
|
|||
#include <stdexcept>
|
|||
#include <string.h>
|
|||
#include <unistd.h>
|
|||
//
|
|||
#include <dirent.h>
|
|||
#include <linux/limits.h>
|
|||
#include <list>
|
|||
#include <math.h>
|
|||
#include <stdio.h>
|
|||
#include <stdlib.h>
|
|||
#include <string.h>
|
|||
#include <sys/prctl.h>
|
|||
#include <sys/times.h>
|
|||
#include <time.h>
|
|||
#include <unistd.h>
|
|||
|
|||
namespace iflytop { |
|||
namespace process { |
|||
using namespace std; |
|||
|
|||
|
|||
typedef long long int num; |
|||
|
|||
typedef struct pid_stat_fields { |
|||
|
|||
num pid; |
|||
char tcomm[PATH_MAX]; |
|||
char state; |
|||
|
|||
num ppid; |
|||
num pgid; |
|||
num sid; |
|||
num tty_nr; |
|||
num tty_pgrp; |
|||
|
|||
num flags; |
|||
num min_flt; |
|||
num cmin_flt; |
|||
num maj_flt; |
|||
num cmaj_flt; |
|||
num utime; |
|||
num stime; |
|||
|
|||
num cutime; |
|||
num cstime; |
|||
num priority; |
|||
num nicev; |
|||
num num_threads; |
|||
num it_real_value; |
|||
|
|||
unsigned long long start_time; |
|||
|
|||
num vsize; |
|||
num rss; |
|||
num rsslim; |
|||
num start_code; |
|||
num end_code; |
|||
num start_stack; |
|||
num esp; |
|||
num eip; |
|||
|
|||
num pending; |
|||
num blocked; |
|||
num sigign; |
|||
num sigcatch; |
|||
num wchan; |
|||
num zero1; |
|||
num zero2; |
|||
num exit_signal; |
|||
num cpu; |
|||
num rt_priority; |
|||
num policy; |
|||
|
|||
} pid_stat_fields; |
|||
|
|||
void readone(num *x, FILE *input) { fscanf(input, "%lld ", x); } |
|||
void readunsigned(unsigned long long *x, FILE *input) { fscanf(input, "%llu ", x); } |
|||
void readstr(char *x, FILE *input) { fscanf(input, "%s ", x); } |
|||
void readchar(char *x, FILE *input) { fscanf(input, "%c ", x); } |
|||
int filter(const struct dirent *dir) { // select number folder
|
|||
int i; |
|||
int n = strlen(dir->d_name); |
|||
for(i = 0; i < n; i++) { |
|||
if(!isdigit(dir->d_name[i])) // 返回所有数字目录
|
|||
return 0; |
|||
else |
|||
return 1; |
|||
} |
|||
return 0; |
|||
} |
|||
pid_stat_fields populate_pid_stats(FILE *stat_file) { |
|||
|
|||
pid_stat_fields stats; |
|||
// reading order is important!!!
|
|||
readone(&(stats.pid), stat_file); |
|||
readstr(&(stats.tcomm[0]), stat_file); |
|||
readchar(&(stats.state), stat_file); |
|||
readone(&(stats.ppid), stat_file); |
|||
readone(&(stats.pgid), stat_file); |
|||
readone(&(stats.sid), stat_file); |
|||
readone(&(stats.tty_nr), stat_file); |
|||
readone(&(stats.tty_pgrp), stat_file); |
|||
readone(&(stats.flags), stat_file); |
|||
readone(&(stats.min_flt), stat_file); |
|||
readone(&(stats.cmin_flt), stat_file); |
|||
readone(&(stats.maj_flt), stat_file); |
|||
readone(&(stats.cmaj_flt), stat_file); |
|||
readone(&(stats.utime), stat_file); |
|||
readone(&(stats.stime), stat_file); |
|||
readone(&(stats.cutime), stat_file); |
|||
readone(&(stats.cstime), stat_file); |
|||
readone(&(stats.priority), stat_file); |
|||
readone(&(stats.nicev), stat_file); |
|||
readone(&(stats.num_threads), stat_file); |
|||
readone(&(stats.it_real_value), stat_file); |
|||
readunsigned(&(stats.start_time), stat_file); |
|||
readone(&(stats.vsize), stat_file); |
|||
readone(&(stats.rss), stat_file); |
|||
readone(&(stats.rsslim), stat_file); |
|||
readone(&(stats.start_code), stat_file); |
|||
readone(&(stats.end_code), stat_file); |
|||
readone(&(stats.start_stack), stat_file); |
|||
readone(&(stats.esp), stat_file); |
|||
readone(&(stats.eip), stat_file); |
|||
readone(&(stats.pending), stat_file); |
|||
readone(&(stats.blocked), stat_file); |
|||
readone(&(stats.sigign), stat_file); |
|||
readone(&(stats.sigcatch), stat_file); |
|||
readone(&(stats.wchan), stat_file); |
|||
readone(&(stats.zero1), stat_file); |
|||
readone(&(stats.zero2), stat_file); |
|||
readone(&(stats.exit_signal), stat_file); |
|||
readone(&(stats.cpu), stat_file); |
|||
readone(&(stats.rt_priority), stat_file); |
|||
readone(&(stats.policy), stat_file); |
|||
|
|||
return stats; |
|||
} |
|||
|
|||
pid_stat_fields get_pid_stats(num pid) { |
|||
|
|||
char stat_file_name[PATH_MAX]; |
|||
sprintf(stat_file_name, "/proc/%lld/stat", pid); |
|||
FILE *stat_file = fopen(stat_file_name, "r"); |
|||
if(stat_file == NULL) { |
|||
throw runtime_error("Could not open /proc/pid/stat"); |
|||
} |
|||
pid_stat_fields stat = populate_pid_stats(stat_file); |
|||
fclose(stat_file); |
|||
return stat; |
|||
} |
|||
|
|||
list<pid_stat_fields> get_pid_stats_list() { |
|||
struct dirent **namelist; |
|||
int total = scandir("/proc", &namelist, filter, alphasort); |
|||
list<pid_stat_fields> pid_stats_list; |
|||
for(int i = 0; i < total; i++) { |
|||
pid_stat_fields stat = get_pid_stats(atoi(namelist[i]->d_name)); |
|||
pid_stats_list.push_back(stat); |
|||
free(namelist[i]); |
|||
} |
|||
free(namelist); |
|||
return pid_stats_list; |
|||
} |
|||
|
|||
/**
|
|||
* @brief 找到某个进程的所子进程,同时也包含其子进程的子进程 |
|||
*/ |
|||
void get_child_processes(list<pid_stat_fields> allpids, list<pid_stat_fields> &childprocess, num pid) { |
|||
list<pid_stat_fields>::iterator it; |
|||
for(it = allpids.begin(); it != allpids.end(); it++) { |
|||
if((*it).ppid == pid && (*it).pid != pid) { |
|||
childprocess.push_back(*it); |
|||
get_child_processes(allpids, childprocess, (*it).pid); |
|||
} |
|||
} |
|||
} |
|||
/**
|
|||
* @brief Get the child processes object |
|||
* |
|||
* @param pid |
|||
* @return list<pid_stat_fields> |
|||
* |
|||
* 进程按照下面规则排序 |
|||
* |
|||
* |
|||
*/ |
|||
list<pid_stat_fields> get_child_processes(num pid) { |
|||
list<pid_stat_fields> allpids = get_pid_stats_list(); |
|||
list<pid_stat_fields> childprocess; |
|||
get_child_processes(allpids, childprocess, pid); |
|||
return childprocess; |
|||
} |
|||
|
|||
bool isprocessrunning(num pid) { |
|||
char stat_file_name[PATH_MAX]; |
|||
sprintf(stat_file_name, "/proc/%lld/stat", pid); |
|||
FILE *stat_file = fopen(stat_file_name, "r"); |
|||
if(stat_file == NULL) { |
|||
return false; |
|||
} |
|||
num _pid; |
|||
char tcomm[PATH_MAX]; |
|||
char state; |
|||
|
|||
readone(&(_pid), stat_file); |
|||
readstr(&(tcomm[0]), stat_file); |
|||
readchar(&(state), stat_file); |
|||
|
|||
fclose(stat_file); |
|||
return state != 'Z'; |
|||
} |
|||
|
|||
|
|||
void show_all_child_process(num pid) { |
|||
list<pid_stat_fields> childprocess_list = get_child_processes(pid); |
|||
list<pid_stat_fields>::iterator it; |
|||
for(it = childprocess_list.begin(); it != childprocess_list.end(); it++) { |
|||
printf("%lld %s\n", (*it).pid, (*it).tcomm); |
|||
} |
|||
} |
|||
void force_kill(num pid) { |
|||
bool firstkill = false; |
|||
int maxkilltimes = 3; |
|||
int killtimes = 0; |
|||
while(isprocessrunning(pid)) { |
|||
if(killtimes >= maxkilltimes) { |
|||
fprintf(stderr, "kill process %lld, fail %s\n", pid, strerror(errno)); |
|||
break; |
|||
} |
|||
|
|||
if(firstkill) { |
|||
fprintf(stderr, "rekill process %lld\n", pid); |
|||
} |
|||
::kill(pid, SIGTERM); |
|||
usleep(500 * 1000); |
|||
firstkill = true; |
|||
killtimes++; |
|||
} |
|||
}; |
|||
|
|||
|
|||
void killallsubprocess(int, void *) { |
|||
|
|||
list<pid_stat_fields> childprocess_list = get_child_processes(getpid()); |
|||
|
|||
// trykill all child process
|
|||
for(auto &childprocess : childprocess_list) { |
|||
if(isprocessrunning(childprocess.pid)) { |
|||
::kill(childprocess.pid, SIGTERM); |
|||
} |
|||
} |
|||
|
|||
// forse kill al child process
|
|||
for(auto &childprocess : childprocess_list) { |
|||
force_kill(childprocess.pid); |
|||
usleep(10 * 1000); |
|||
} |
|||
} |
|||
|
|||
|
|||
Process::Data::Data() noexcept : id(-1) {} |
|||
|
|||
Process::Process(const 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, const Config &config) noexcept |
|||
: closed(true), read_stdout(std::move(read_stdout)), read_stderr(std::move(read_stderr)), open_stdin(open_stdin), config(config) { |
|||
open(function); |
|||
async_read(); |
|||
} |
|||
|
|||
Process::id_type Process::open(const 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) { |
|||
prctl(PR_SET_PDEATHSIG, SIGTERM); |
|||
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]); |
|||
} |
|||
|
|||
if(!config.inherit_file_descriptors) { |
|||
// Optimization on some systems: using 8 * 1024 (Debian's default _SC_OPEN_MAX) as fd_max limit
|
|||
int fd_max = std::min(8192, static_cast<int>(sysconf(_SC_OPEN_MAX))); // Truncation is safe
|
|||
if(fd_max < 0) |
|||
fd_max = 8192; |
|||
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, const environment_type *environment) noexcept { |
|||
return open([&command, &path, &environment] { |
|||
auto command_c_str = command.c_str(); |
|||
std::string cd_path_and_command; |
|||
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; |
|||
} |
|||
cd_path_and_command = "cd '" + path_escaped + "' && " + command; // To avoid resolving symbolic links
|
|||
command_c_str = cd_path_and_command.c_str(); |
|||
} |
|||
|
|||
if(!environment) |
|||
execl("/bin/sh", "/bin/sh", "-c", command_c_str, nullptr); |
|||
else { |
|||
std::vector<std::string> env_strs; |
|||
std::vector<const char *> env_ptrs; |
|||
env_strs.reserve(environment->size()); |
|||
env_ptrs.reserve(environment->size() + 1); |
|||
for(const auto &e : *environment) { |
|||
env_strs.emplace_back(e.first + '=' + e.second); |
|||
env_ptrs.emplace_back(env_strs.back().c_str()); |
|||
} |
|||
env_ptrs.emplace_back(nullptr); |
|||
execle("/bin/sh", "/bin/sh", "-c", command_c_str, nullptr, env_ptrs.data()); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
void Process::async_read() noexcept { |
|||
if(data.id <= 0 || (!stdout_fd && !stderr_fd)) |
|||
return; |
|||
|
|||
stdout_stderr_thread = std::thread([this] { |
|||
std::vector<pollfd> pollfds; |
|||
std::bitset<2> fd_is_stdout; |
|||
if(stdout_fd) { |
|||
fd_is_stdout.set(pollfds.size()); |
|||
pollfds.emplace_back(); |
|||
pollfds.back().fd = fcntl(*stdout_fd, F_SETFL, fcntl(*stdout_fd, F_GETFL) | O_NONBLOCK) == 0 ? *stdout_fd : -1; |
|||
pollfds.back().events = POLLIN; |
|||
} |
|||
if(stderr_fd) { |
|||
pollfds.emplace_back(); |
|||
pollfds.back().fd = fcntl(*stderr_fd, F_SETFL, fcntl(*stderr_fd, F_GETFL) | O_NONBLOCK) == 0 ? *stderr_fd : -1; |
|||
pollfds.back().events = POLLIN; |
|||
} |
|||
auto buffer = std::unique_ptr<char[]>(new char[config.buffer_size]); |
|||
bool any_open = !pollfds.empty(); |
|||
while(any_open && (poll(pollfds.data(), static_cast<nfds_t>(pollfds.size()), -1) > 0 || errno == EINTR)) { |
|||
any_open = false; |
|||
for(size_t i = 0; i < pollfds.size(); ++i) { |
|||
if(pollfds[i].fd >= 0) { |
|||
if(pollfds[i].revents & POLLIN) { |
|||
const ssize_t n = read(pollfds[i].fd, buffer.get(), config.buffer_size); |
|||
if(n > 0) { |
|||
if(fd_is_stdout[i]) |
|||
read_stdout(buffer.get(), static_cast<size_t>(n)); |
|||
else |
|||
read_stderr(buffer.get(), static_cast<size_t>(n)); |
|||
} |
|||
else if(n < 0 && errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) { |
|||
pollfds[i].fd = -1; |
|||
continue; |
|||
} |
|||
} |
|||
if(pollfds[i].revents & (POLLERR | POLLHUP | POLLNVAL)) { |
|||
pollfds[i].fd = -1; |
|||
continue; |
|||
} |
|||
any_open = true; |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
|
|||
int Process::get_exit_status() noexcept { |
|||
if(data.id <= 0) |
|||
return -1; |
|||
|
|||
int exit_status; |
|||
id_type pid; |
|||
do { |
|||
pid = waitpid(data.id, &exit_status, 0); |
|||
// printf("waitpid(%d, %d, 0) = %d\n", data.id, exit_status, pid);
|
|||
} while(pid < 0 && errno == EINTR); |
|||
|
|||
if(pid < 0 && errno == ECHILD) { |
|||
// PID doesn't exist anymore, return previously sampled exit status (or -1)
|
|||
return data.exit_status; |
|||
} |
|||
else { |
|||
// Store exit status for future calls
|
|||
if(exit_status >= 256) |
|||
exit_status = exit_status >> 8; |
|||
data.exit_status = exit_status; |
|||
} |
|||
|
|||
{ |
|||
std::lock_guard<std::mutex> lock(close_mutex); |
|||
closed = true; |
|||
} |
|||
close_fds(); |
|||
|
|||
return exit_status; |
|||
} |
|||
|
|||
bool Process::try_get_exit_status(int &exit_status) noexcept { |
|||
if(data.id <= 0) |
|||
return false; |
|||
|
|||
const id_type pid = waitpid(data.id, &exit_status, WNOHANG); |
|||
if(pid < 0 && errno == ECHILD) { |
|||
// PID doesn't exist anymore, set previously sampled exit status (or -1)
|
|||
exit_status = data.exit_status; |
|||
return true; |
|||
} |
|||
else if(pid <= 0) { |
|||
// Process still running (p==0) or error
|
|||
return false; |
|||
} |
|||
else { |
|||
// store exit status for future calls
|
|||
if(exit_status >= 256) |
|||
exit_status = exit_status >> 8; |
|||
data.exit_status = exit_status; |
|||
} |
|||
|
|||
{ |
|||
std::lock_guard<std::mutex> lock(close_mutex); |
|||
closed = true; |
|||
} |
|||
close_fds(); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
void Process::close_fds() noexcept { |
|||
if(stdout_stderr_thread.joinable()) |
|||
stdout_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); |
|||
int ret = 0; |
|||
if(data.id > 0 && !closed) { |
|||
// show_all_child_process();
|
|||
bool firstkill = false; |
|||
if(force) { |
|||
// 按照从最底层的进程开始杀死
|
|||
list<pid_stat_fields> childprocess_list = get_child_processes(data.id); |
|||
force_kill(data.id); |
|||
usleep(10 * 1000); |
|||
for(auto &childprocess : childprocess_list) { |
|||
force_kill(childprocess.pid); |
|||
usleep(10 * 1000); |
|||
} |
|||
} |
|||
else { |
|||
kill(-data.id, SIGTERM); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void Process::kill(id_type id, bool force) noexcept { |
|||
if(id <= 0) |
|||
return; |
|||
|
|||
if(force) |
|||
::kill(-id, SIGTERM); |
|||
else |
|||
::kill(-id, SIGINT); |
|||
} |
|||
|
|||
void Process::signal(int signum) noexcept { |
|||
std::lock_guard<std::mutex> lock(close_mutex); |
|||
if(data.id > 0 && !closed) { |
|||
::kill(-data.id, signum); |
|||
} |
|||
} |
|||
|
|||
} // namespace process
|
|||
} // namespace iflytop
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue