diff --git a/common/arg.cpp b/common/arg.cpp index 231de227a..ced0752f3 100644 --- a/common/arg.cpp +++ b/common/arg.cpp @@ -3373,5 +3373,9 @@ common_params_context common_params_parser_init(common_params & params, llama_ex } ).set_examples({LLAMA_EXAMPLE_SERVER})); + add_opt(common_arg({ "--pidfile" }, "FILE", "path to PID file for server process", + [](common_params & params, const std::string & value) { params.pidfile = value; }) + .set_examples({ LLAMA_EXAMPLE_SERVER })); + return ctx_arg; } diff --git a/common/common.h b/common/common.h index 00b6ca03a..a41af4b70 100644 --- a/common/common.h +++ b/common/common.h @@ -366,6 +366,7 @@ struct common_params { std::string hostname = "127.0.0.1"; std::string public_path = ""; // NOLINT std::string chat_template = ""; // NOLINT + std::string pidfile = ""; // path to PID file for server process // NOLINT bool use_jinja = false; // NOLINT bool enable_chat_template = true; common_reasoning_format reasoning_format = COMMON_REASONING_FORMAT_DEEPSEEK; diff --git a/tools/server/server.cpp b/tools/server/server.cpp index 721d09182..8bb0902c1 100644 --- a/tools/server/server.cpp +++ b/tools/server/server.cpp @@ -14,22 +14,31 @@ // mime type for sending response #define MIMETYPE_JSON "application/json; charset=utf-8" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + // auto generated files (see README.md for details) #include "index.html.gz.hpp" #include "loading.html.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#ifdef _WIN32 +#include +#define getpid _getpid +#define pid_t int; +#else +#include +#endif using json = nlohmann::ordered_json; @@ -3691,6 +3700,77 @@ inline void signal_handler(int signal) { shutdown_handler(signal); } +static bool check_pid_alive(const pid_t pid) { + if (pid <= 0) { + return false; + } + + // Process is alive or exists but is inaccessible + if (kill(pid, 0) == 0 || errno == EPERM) { + return true; // Process is alive + } + + return false; // Process does not exist or other error +} + +class PidFile { + public: + FILE * file = nullptr; + std::string fname; + bool rm = false; + + FILE * open(const std::string & filename, const char * mode, const bool r = false) { + file = ggml_fopen(filename.c_str(), mode); + fname = filename; + rm = r; + + return file; + } + + void close() { + fclose(file); + file = nullptr; + + if (rm) { + // Remove stale pidfile + unlink(fname.c_str()); + } + } + + ~PidFile() { + if (file) { + close(); + } + } +}; + +static bool is_old_pid_alive(const std::string & filename) { + pid_t oldpid = 0; + PidFile f; + if (f.open(filename, "r")) { + if (fscanf(f.file, "%d", &oldpid) == 1) { + if (check_pid_alive(oldpid)) { + LOG_ERR("Process already running with PID %d\n", oldpid); + return true; + } + } + } + + return false; +} + +static int create_pidfile(const std::string & pidfile, PidFile & f) { + if (!f.open(pidfile.c_str(), "w", true)) { + LOG_ERR("Unable to open pidfile %s: %s\n", pidfile.c_str(), strerror(errno)); + return -1; + } + + fprintf(f.file, "%d\n", getpid()); + fflush(f.file); + + return 0; +} + int main(int argc, char ** argv) { // own arguments required by this example common_params params; @@ -3699,6 +3779,13 @@ int main(int argc, char ** argv) { return 1; } + PidFile f; + if (!params.pidfile.empty()) { + if (is_old_pid_alive(params.pidfile) || create_pidfile(params.pidfile, f)) { + return 1; + } + } + common_init(); // struct that contains llama context and inference