I'm having difficulty creating a daemon for a unix socket server in rust. In a language like node.js i'd just spawn a detached process. But rust seems to be a bit more challenging.
(all work is on unix environments)
Here is a quick example. I wrote up:
use std::os::unix::net::{UnixListener, UnixStream};
use std::path::{Path};
use std::io::{Read, Write};
use std::{fs};
use std::thread;
fn handle_stream(mut stream: UnixStream) {
loop {
let mut read = [0; 1028];
match stream.read(&mut read) {
Ok(n) => {
if n == 0 {
// connection was closed
println!("connection closed?");
break;
}
let msg = String::from_utf8(read.to_vec()).unwrap();
println!("SERVER: {} received from remote client.", msg);
match stream.write_all(&read[0..n]) {
Ok(_) => {}
Err(e) => println!("Error writing to socket: {}", e),
}
}
Err(err) => {
panic!(err);
}
}
}
println!("SERVER: Ending connection with client.");
}
fn server_start() {
// remove existing sock if exists
let _did_remove = fs::remove_file("/Users/tom/desktop/app.sock");
// socket location
let socket_file = "/Users/tom/desktop/app.sock";
let socket = Path::new(socket_file);
// Bind to socket
let listener = match UnixListener::bind(&socket) {
Err(_) => panic!("failed to bind socket"),
Ok(stream) => stream,
};
println!("Server started, waiting for clients");
for conn in listener.incoming() {
match conn {
Ok(stream) => {
// spawn a new thread for each incoming
thread::spawn(|| handle_stream(stream));
}
Err(err) => {
println!("Error connecting to client: {}", err);
break;
}
}
}
}
fn main() {
server_start();
}
Following the messages in the comments, I decided to use a systemd service rather than creating my own daemon. This seems to be an ideal way to manage background tasks. I've edited the top code so that it makes sense with the answer.
Systemd - linux
You will need to create a .service
file and place it in your systemd daemon directory. For example: /etc/systemd/system/test.service
Then update the file rights:
sudo chmod 644 /etc/systemd/system/test.service
To start your service:
sudo systemctl start service_name
Service Code:
[Unit]
Description=Test service
After=network.target
StartLimitIntervalSec=0
[Service]
Type=simple
Restart=always
RestartSec=1
User=username
ExecStart=/usr/bin/env test
[Install]
WantedBy=multi-user.target
Launchctl - macOS
For macOS we need to create a .plist
file and place it in the launch daemons directory. For example: /Library/LaunchDaemons/test.plist
Next update the permissions on the file:
sudo chown root:wheel /Library/LaunchDaemons/com.test.daemon.plist
Load the daemon:
launchctl load /Library/LaunchDaemons/com.test.daemon.plist
Start the daemon:
launchctl start /Library/LaunchDaemons/com.test.daemon
Plist code:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.test.daemon</string>
<key>ServiceDescription</key>
<string>Test Server</string>
<key>ProgramArguments</key>
<array>
<string>/Users/tom/desktop/test/target/debug/test</string>
</array>
<key>RunAtLoad</key>
<false/>
</dict>
</plist>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With