Skip to content

Commit 846ba1d

Browse files
committed
starting work on system wide services
1 parent 621193f commit 846ba1d

File tree

5 files changed

+75
-39
lines changed

5 files changed

+75
-39
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ Reimplementation of features already in the main branch:
2828

2929
Planned:
3030

31+
- Flag in main to always confirm prompts.
32+
- Maybe add environment variable for the main crescent directory, retrieving the user's home directory while in root returns "/root".
33+
- Fix some commands not erroring when failing to send commands to a system service.
3134
- Save information about the service in a file inside the service folder.
3235
- Add more arguments/commands to `log`, commands to manage the logs for that service for example.
3336
- Add `delete` service/profile command.

src/commands/new.rs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ impl NewArgs {
9595
}
9696

9797
let exec_cmd = self.format_exec_cmd(&exec_path);
98+
let user_service = Crescent::parse().user_service;
9899

99100
util::println_field_value("Service name", &name);
100101
util::println_field_value("Profile", self.profile.clone().unwrap_or_default());
@@ -106,18 +107,28 @@ impl NewArgs {
106107
init_system.get_scripts_paths().iter().for_each(|path| {
107108
println!("{path}");
108109
});
109-
util::println_field_value("System wide:", Crescent::parse().system_wide);
110+
util::println_field_value("System wide:", user_service);
110111

111112
util::confirm("Create this service?")?;
112113

113-
init_system.create(&exec_cmd)?;
114-
eprintln!("Service '{name}' created");
114+
// To write system wide files we need to be root.
115+
if let Err(err) = init_system.create(&exec_cmd) {
116+
if !user_service && err.to_string().contains("Permission denied") {
117+
return Err(anyhow!("You must be root to create system wide services."));
118+
}
119+
}
115120

116121
init_system.reload()?;
117122

123+
let event = if user_service { "user login" } else { "boot" };
124+
let question = format!("Enable this service to start on {event}?");
125+
if util::confirm(&question).is_ok() {
126+
eprintln!("Enabling '{name}'");
127+
init_system.enable()?;
128+
}
129+
118130
eprintln!("Starting '{name}'");
119131
init_system.start()?;
120-
println!("Service '{name}' started");
121132
Ok(())
122133
}
123134

@@ -132,7 +143,10 @@ impl NewArgs {
132143
let name = name.to_lowercase();
133144

134145
if name.len() > 16 {
135-
return Err(anyhow!("Name is too long. Max length is 16 characters."));
146+
return Err(anyhow!(
147+
"Name is too long. Max length is 16 characters: {}",
148+
name
149+
));
136150
}
137151

138152
let valid_chars = ['_', '-', '.'];

src/init_systems/systemd.rs

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ pub struct Systemd {
2929
service_name: String,
3030
/// `cres.<name>.socket`
3131
socket_name: String,
32-
system_wide: bool,
32+
/// Use the user's service directory instead of the system's, requires root if false.
33+
user_service: bool,
3334
}
3435

3536
impl Systemd {
@@ -40,14 +41,17 @@ impl Systemd {
4041
name: name.to_string(),
4142
service_name: format!("cres.{name}.service"),
4243
socket_name: format!("cres.{name}.socket"),
43-
system_wide: Crescent::parse().system_wide,
44+
user_service: Crescent::parse().user_service,
4445
}
4546
}
4647

47-
/// Run a `systemctl` command as the user.
48-
fn run_command(&self, args: Vec<&str>) -> Result<Output> {
48+
/// Run a `systemctl` command.
49+
fn run_command(&self, mut args: Vec<&str>) -> Result<Output> {
50+
if self.user_service {
51+
args.push("--user");
52+
}
53+
4954
Ok(Command::new("systemctl")
50-
.arg("--user")
5155
.arg("--no-pager")
5256
.args(args)
5357
.output()?)
@@ -57,6 +61,10 @@ impl Systemd {
5761
let requires = format!("Requires={}", self.socket_name);
5862
let after = format!("After=network.target {}", self.socket_name);
5963
let exec_start = format!("ExecStart={cmd}");
64+
let wanted_by = match self.user_service {
65+
true => "WantedBy=default.target",
66+
false => "WantedBy=multi-user.target",
67+
};
6068

6169
let service = [
6270
"[Unit]",
@@ -71,13 +79,10 @@ impl Systemd {
7179
"StandardError=journal",
7280
"",
7381
"[Install]",
74-
"WantedBy=default.target",
82+
wanted_by,
7583
"",
7684
];
7785

78-
if let Some(parent) = path.parent() {
79-
fs::create_dir_all(parent)?;
80-
}
8186
fs::write(path, service.join("\n"))?;
8287
Ok(())
8388
}
@@ -94,9 +99,6 @@ impl Systemd {
9499
"",
95100
];
96101

97-
if let Some(parent) = path.parent() {
98-
fs::create_dir_all(parent)?;
99-
}
100102
fs::write(path, socket.join("\n"))?;
101103
Ok(())
102104
}
@@ -110,15 +112,15 @@ impl InitSystem for Systemd {
110112
}
111113

112114
fn get_scripts_paths(&self) -> Vec<String> {
113-
if self.system_wide {
115+
if self.user_service {
114116
vec![
115-
SYSTEM_DIR.to_string() + &self.service_name,
116-
SYSTEM_DIR.to_string() + &self.socket_name,
117+
USER_DIR.to_string() + &self.service_name,
118+
USER_DIR.to_string() + &self.socket_name,
117119
]
118120
} else {
119121
vec![
120-
USER_DIR.to_string() + &self.service_name,
121-
USER_DIR.to_string() + &self.socket_name,
122+
SYSTEM_DIR.to_string() + &self.service_name,
123+
SYSTEM_DIR.to_string() + &self.socket_name,
122124
]
123125
}
124126
}
@@ -143,8 +145,11 @@ impl InitSystem for Systemd {
143145
}
144146

145147
fn create(&self, cmd: &str) -> Result<()> {
146-
let path_str =
147-
env::var("HOME").expect("Error retrieving HOME directory.") + "/.config/systemd/user/";
148+
let path_str = match self.user_service {
149+
true => USER_DIR.to_string(),
150+
false => SYSTEM_DIR.to_string(),
151+
};
152+
148153
let path = PathBuf::from(path_str);
149154

150155
eprintln!("Writing '{}' unit", self.service_name);
@@ -265,12 +270,12 @@ impl InitSystem for Systemd {
265270
}
266271

267272
fn list(&self) -> Result<Vec<String>> {
268-
let output = self.run_command(vec!["list-unit-files"])?;
273+
let output = self.run_command(vec!["list-unit-files", "cres.*.service"])?;
269274
let out = String::from_utf8(output.stdout)?;
270275
let output_lines = out.lines().collect::<Vec<&str>>();
271276
let names: Vec<String> = output_lines
272277
.iter()
273-
.filter(|line| line.contains("cres.") && line.contains(".service"))
278+
.filter(|line| line.starts_with("cres."))
274279
.map(|s| s.split_whitespace().next().unwrap_or("").to_string())
275280
.collect();
276281
Ok(names)

src/loggers/journald.rs

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,32 @@
11
use std::process::{Child, Command, Output, Stdio};
22

33
use anyhow::Result;
4+
use clap::Parser;
45

5-
use crate::logger::Logger;
6+
use crate::{logger::Logger, Crescent};
67

78
/// `journald` implementation.
89
pub struct Journald {
910
service_name: String,
11+
user_service: bool,
1012
}
1113

1214
impl Journald {
1315
pub fn new(service_name: &str) -> Self {
1416
let service_name = format!("cres.{}.service", service_name);
15-
Self { service_name }
17+
Self {
18+
service_name,
19+
user_service: Crescent::parse().user_service,
20+
}
1621
}
1722

1823
/// Run a `journald` command as the user.
19-
fn run_command(&self, args: Vec<&str>) -> Result<Output> {
24+
fn run_command(&self, mut args: Vec<&str>) -> Result<Output> {
25+
if self.user_service {
26+
args.push("--user");
27+
}
28+
2029
Ok(Command::new("journalctl")
21-
.arg("--user")
2230
.arg("--unit")
2331
.arg(&self.service_name)
2432
.arg("--no-pager")
@@ -35,13 +43,20 @@ impl Logger for Journald {
3543
}
3644

3745
fn follow(&self) -> Result<Child> {
46+
let mut args = vec![
47+
"--unit",
48+
&self.service_name,
49+
"--no-pager",
50+
"--follow",
51+
"--lines=200",
52+
];
53+
54+
if self.user_service {
55+
args.push("--user");
56+
}
57+
3858
Ok(Command::new("journalctl")
39-
.arg("--user")
40-
.arg("--unit")
41-
.arg(&self.service_name)
42-
.arg("--no-pager")
43-
.arg("--follow")
44-
.arg("--lines=200")
59+
.args(args)
4560
.stdout(Stdio::piped())
4661
.spawn()?)
4762
}

src/main.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,9 @@ struct Crescent {
6161
help = "Talk to the init and logging systems of the current user instead of the system",
6262
short = 'u',
6363
long = "user",
64-
global = true,
65-
action = clap::ArgAction::SetFalse
64+
global = true
6665
)]
67-
pub system_wide: bool,
66+
pub user_service: bool,
6867

6968
#[command(subcommand)]
7069
pub commands: Commands,

0 commit comments

Comments
 (0)