Skip to content

Commit 98bde16

Browse files
authored
Merge pull request #46 from hug-dev/daemon
Add a compile-time option for a daemon binary
2 parents 7e6dbeb + 3a8d82e commit 98bde16

File tree

7 files changed

+103
-13
lines changed

7 files changed

+103
-13
lines changed

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ uuid = "0.7.4"
1717
threadpool = "1.7.1"
1818
std-semaphore = "0.1.0"
1919
signal-hook = "0.1.10"
20+
sd-notify = { version = "0.1.0", optional = true }
2021

2122
[dev-dependencies]
2223
parsec-client-test = { git = "https://github.com/parallaxsecond/parsec-client-test", tag = "0.1.2" }
@@ -34,3 +35,7 @@ mbed-crypto-version = "mbedcrypto-1.1.0"
3435
[features]
3536
default = ["mbed"]
3637
mbed = []
38+
39+
# Feature to compile the PARSEC binary to be executed as a systemd daemon.
40+
# This feature is only available on Linux.
41+
systemd-daemon = ["sd-notify"]

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ This project uses the following third party crates:
139139
* std-semaphore (MIT and Apache-2.0)
140140
* num_cpus (MIT and Apache-2.0)
141141
* signal-hook (MIT and Apache-2.0)
142+
* sd-notify (Apache-2.0)
142143

143144
This project uses the following third party libraries:
144145
* [Mbed Crypto](https://github.com/ARMmbed/mbed-crypto) (Apache-2.0)
@@ -160,6 +161,39 @@ You can execute unit tests with `cargo test --lib`.
160161
The [test client](https://github.com/parallaxsecond/parsec-client-test) is used for integration
161162
testing. Check that repository for more details.
162163

164+
# **Installing the PARSEC service (Linux only)**
165+
166+
PARSEC can be built and installed as a Linux daemon using systemd. The PARSEC daemon uses socket
167+
activation which means that the daemon will be automatically started when a client request is
168+
made on the socket. The daemon is a systemd user daemon run by the `parsec` user.
169+
170+
If your Linux system uses systemd to manage daemons, you can follow these steps.
171+
172+
* Create and log in to a new user named `parsec`
173+
* In its home directory, pull and install PARSEC as a daemon
174+
```bash
175+
$ git pull https://github.com/parallaxsecond/parsec.git
176+
$ cargo install --features "systemd-daemon" --path parsec
177+
```
178+
* Install the systemd unit files and activate the PARSEC socket
179+
```bash
180+
$ mkdir -p ~/.config/systemd/user
181+
$ cp -r systemd-daemon/parsec.service systemd-daemon/parsec.socket ~/.config/systemd/user
182+
$ systemctl --user enable parsec.socket
183+
$ systemctl --user start parsec.socket
184+
```
185+
186+
Every user on the system can now use PARSEC!
187+
188+
You can test it going inside the `parsec` directory and:
189+
```bash
190+
$ cargo test --test normal
191+
```
192+
Check the logs with:
193+
```bash
194+
$ journalclt --user -u parsec
195+
```
196+
163197
# **Contributing**
164198

165199
Please check the [**Contributing**](CONTRIBUTING.md) to know more about the contribution process.

src/bin/main.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ fn main() -> Result<(), Error> {
109109

110110
let threadpool = Builder::new().build();
111111

112+
#[cfg(feature = "systemd-daemon")]
113+
// Notify systemd that the daemon is ready, the start command will block until this point.
114+
let _ = sd_notify::notify(true, &[sd_notify::NotifyState::Ready]);
115+
112116
loop {
113117
if kill_signal.load(Ordering::Relaxed) {
114118
println!("SIGTERM signal received.");

src/front/domain_socket.rs

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use super::listener;
2222

2323
use listener::Listen;
2424
use listener::ReadWrite;
25+
use std::os::unix::io::FromRawFd;
2526

2627
static SOCKET_PATH: &str = "/tmp/security-daemon-socket";
2728

@@ -43,23 +44,35 @@ impl DomainSocketListener {
4344
/// fails
4445
/// - if binding to the socket path fails
4546
fn init(&mut self) {
46-
let socket = Path::new(SOCKET_PATH);
47+
if cfg!(feature = "systemd-daemon") {
48+
// The PARSEC service is socket activated (see parsec.socket file).
49+
// systemd creates the PARSEC service giving it an initialised socket as the file
50+
// descriptor number 3 (see sd_listen_fds(3) man page).
51+
// If an instance of PARSEC compiled with the "systemd-daemon" feature is run directly
52+
// instead of by systemd, this call will still work but the next accept call on the
53+
// UnixListener will generate a Linux error 9 (Bad file number), as checked below.
54+
unsafe {
55+
self.listener = Some(UnixListener::from_raw_fd(3));
56+
}
57+
} else {
58+
let socket = Path::new(SOCKET_PATH);
4759

48-
if socket.exists() {
49-
fs::remove_file(&socket).unwrap();
50-
}
60+
if socket.exists() {
61+
fs::remove_file(&socket).unwrap();
62+
}
5163

52-
let listener_val = match UnixListener::bind(SOCKET_PATH) {
53-
Ok(listener) => listener,
54-
Err(err) => panic!(err),
55-
};
64+
let listener_val = match UnixListener::bind(SOCKET_PATH) {
65+
Ok(listener) => listener,
66+
Err(err) => panic!(err),
67+
};
5668

57-
// Set the socket as non-blocking.
58-
listener_val
59-
.set_nonblocking(true)
60-
.expect("Could not set the socket as non-blocking");
69+
// Set the socket as non-blocking.
70+
listener_val
71+
.set_nonblocking(true)
72+
.expect("Could not set the socket as non-blocking");
6173

62-
self.listener = Some(listener_val);
74+
self.listener = Some(listener_val);
75+
}
6376
}
6477
}
6578

@@ -84,6 +97,16 @@ impl Listen for DomainSocketListener {
8497
}
8598
}
8699
Err(err) => {
100+
if cfg!(feature = "systemd-daemon") {
101+
// When run as a systemd daemon, a file descriptor mapping to the Domain Socket
102+
// should have been passed to this process.
103+
if let Some(os_error) = err.raw_os_error() {
104+
// On Linux, 9 is EBADF (Bad file number)
105+
if os_error == 9 {
106+
panic!("The Unix Domain Socket file descriptor (number 3) should have been given to this process.");
107+
}
108+
}
109+
}
87110
// Check if the error is because no connections are currently present.
88111
if err.kind() != ErrorKind::WouldBlock {
89112
// Only log the real errors.

systemd-daemon/parsec.service

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[Unit]
2+
Description=PARSEC Service
3+
Documentation=https://github.com/parallaxsecond/parsec
4+
5+
[Service]
6+
Type=notify
7+
NonBlocking=true
8+
ExecStart=/home/parsec/.cargo/bin/parsec

systemd-daemon/parsec.socket

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[Unit]
2+
Description=PARSEC Socket
3+
Documentation=https://github.com/parallaxsecond/parsec
4+
5+
[Socket]
6+
ListenStream=/home/parsec/parsec.sock
7+
8+
[Install]
9+
WantedBy=sockets.target

0 commit comments

Comments
 (0)