Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/include/service.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#pragma once
#include <string>
#include "program_args.hpp"

namespace service_cmd {
int handle_service_command(const std::string& subcommand, const program_args_t& args);
}
3 changes: 2 additions & 1 deletion src/include/utils/vm_args.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ inline void print_help(po::options_description& general) {
std::cout << " help - Show this help message" << std::endl;
std::cout << " port - Show the default server port" << std::endl;
std::cout << " validate - Validate the NPU stack" << std::endl;
std::cout << " service <action> - Manage systemd service (install|uninstall|start|stop|status)" << std::endl;
std::cout << std::endl;
std::cout << general << std::endl;
std::cout << "Examples:" << std::endl;
Expand Down Expand Up @@ -168,7 +169,7 @@ bool parse_options(int argc, char *argv[], program_args_t& parsed_args) {
return false;
}

if (parsed_args.command != "serve")
if (parsed_args.command != "serve" && parsed_args.command != "service")
{
if (!vm["socket"].defaulted())
{
Expand Down
8 changes: 8 additions & 0 deletions src/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "utils/vm_args.hpp"
#include <boost/program_options.hpp>
#include "benchmarking.hpp"
#include "service.hpp"

#ifndef _WIN32
#include <fcntl.h>
Expand Down Expand Up @@ -705,6 +706,13 @@ int main(int argc, char* argv[]) {
}
}
}
else if (parsed_args.command == "service") {
if (parsed_args.model_tag.empty() || parsed_args.model_tag == "model-faker") {
std::cerr << "Usage: flm service <install|uninstall|start|stop|status>" << std::endl;
return 1;
}
return service_cmd::handle_service_command(parsed_args.model_tag, parsed_args);
}
else {
// Invalid command, this will be used to show the invalid command
std::cerr << "Invalid command: " << parsed_args.command << std::endl;
Expand Down
210 changes: 210 additions & 0 deletions src/src/service.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
#include "service.hpp"
#include "utils/utils.hpp"
#include <iostream>
#include <fstream>
#include <sstream>
#include <cstdlib>
#include <filesystem>
#include <pwd.h>
#include <unistd.h>
#include <sys/stat.h>

namespace service_cmd {

static std::string resolve_model_path() {
const char* sudo_user = std::getenv("SUDO_USER");
if (sudo_user && strlen(sudo_user) > 0) {
struct passwd* pw = getpwnam(sudo_user);
if (pw && pw->pw_dir) {
return std::string(pw->pw_dir) + "/.config/flm";
}
}
return utils::get_models_directory();
}

static std::string resolve_flm_binary() {
return utils::get_executable_directory() + "/flm";
}

static bool run_command(const std::string& cmd, bool report_errors = true) {
int ret = system(cmd.c_str());
if (ret != 0 && report_errors) {
std::cerr << "Command failed: " << cmd << std::endl;
}
return ret == 0;
}

static bool is_root() {
return geteuid() == 0;
}

static bool has_systemd() {
return std::filesystem::exists("/run/systemd/system");
}

static bool file_write(const std::string& path, const std::string& content) {
std::ofstream f(path);
if (!f.is_open()) {
std::cerr << "Error: Cannot write to " << path << std::endl;
return false;
}
f << content;
f.close();
return true;
}

static int do_install(const program_args_t& args) {
if (!is_root()) {
std::cerr << "Error: flm service install must be run as root (use sudo)" << std::endl;
return 1;
}
if (!has_systemd()) {
std::cerr << "Error: systemd not detected on this system" << std::endl;
return 1;
}
if (std::filesystem::exists("/etc/systemd/system/flm.service")) {
std::cerr << "Error: flm service is already installed. Run 'flm service uninstall' first." << std::endl;
return 1;
}

// Create flm system user if it doesn't exist
if (getpwnam("flm") == nullptr) {
if (!run_command("useradd --system --no-create-home --shell /usr/sbin/nologin flm")) {
std::cerr << "Error: Failed to create flm system user" << std::endl;
return 1;
}
std::cout << "Created system user 'flm'" << std::endl;
}

// Add flm user to render group for NPU access
run_command("usermod -a -G render flm", false);

// Resolve paths
std::string model_path = resolve_model_path();
std::string flm_binary = resolve_flm_binary();
int port = utils::get_server_port(args.port);
std::string host = args.host;

// Create /etc/flm directory
std::filesystem::create_directories("/etc/flm");

// Write config file
std::ostringstream conf;
conf << "# FastFlowLM service configuration\n";
conf << "FLM_HOST=" << host << "\n";
conf << "FLM_PORT=" << port << "\n";
conf << "FLM_MODEL_PATH=" << model_path << "\n";
if (!file_write("/etc/flm/service.conf", conf.str())) return 1;

// Write wrapper script
std::ostringstream wrapper;
wrapper << "#!/bin/bash\n";
wrapper << "source /etc/flm/service.conf\n";
wrapper << "export FLM_MODEL_PATH\n";
wrapper << "exec " << flm_binary << " serve --quiet \\\n";
wrapper << " --host \"${FLM_HOST}\" \\\n";
wrapper << " --port \"${FLM_PORT}\"\n";
if (!file_write("/etc/flm/flm-service-wrapper", wrapper.str())) return 1;
chmod("/etc/flm/flm-service-wrapper", 0755);

// Write systemd unit
std::ostringstream unit;
unit << "[Unit]\n";
unit << "Description=FastFlowLM Inference Server\n";
unit << "After=network.target\n";
unit << "\n";
unit << "[Service]\n";
unit << "Type=simple\n";
unit << "User=flm\n";
unit << "Group=render\n";
unit << "ExecStart=/etc/flm/flm-service-wrapper\n";
unit << "Restart=on-failure\n";
unit << "RestartSec=5\n";
unit << "LimitMEMLOCK=infinity\n";
unit << "NoNewPrivileges=true\n";
unit << "ProtectSystem=strict\n";
unit << "ProtectHome=read-only\n";
unit << "PrivateTmp=true\n";
unit << "StandardOutput=journal\n";
unit << "StandardError=journal\n";
unit << "SyslogIdentifier=flm\n";
unit << "\n";
unit << "[Install]\n";
unit << "WantedBy=multi-user.target\n";
if (!file_write("/etc/systemd/system/flm.service", unit.str())) return 1;

// Reload and enable
run_command("systemctl daemon-reload");
run_command("systemctl enable flm.service");

std::cout << "FastFlowLM service installed successfully." << std::endl;
std::cout << std::endl;
std::cout << "Configuration:" << std::endl;
std::cout << " Host: " << host << std::endl;
std::cout << " Port: " << port << std::endl;
std::cout << " Model path: " << model_path << std::endl;
std::cout << " Binary: " << flm_binary << std::endl;
std::cout << std::endl;
std::cout << "Next steps:" << std::endl;
std::cout << " sudo flm service start - Start the service" << std::endl;
std::cout << " flm service status - Check service status" << std::endl;
std::cout << " journalctl -u flm -f - View logs" << std::endl;

return 0;
}

static int do_uninstall() {
if (!is_root()) {
std::cerr << "Error: flm service uninstall must be run as root (use sudo)" << std::endl;
return 1;
}

run_command("systemctl stop flm.service", false);
run_command("systemctl disable flm.service", false);

std::filesystem::remove("/etc/systemd/system/flm.service");
std::filesystem::remove("/etc/flm/flm-service-wrapper");
std::filesystem::remove("/etc/flm/service.conf");
std::filesystem::remove("/etc/flm");

run_command("systemctl daemon-reload");

std::cout << "FastFlowLM service uninstalled." << std::endl;
std::cout << "Note: The 'flm' system user was not removed (may own files)." << std::endl;
return 0;
}

static int do_start() {
if (!is_root()) {
std::cerr << "Error: flm service start must be run as root (use sudo)" << std::endl;
return 1;
}
return run_command("systemctl start flm.service") ? 0 : 1;
}

static int do_stop() {
if (!is_root()) {
std::cerr << "Error: flm service stop must be run as root (use sudo)" << std::endl;
return 1;
}
return run_command("systemctl stop flm.service") ? 0 : 1;
}

static int do_status() {
// status doesn't require root
return run_command("systemctl status flm.service") ? 0 : 1;
}

int handle_service_command(const std::string& subcommand, const program_args_t& args) {
if (subcommand == "install") return do_install(args);
if (subcommand == "uninstall") return do_uninstall();
if (subcommand == "start") return do_start();
if (subcommand == "stop") return do_stop();
if (subcommand == "status") return do_status();

std::cerr << "Unknown service action: " << subcommand << std::endl;
std::cerr << "Usage: flm service <install|uninstall|start|stop|status>" << std::endl;
return 1;
}

} // namespace service_cmd