Skip to content
Closed
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
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ xml-rs = "0.8.19"
encoding_rs = { version = "0.8", optional = true }
encoding-utils = { version ="0.1", optional = true }

[target.'cfg(windows)'.dependencies]
windows-service = "0.7.0"

[dev-dependencies]
assert_fs = "1.0.13"
indoc = "2.0.4"
Expand Down
3 changes: 3 additions & 0 deletions src/kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ pub enum ServiceManagerKind {
/// Use Windows service controller to manage the service
Sc,

/// Use Service Control Manager to manage the service
Scm,

/// Use systemd to manage the service
Systemd,

Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ mod systemd;
mod typed;
mod utils;
mod winsw;
mod scm;

pub use kind::*;
pub use launchd::*;
Expand All @@ -29,6 +30,7 @@ pub use sc::*;
pub use systemd::*;
pub use typed::*;
pub use winsw::*;
pub use scm::*;

/// Interface for a service manager
pub trait ServiceManager {
Expand Down
108 changes: 73 additions & 35 deletions src/openrc.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use crate::utils::wrap_output;
use crate::utils::{wrap_output, option_iterator_to_string};

use super::{
utils, ServiceInstallCtx, ServiceLevel, ServiceManager, ServiceStartCtx, ServiceStopCtx,
ServiceUninstallCtx,
};

use std::{
ffi::{OsStr, OsString},
ffi::OsStr,
io,
path::PathBuf,
process::{Command, Output, Stdio},
Expand All @@ -19,7 +20,20 @@ const SCRIPT_FILE_PERMISSIONS: u32 = 0o755;

/// Configuration settings tied to OpenRC services
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct OpenRcConfig {}
pub struct OpenRcConfig {
pub install: OpenRcInstallConfig,
}

#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct OpenRcInstallConfig {
pub description: Option<String>,
pub provide: Option<Vec<String>>,
pub want: Option<Vec<String>>,
pub need: Option<Vec<String>>,
pub after: Option<Vec<String>>,
pub before: Option<Vec<String>>,
pub uses: Option<Vec<String>>,
}

/// Implementation of [`ServiceManager`] for Linux's [OpenRC](https://en.wikipedia.org/wiki/OpenRC)
#[derive(Clone, Debug, Default, PartialEq, Eq)]
Expand Down Expand Up @@ -56,15 +70,7 @@ impl ServiceManager for OpenRcServiceManager {
let script_name = ctx.label.to_script_name();
let script_path = dir_path.join(&script_name);

let script = match ctx.contents {
Some(contents) => contents,
_ => make_script(
&script_name,
&script_name,
ctx.program.as_os_str(),
ctx.args,
),
};
let script = make_script(&ctx, &self.config.install);

utils::write_file(
script_path.as_path(),
Expand Down Expand Up @@ -208,28 +214,60 @@ fn service_dir_path() -> PathBuf {
PathBuf::from("/etc/init.d")
}

fn make_script(description: &str, provide: &str, program: &OsStr, args: Vec<OsString>) -> String {
let program = program.to_string_lossy();
let args = args
.into_iter()
.map(|a| a.to_string_lossy().to_string())
.collect::<Vec<String>>()
fn make_script(ctx: &ServiceInstallCtx, config: &OpenRcInstallConfig) -> String {
if let Some(ref contents) = ctx.contents {
return contents.clone();
}

use std::fmt::Write;

let provide = option_iterator_to_string(&config.provide, " ")
.unwrap_or(ctx.label.to_script_name());
let program = ctx.program.display().to_string();
let args = ctx
.args
.iter()
.map(|v| v.to_string_lossy().trim().to_string())
.collect::<Vec<_>>()
.join(" ");
format!(
r#"
#!/sbin/openrc-run

description="{description}"
command="{program}"
command_args="{args}"
pidfile="/run/${{RC_SVCNAME}}.pid"
command_background=true

depend() {{
provide {provide}
}}
"#
)
.trim()
.to_string()
let destription = config
.description
.clone()
.unwrap_or(ctx.label.to_script_name());
let workdir = ctx.working_directory.as_ref();

let mut script = String::new();

let _ = writeln!(script, "!/sbin/openrc-run");
let _ = writeln!(script);
let _ = writeln!(script, "description=\"{destription}\"");
let _ = writeln!(script, "command=\"{program}\"");
let _ = writeln!(script, "command_args=\"{args}\"");
let _ = writeln!(script, "pidfile=\"/run/${{RC_SVCNAME}}.pid\"");
let _ = writeln!(script, "command_background=\"yes\"");
if let Some(workdir) = workdir {
let workdir = workdir.display().to_string();
let _ = writeln!(script, "directory=\"{workdir}\"");
}
let _ = writeln!(script);
let _ = writeln!(script, "depend() {{");
let _ = writeln!(script, " provide {provide}");
if let Some(v) = option_iterator_to_string(&config.need, " ") {
let _ = writeln!(script, " need {v}");
}
if let Some(v) = option_iterator_to_string(&config.want, " ") {
let _ = writeln!(script, " want {v}");
}
if let Some(v) = option_iterator_to_string(&config.uses, " ") {
let _ = writeln!(script, " use {v}");
}
if let Some(v) = option_iterator_to_string(&config.after, " ") {
let _ = writeln!(script, " after {v}");
}
if let Some(v) = option_iterator_to_string(&config.before, " ") {
let _ = writeln!(script, " before {v}");
}
let _ = writeln!(script, "}}");

script
}
113 changes: 74 additions & 39 deletions src/rcd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use super::{
ServiceUninstallCtx,
};
use std::{
ffi::{OsStr, OsString},
io,
path::PathBuf,
process::{Command, ExitStatus, Stdio},
Expand All @@ -16,7 +15,17 @@ const SCRIPT_FILE_PERMISSIONS: u32 = 0o755;

/// Configuration settings tied to rc.d services
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct RcdConfig {}
pub struct RcdConfig {
pub install: RcdInstallConfig,
}

#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct RcdInstallConfig {
pub provide: Option<Vec<String>>,
pub description: Option<String>,
pub require: Option<Vec<String>>,
pub before: Option<Vec<String>>,
}

/// Implementation of [`ServiceManager`] for FreeBSD's [rc.d](https://en.wikipedia.org/wiki/Init#Research_Unix-style/BSD-style)
#[derive(Clone, Debug, Default, PartialEq, Eq)]
Expand Down Expand Up @@ -48,10 +57,7 @@ impl ServiceManager for RcdServiceManager {

fn install(&self, ctx: ServiceInstallCtx) -> io::Result<()> {
let service = ctx.label.to_script_name();
let script = match ctx.contents {
Some(contents) => contents,
_ => make_script(&service, &service, ctx.program.as_os_str(), ctx.args),
};
let script = make_script(&ctx, &self.config.install);

utils::write_file(
&rc_d_script_path(&service),
Expand Down Expand Up @@ -151,40 +157,69 @@ fn rc_d_script(cmd: &str, service: &str, wrap: bool) -> io::Result<ExitStatus> {
}
}

fn make_script(description: &str, provide: &str, program: &OsStr, args: Vec<OsString>) -> String {
let name = provide.replace('-', "_");
let program = program.to_string_lossy();
let args = args
.into_iter()
fn make_script(ctx: &ServiceInstallCtx, config: &RcdInstallConfig) -> String {
if let Some(ref contents) = ctx.contents {
return contents.clone();
}

use std::fmt::Write;

let script_name = ctx.label.to_script_name();
let provide = utils::option_iterator_to_string(&config.provide, " ")
.unwrap_or(ctx.label.to_script_name());
let name = script_name.replace("-", "_");
let description = config
.description
.as_deref()
.and_then(|v| {
let s = v.trim();
(!s.is_empty()).then(|| s)
})
.unwrap_or(provide.as_str());
let program = ctx.program.display().to_string();
let args = ctx
.args
.iter()
.map(|a| a.to_string_lossy().to_string())
.collect::<Vec<String>>()
.join(" ");
format!(
r#"
#!/bin/sh
#
# PROVIDE: {provide}
# REQUIRE: LOGIN FILESYSTEMS
# KEYWORD: shutdown

. /etc/rc.subr

name="{name}"
desc="{description}"
rcvar="{name}_enable"

load_rc_config ${{name}}

: ${{{name}_options="{args}"}}

pidfile="/var/run/{name}.pid"
procname="{program}"
command="/usr/sbin/daemon"
command_args="-c -S -T ${{name}} -p ${{pidfile}} ${{procname}} ${{{name}_options}}"

run_rc_command "$1"
"#
)
.trim()
.to_string()
let require = utils::option_iterator_to_string(&config.require, " ")
.unwrap_or("LOGIN FILESYSTEMS".to_string());

let mut script = String::new();

_ = writeln!(script, "#!/bin/sh");
_ = writeln!(script, "#");
_ = writeln!(script, "# PROVIDE: {provide}");
_ = writeln!(script, "# REQUIRE: {require}");
if let Some(before) = utils::option_iterator_to_string(&config.before, " ") {
_ = writeln!(script, "# BEFORE: {before}");
}
_ = writeln!(script, "# KEYWORD: shutdown");
_ = writeln!(script);
_ = writeln!(script, ". /etc/rc.subr");
_ = writeln!(script);
_ = writeln!(script, "name=\"{name}\"");
_ = writeln!(script, "desc=\"{description}\"");
_ = writeln!(script, "rcvar=\"{name}_enable\"");
_ = writeln!(script);
_ = writeln!(script, "load_rc_config ${{name}}");
_ = writeln!(script);
_ = writeln!(script, ": ${{{name}_options=\"{args}\"}}");
_ = writeln!(script);
if let Some(ref x) = ctx.working_directory {
let work_dir = x.display().to_string();
_ = writeln!(script, "{name}_chdir=\"{work_dir}\"");
}
_ = writeln!(script, "pidfile=\"/var/run/${{name}}.pid\"");
_ = writeln!(script, "procname=\"{program}\"");
_ = writeln!(script, "command=\"/usr/sbin/daemon\"");
_ = writeln!(
script,
"command_args=\"-c -S -T ${{name}} -p ${{pidfile}} ${{procname}} ${{{name}_options}}\""
);
_ = writeln!(script);
_ = writeln!(script, "run_rc_command \"$1\"");

script
}
Loading