#ifndef _WIN32 #include "platform.hpp" #include #include #include #include #include #include #include #include #include #include namespace connector { namespace { constexpr unsigned short kConnectorPort = 27843; std::filesystem::path lsof_path() { for (const char* candidate : {"/usr/sbin/lsof", "/usr/bin/lsof"}) { if (std::filesystem::exists(candidate)) return candidate; } return {}; } std::set connector_port_pids() { const std::filesystem::path executable = lsof_path(); if (executable.empty()) return {}; const ProcessResult result = run_process(executable.string(), {"-nP", "-tiTCP:" + std::to_string(kConnectorPort), "-sTCP:LISTEN"}, {}, 3); if (result.timed_out || (result.exit_code != 0 && result.exit_code != 1)) return {}; std::set pids; std::istringstream lines(result.output); std::string line; while (std::getline(lines, line)) { try { const long value = std::stol(line); if (value > 0) pids.insert(static_cast(value)); } catch (...) { // lsof 可能输出诊断信息;只接受纯数字 PID。 } } return pids; } } // namespace ProcessResult run_process(const std::string& executable, const std::vector& arguments, const std::filesystem::path& working_directory, int timeout_seconds) { int output_pipe[2]; if (pipe(output_pipe) != 0) throw std::runtime_error("无法创建 Spine 输出管道"); const pid_t child = fork(); if (child < 0) { close(output_pipe[0]); close(output_pipe[1]); throw std::runtime_error("无法启动 Spine 进程"); } if (child == 0) { close(output_pipe[0]); dup2(output_pipe[1], STDOUT_FILENO); dup2(output_pipe[1], STDERR_FILENO); close(output_pipe[1]); if (!working_directory.empty()) chdir(working_directory.c_str()); std::vector argv; argv.push_back(const_cast(executable.c_str())); for (const std::string& argument : arguments) argv.push_back(const_cast(argument.c_str())); argv.push_back(nullptr); execv(executable.c_str(), argv.data()); _exit(127); } close(output_pipe[1]); fcntl(output_pipe[0], F_SETFL, fcntl(output_pipe[0], F_GETFL) | O_NONBLOCK); ProcessResult result; int status = 0; const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(timeout_seconds); while (true) { char buffer[8192]; const ssize_t count = read(output_pipe[0], buffer, sizeof(buffer)); if (count > 0 && result.output.size() < 4 * 1024 * 1024) result.output.append(buffer, static_cast(count)); const pid_t state = waitpid(child, &status, WNOHANG); if (state == child) break; if (std::chrono::steady_clock::now() >= deadline) { result.timed_out = true; kill(child, SIGKILL); waitpid(child, &status, 0); break; } std::this_thread::sleep_for(std::chrono::milliseconds(20)); } while (true) { char buffer[8192]; const ssize_t count = read(output_pipe[0], buffer, sizeof(buffer)); if (count <= 0) break; if (result.output.size() < 4 * 1024 * 1024) result.output.append(buffer, static_cast(count)); } close(output_pipe[0]); if (WIFEXITED(status)) result.exit_code = WEXITSTATUS(status); else if (WIFSIGNALED(status)) result.exit_code = 128 + WTERMSIG(status); return result; } bool connector_port_in_use() { return !connector_port_pids().empty(); } bool stop_connector_port(std::string& error) { const std::set pids = connector_port_pids(); if (pids.empty()) { error.clear(); return true; } for (const pid_t pid : pids) { if (pid == getpid()) { error = "无法从外部停止当前连接器进程"; return false; } if (kill(pid, SIGTERM) != 0) { error = "无法停止占用端口 27843 的进程,请检查系统权限"; return false; } } const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(3); while (std::chrono::steady_clock::now() < deadline) { if (!connector_port_in_use()) { error.clear(); return true; } std::this_thread::sleep_for(std::chrono::milliseconds(50)); } for (const pid_t pid : pids) { if (kill(pid, 0) == 0) kill(pid, SIGKILL); } std::this_thread::sleep_for(std::chrono::milliseconds(100)); if (connector_port_in_use()) { error = "已请求停止进程,但端口 27843 仍未释放"; return false; } error.clear(); return true; } } // namespace connector #endif