mirror of https://github.com/telemt/telemt.git
377 lines
10 KiB
Rust
377 lines
10 KiB
Rust
//! Service manager integration for telemt.
|
|
//!
|
|
//! Supports generating service files for:
|
|
//! - systemd (Linux)
|
|
//! - OpenRC (Alpine, Gentoo)
|
|
//! - rc.d (FreeBSD)
|
|
|
|
use std::path::Path;
|
|
|
|
/// Detected init/service system.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum InitSystem {
|
|
/// systemd (most modern Linux distributions)
|
|
Systemd,
|
|
/// OpenRC (Alpine, Gentoo, some BSDs)
|
|
OpenRC,
|
|
/// FreeBSD rc.d
|
|
FreeBSDRc,
|
|
/// No known init system detected
|
|
Unknown,
|
|
}
|
|
|
|
impl std::fmt::Display for InitSystem {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
InitSystem::Systemd => write!(f, "systemd"),
|
|
InitSystem::OpenRC => write!(f, "OpenRC"),
|
|
InitSystem::FreeBSDRc => write!(f, "FreeBSD rc.d"),
|
|
InitSystem::Unknown => write!(f, "unknown"),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Detects the init system in use on the current host.
|
|
pub fn detect_init_system() -> InitSystem {
|
|
// Check for systemd first (most common on Linux)
|
|
if Path::new("/run/systemd/system").exists() {
|
|
return InitSystem::Systemd;
|
|
}
|
|
|
|
// Check for OpenRC
|
|
if Path::new("/sbin/openrc-run").exists() || Path::new("/sbin/openrc").exists() {
|
|
return InitSystem::OpenRC;
|
|
}
|
|
|
|
// Check for FreeBSD rc.d
|
|
if Path::new("/etc/rc.subr").exists() && Path::new("/etc/rc.d").exists() {
|
|
return InitSystem::FreeBSDRc;
|
|
}
|
|
|
|
// Fallback: check if systemctl exists even without /run/systemd
|
|
if Path::new("/usr/bin/systemctl").exists() || Path::new("/bin/systemctl").exists() {
|
|
return InitSystem::Systemd;
|
|
}
|
|
|
|
InitSystem::Unknown
|
|
}
|
|
|
|
/// Returns the default service file path for the given init system.
|
|
pub fn service_file_path(init_system: InitSystem) -> &'static str {
|
|
match init_system {
|
|
InitSystem::Systemd => "/etc/systemd/system/telemt.service",
|
|
InitSystem::OpenRC => "/etc/init.d/telemt",
|
|
InitSystem::FreeBSDRc => "/usr/local/etc/rc.d/telemt",
|
|
InitSystem::Unknown => "/etc/init.d/telemt",
|
|
}
|
|
}
|
|
|
|
/// Options for generating service files.
|
|
pub struct ServiceOptions<'a> {
|
|
/// Path to the telemt executable
|
|
pub exe_path: &'a Path,
|
|
/// Path to the configuration file
|
|
pub config_path: &'a Path,
|
|
/// User to run as (optional)
|
|
pub user: Option<&'a str>,
|
|
/// Group to run as (optional)
|
|
pub group: Option<&'a str>,
|
|
/// PID file path
|
|
pub pid_file: &'a str,
|
|
/// Working directory
|
|
pub working_dir: Option<&'a str>,
|
|
/// Description
|
|
pub description: &'a str,
|
|
}
|
|
|
|
impl<'a> Default for ServiceOptions<'a> {
|
|
fn default() -> Self {
|
|
Self {
|
|
exe_path: Path::new("/usr/local/bin/telemt"),
|
|
config_path: Path::new("/etc/telemt/config.toml"),
|
|
user: Some("telemt"),
|
|
group: Some("telemt"),
|
|
pid_file: "/var/run/telemt.pid",
|
|
working_dir: Some("/var/lib/telemt"),
|
|
description: "Telemt MTProxy - Telegram MTProto Proxy",
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Generates a service file for the given init system.
|
|
pub fn generate_service_file(init_system: InitSystem, opts: &ServiceOptions) -> String {
|
|
match init_system {
|
|
InitSystem::Systemd => generate_systemd_unit(opts),
|
|
InitSystem::OpenRC => generate_openrc_script(opts),
|
|
InitSystem::FreeBSDRc => generate_freebsd_rc_script(opts),
|
|
InitSystem::Unknown => generate_systemd_unit(opts), // Default to systemd format
|
|
}
|
|
}
|
|
|
|
/// Generates an enhanced systemd unit file.
|
|
fn generate_systemd_unit(opts: &ServiceOptions) -> String {
|
|
let user_line = opts.user.map(|u| format!("User={}", u)).unwrap_or_default();
|
|
let group_line = opts.group.map(|g| format!("Group={}", g)).unwrap_or_default();
|
|
let working_dir = opts.working_dir.map(|d| format!("WorkingDirectory={}", d)).unwrap_or_default();
|
|
|
|
format!(
|
|
r#"[Unit]
|
|
Description={description}
|
|
Documentation=https://github.com/telemt/telemt
|
|
After=network-online.target
|
|
Wants=network-online.target
|
|
|
|
[Service]
|
|
Type=simple
|
|
ExecStart={exe} --foreground --pid-file {pid_file} {config}
|
|
ExecReload=/bin/kill -HUP $MAINPID
|
|
PIDFile={pid_file}
|
|
Restart=always
|
|
RestartSec=5
|
|
{user}
|
|
{group}
|
|
{working_dir}
|
|
|
|
# Resource limits
|
|
LimitNOFILE=65535
|
|
LimitNPROC=4096
|
|
|
|
# Security hardening
|
|
NoNewPrivileges=true
|
|
ProtectSystem=strict
|
|
ProtectHome=true
|
|
PrivateTmp=true
|
|
PrivateDevices=true
|
|
ProtectKernelTunables=true
|
|
ProtectKernelModules=true
|
|
ProtectControlGroups=true
|
|
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
|
|
RestrictNamespaces=true
|
|
RestrictRealtime=true
|
|
RestrictSUIDSGID=true
|
|
MemoryDenyWriteExecute=true
|
|
LockPersonality=true
|
|
|
|
# Allow binding to privileged ports and writing to specific paths
|
|
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
|
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
|
ReadWritePaths=/etc/telemt /var/run /var/lib/telemt
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
"#,
|
|
description = opts.description,
|
|
exe = opts.exe_path.display(),
|
|
config = opts.config_path.display(),
|
|
pid_file = opts.pid_file,
|
|
user = user_line,
|
|
group = group_line,
|
|
working_dir = working_dir,
|
|
)
|
|
}
|
|
|
|
/// Generates an OpenRC init script.
|
|
fn generate_openrc_script(opts: &ServiceOptions) -> String {
|
|
let user = opts.user.unwrap_or("root");
|
|
let group = opts.group.unwrap_or("root");
|
|
|
|
format!(
|
|
r#"#!/sbin/openrc-run
|
|
# OpenRC init script for telemt
|
|
|
|
description="{description}"
|
|
command="{exe}"
|
|
command_args="--daemon --syslog --pid-file {pid_file} {config}"
|
|
command_user="{user}:{group}"
|
|
pidfile="{pid_file}"
|
|
|
|
depend() {{
|
|
need net
|
|
use logger
|
|
after firewall
|
|
}}
|
|
|
|
start_pre() {{
|
|
checkpath --directory --owner {user}:{group} --mode 0755 /var/run
|
|
checkpath --directory --owner {user}:{group} --mode 0755 /var/lib/telemt
|
|
checkpath --directory --owner {user}:{group} --mode 0755 /var/log/telemt
|
|
}}
|
|
|
|
reload() {{
|
|
ebegin "Reloading ${{RC_SVCNAME}}"
|
|
start-stop-daemon --signal HUP --pidfile "${{pidfile}}"
|
|
eend $?
|
|
}}
|
|
"#,
|
|
description = opts.description,
|
|
exe = opts.exe_path.display(),
|
|
config = opts.config_path.display(),
|
|
pid_file = opts.pid_file,
|
|
user = user,
|
|
group = group,
|
|
)
|
|
}
|
|
|
|
/// Generates a FreeBSD rc.d script.
|
|
fn generate_freebsd_rc_script(opts: &ServiceOptions) -> String {
|
|
let user = opts.user.unwrap_or("root");
|
|
let group = opts.group.unwrap_or("wheel");
|
|
|
|
format!(
|
|
r#"#!/bin/sh
|
|
#
|
|
# PROVIDE: telemt
|
|
# REQUIRE: LOGIN NETWORKING
|
|
# KEYWORD: shutdown
|
|
#
|
|
# Add the following lines to /etc/rc.conf to enable telemt:
|
|
#
|
|
# telemt_enable="YES"
|
|
# telemt_config="/etc/telemt/config.toml" # optional
|
|
# telemt_user="telemt" # optional
|
|
# telemt_group="telemt" # optional
|
|
#
|
|
|
|
. /etc/rc.subr
|
|
|
|
name="telemt"
|
|
rcvar="telemt_enable"
|
|
desc="{description}"
|
|
|
|
load_rc_config $name
|
|
|
|
: ${{telemt_enable:="NO"}}
|
|
: ${{telemt_config:="{config}"}}
|
|
: ${{telemt_user:="{user}"}}
|
|
: ${{telemt_group:="{group}"}}
|
|
: ${{telemt_pidfile:="{pid_file}"}}
|
|
|
|
pidfile="${{telemt_pidfile}}"
|
|
command="{exe}"
|
|
command_args="--daemon --syslog --pid-file ${{telemt_pidfile}} ${{telemt_config}}"
|
|
|
|
start_precmd="telemt_prestart"
|
|
reload_cmd="telemt_reload"
|
|
extra_commands="reload"
|
|
|
|
telemt_prestart() {{
|
|
install -d -o ${{telemt_user}} -g ${{telemt_group}} -m 755 /var/run
|
|
install -d -o ${{telemt_user}} -g ${{telemt_group}} -m 755 /var/lib/telemt
|
|
}}
|
|
|
|
telemt_reload() {{
|
|
if [ -f "${{pidfile}}" ]; then
|
|
echo "Reloading ${{name}} configuration."
|
|
kill -HUP $(cat ${{pidfile}})
|
|
else
|
|
echo "${{name}} is not running."
|
|
return 1
|
|
fi
|
|
}}
|
|
|
|
run_rc_command "$1"
|
|
"#,
|
|
description = opts.description,
|
|
exe = opts.exe_path.display(),
|
|
config = opts.config_path.display(),
|
|
pid_file = opts.pid_file,
|
|
user = user,
|
|
group = group,
|
|
)
|
|
}
|
|
|
|
/// Installation instructions for each init system.
|
|
pub fn installation_instructions(init_system: InitSystem) -> &'static str {
|
|
match init_system {
|
|
InitSystem::Systemd => {
|
|
r#"To install and enable the service:
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable telemt
|
|
sudo systemctl start telemt
|
|
|
|
To check status:
|
|
sudo systemctl status telemt
|
|
|
|
To view logs:
|
|
journalctl -u telemt -f
|
|
|
|
To reload configuration:
|
|
sudo systemctl reload telemt
|
|
"#
|
|
}
|
|
InitSystem::OpenRC => {
|
|
r#"To install and enable the service:
|
|
sudo chmod +x /etc/init.d/telemt
|
|
sudo rc-update add telemt default
|
|
sudo rc-service telemt start
|
|
|
|
To check status:
|
|
sudo rc-service telemt status
|
|
|
|
To reload configuration:
|
|
sudo rc-service telemt reload
|
|
"#
|
|
}
|
|
InitSystem::FreeBSDRc => {
|
|
r#"To install and enable the service:
|
|
sudo chmod +x /usr/local/etc/rc.d/telemt
|
|
sudo sysrc telemt_enable="YES"
|
|
sudo service telemt start
|
|
|
|
To check status:
|
|
sudo service telemt status
|
|
|
|
To reload configuration:
|
|
sudo service telemt reload
|
|
"#
|
|
}
|
|
InitSystem::Unknown => {
|
|
r#"No supported init system detected.
|
|
You may need to create a service file manually or run telemt directly:
|
|
telemt start /etc/telemt/config.toml
|
|
"#
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_systemd_unit_generation() {
|
|
let opts = ServiceOptions::default();
|
|
let unit = generate_systemd_unit(&opts);
|
|
assert!(unit.contains("[Unit]"));
|
|
assert!(unit.contains("[Service]"));
|
|
assert!(unit.contains("[Install]"));
|
|
assert!(unit.contains("ExecReload="));
|
|
assert!(unit.contains("PIDFile="));
|
|
}
|
|
|
|
#[test]
|
|
fn test_openrc_script_generation() {
|
|
let opts = ServiceOptions::default();
|
|
let script = generate_openrc_script(&opts);
|
|
assert!(script.contains("#!/sbin/openrc-run"));
|
|
assert!(script.contains("depend()"));
|
|
assert!(script.contains("reload()"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_freebsd_rc_script_generation() {
|
|
let opts = ServiceOptions::default();
|
|
let script = generate_freebsd_rc_script(&opts);
|
|
assert!(script.contains("#!/bin/sh"));
|
|
assert!(script.contains("PROVIDE: telemt"));
|
|
assert!(script.contains("run_rc_command"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_service_file_paths() {
|
|
assert_eq!(service_file_path(InitSystem::Systemd), "/etc/systemd/system/telemt.service");
|
|
assert_eq!(service_file_path(InitSystem::OpenRC), "/etc/init.d/telemt");
|
|
assert_eq!(service_file_path(InitSystem::FreeBSDRc), "/usr/local/etc/rc.d/telemt");
|
|
}
|
|
}
|