diff --git a/build.rs b/build.rs index c63a52e..8755cb7 100644 --- a/build.rs +++ b/build.rs @@ -1,47 +1,48 @@ use chrono::Datelike; use std::{env, process::Command}; fn main() { #[cfg(windows)] { println!("cargo:warning=This project is not supported on Windows."); std::process::exit(1); } /* version attributes */ let date = chrono::Utc::now(); let profile = env::var("PROFILE").unwrap(); let output = Command::new("git").args(&["rev-parse", "--short=10", "HEAD"]).output().unwrap(); let output_full = Command::new("git").args(&["rev-parse", "HEAD"]).output().unwrap(); println!("cargo:rustc-env=TARGET={}", env::var("TARGET").unwrap()); println!("cargo:rustc-env=GIT_HASH={}", String::from_utf8(output.stdout).unwrap()); println!("cargo:rustc-env=GIT_HASH_FULL={}", String::from_utf8(output_full.stdout).unwrap()); println!("cargo:rustc-env=BUILD_DATE={}-{}-{}", date.year(), date.month(), date.day()); /* profile matching */ match profile.as_str() { "debug" => println!("cargo:rustc-env=PROFILE=debug"), "release" => println!("cargo:rustc-env=PROFILE=release"), _ => println!("cargo:rustc-env=PROFILE=none"), } /* cc linking */ cxx_build::bridge("src/lib.rs") .file("lib/bridge.cc") .file("lib/process.cc") .file("lib/fork.cc") + .include("lib/include") .flag_if_supported("-std=c++17") .compile("bridge"); let watched = vec![ "src/lib.rs", "lib/bridge.cc", "lib/process.cc", "lib/fork.cc", "lib/include/bridge.h", "lib/include/process.h", "lib/include/fork.h", ]; watched.iter().for_each(|file| println!("cargo:rerun-if-changed={file}")); } diff --git a/lib/bridge.cc b/lib/bridge.cc index fe7c9e2..b37a15a 100644 --- a/lib/bridge.cc +++ b/lib/bridge.cc @@ -1,103 +1,102 @@ -#include "include/bridge.h" -#include "include/process.h" - +#include <bridge.h> +#include <process.h> #include <iostream> #include <signal.h> #include <stdio.h> -#include <string> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <vector> #ifdef __linux__ #include <cstring> +#include <string> #include <cstdlib> #include <dirent.h> #elif __APPLE__ #include <libproc.h> #include <sys/proc_info.h> #endif using namespace std; int64_t get_child_pid(int64_t parentPID) { #ifdef __linux__ DIR *dir = opendir("/proc"); if (!dir) { std::cerr << "[PMC] (cc) Error opening /proc directory.\n"; return -1; } int targetPID = -1; dirent *entry; while ((entry = readdir(dir)) != nullptr) { if (entry->d_type == DT_DIR && isdigit(entry->d_name[0])) { int pid = atoi(entry->d_name); char statusPath[256]; snprintf(statusPath, sizeof(statusPath), "/proc/%d/status", pid); FILE *statusFile = fopen(statusPath, "r"); if (statusFile) { char buffer[256]; while (fgets(buffer, sizeof(buffer), statusFile) != nullptr) { if (strncmp(buffer, "PPid:", 5) == 0) { int parentID; if (sscanf(buffer + 5, "%d", &parentID) == 1 && parentID == parentPID) { targetPID = pid; break; } break; } } fclose(statusFile); } } } closedir(dir); return targetPID; #elif __APPLE__ pid_t pidList[1024]; int count = proc_listpids(PROC_ALL_PIDS, 0, pidList, sizeof(pidList)); if (count <= 0) { std::cerr << "Error retrieving process list." << std::endl; return -1; } for (int i = 0; i < count; ++i) { struct proc_bsdinfo procInfo; if (proc_pidinfo(pidList[i], PROC_PIDTBSDINFO, 0, &procInfo, sizeof(procInfo)) > 0) { if (procInfo.pbi_ppid == parentPID) { return static_cast<int>(pidList[i]); } } } return -1; #else return -1; #endif } int64_t stop(int64_t pid) { vector<pid_t> children; int64_t child; while ((child = get_child_pid(pid)) != -1) { children.push_back(child); pid = child; } for (size_t i = 0; i < children.size(); i++) { kill(children[i], SIGTERM); } return kill(pid, SIGTERM); } int64_t run(ProcessMetadata metadata) { process::Runner runner; runner.New(std::string(metadata.name), std::string(metadata.log_path)); return runner.Run(std::string(metadata.command), std::string(metadata.shell), metadata.args); } \ No newline at end of file diff --git a/lib/fork.cc b/lib/fork.cc index 75abd5d..8d56259 100644 --- a/lib/fork.cc +++ b/lib/fork.cc @@ -1,82 +1,85 @@ -#include "include/fork.h" -#include <cstring> +#include <fork.h> #include <stdexcept> #include <cstdlib> #include <iostream> #include <unistd.h> #ifdef _WIN32 #include <windows.h> +#elif __linux__ +#include <cstring> #else #include <pwd.h> #include <unistd.h> #endif +using namespace std; + std::string home() { #ifdef _WIN32 const char* userProfile = std::getenv("USERPROFILE"); if (userProfile) { return std::string(userProfile); } else { return ""; } #else struct passwd* pw = getpwuid(getuid()); if (pw && pw->pw_dir) { return std::string(pw->pw_dir); } else { return ""; } #endif } Fork fork_process() { pid_t res = ::fork(); if (res == -1) { throw std::runtime_error("fork() failed"); } else if (res == 0) { return Fork::Child; } else { return Fork::Parent; } } pid_t set_sid() { pid_t res = ::setsid(); if (res == -1) { throw std::runtime_error("setsid() failed"); } return res; } void close_fd() { if (::close(0) == -1 || ::close(1) == -1 || ::close(2) == -1) { throw std::runtime_error("close() failed"); } } int32_t try_fork(bool nochdir, bool noclose, Callback callback) { try { Fork forkResult = fork_process(); if (forkResult == Fork::Parent) { exit(0); } else if (forkResult == Fork::Child) { set_sid(); if (!nochdir) { std::string home_dir = home() + ".pmc"; chdir(home_dir.c_str()); } if (!noclose) { close_fd(); } forkResult = fork_process(); } return static_cast<int32_t>(forkResult); } catch (const std::exception& e) { std::cerr << "[PMC] (cc) Error setting up daemon handler\n"; } callback(); return -1; } \ No newline at end of file diff --git a/lib/include/bridge.h b/lib/include/bridge.h index 7041631..aac5160 100644 --- a/lib/include/bridge.h +++ b/lib/include/bridge.h @@ -1,21 +1,21 @@ #ifndef BRIDGE_H #define BRIDGE_H -#include "rust.h" +#include <rust.h> using namespace rust; #ifndef CXXBRIDGE1_STRUCT_ProcessMetadata #define CXXBRIDGE1_STRUCT_ProcessMetadata struct ProcessMetadata final { String name; String shell; String command; String log_path; Vec<String> args; using IsRelocatable = std::true_type; }; #endif extern "C" int64_t stop(int64_t pid); extern "C" int64_t run(ProcessMetadata metadata); #endif diff --git a/lib/include/process.h b/lib/include/process.h index 3947a37..9a3ef5c 100644 --- a/lib/include/process.h +++ b/lib/include/process.h @@ -1,20 +1,20 @@ #ifndef PROCESS_H #define PROCESS_H -#include "rust.h" +#include <rust.h> using namespace rust; namespace process { class Runner { public: void New(const std::string &name, const std::string &logPath); int64_t Run(const std::string &command, const std::string &shell, Vec<String> args); ~Runner(); private: int stdout_fd; int stderr_fd; }; } #endif diff --git a/lib/process.cc b/lib/process.cc index 06f0e62..5052c9f 100644 --- a/lib/process.cc +++ b/lib/process.cc @@ -1,93 +1,94 @@ -#include "include/process.h" +#include <process.h> #include <fcntl.h> #include <unistd.h> #include <sys/wait.h> #include <signal.h> #include <iostream> + using namespace std; namespace process { volatile sig_atomic_t childExitStatus = 0; pair<std::string, std::string> split(const std::string& str) { size_t length = str.length(); size_t midpoint = length / 2; std::string firstHalf = str.substr(0, midpoint); std::string secondHalf = str.substr(midpoint); return make_pair(firstHalf, secondHalf); } void sigchld_handler(int signo) { (void)signo; int status; while (waitpid(-1, &status, WNOHANG) > 0) { childExitStatus = status; } } void Runner::New(const std::string &name, const std::string &logPath) { std::string stdoutFileName = logPath + "/" + name + "-out.log"; std::string stderrFileName = logPath + "/" + name + "-error.log"; stdout_fd = open(stdoutFileName.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0644); stderr_fd = open(stderrFileName.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0644); struct sigaction sa; sa.sa_handler = sigchld_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; if (sigaction(SIGCHLD, &sa, NULL) == -1) { std::cerr << "[PMC] (cc) Error setting up SIGCHLD handler\n"; } } Runner::~Runner() { if (stdout_fd != -1) { close(stdout_fd); } if (stderr_fd != -1) { close(stderr_fd); } } int64_t Runner::Run(const std::string &command, const std::string &shell, Vec<String> args) { pid_t pid = fork(); if (pid == -1) { std::cerr << "[PMC] (cc) Unable to fork\n"; return -1; } else if (pid == 0) { setsid(); close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); dup2(stdout_fd, STDOUT_FILENO); dup2(stderr_fd, STDERR_FILENO); std::vector<const char*> argsArray; argsArray.push_back(shell.c_str()); transform(args.begin(), args.end(), std::back_inserter(argsArray), [](rust::String& arg) { return arg.c_str(); }); argsArray.push_back(command.c_str()); argsArray.push_back((char *)nullptr); if (execvp(shell.c_str(), const_cast<char* const*>(argsArray.data())) == -1) { std::cerr << "[PMC] (cc) Unable to execute the command\n"; exit(EXIT_FAILURE); } } else { close(stdout_fd); close(stderr_fd); return pid; } return -1; }} diff --git a/src/daemon/log.rs b/src/daemon/log.rs index 557158d..8429643 100644 --- a/src/daemon/log.rs +++ b/src/daemon/log.rs @@ -1,21 +1,23 @@ use chrono::Local; use global_placeholders::global; use std::fs::{File, OpenOptions}; use std::io::{self, Write}; pub struct Logger { file: File, } impl Logger { pub fn new() -> io::Result<Self> { let file = OpenOptions::new().create(true).append(true).open(global!("pmc.daemon.log"))?; Ok(Logger { file }) } - pub fn write(&mut self, message: &str) { writeln!(&mut self.file, "[{}] {}", Local::now().format("%Y-%m-%d %H:%M:%S%.3f"), message).unwrap() } + pub fn write(&mut self, message: &str) { + writeln!(&mut self.file, "[{}] {}", Local::now().format("%Y-%m-%d %H:%M:%S%.3f"), message).unwrap() + } } #[macro_export] macro_rules! log { ($message:expr $(, $arg:expr)*) => { crate::daemon::log::Logger::new().unwrap().write(format!($message $(, $arg)*).as_str()) }}