Systemd integration
Proposal
It'd be nice to natively integrate into systemd (possibly as an optional feature). Interesting features:
- [ ] socket activation - get the listen sockets from systemd env var to enable parallel start
- [ ] notification - notify systemd when exactly the service started
- [ ] logging to journald - I'm not sure what the exact benefits are. Maybe nice, unified interface?
Analysis of available crates
I did preliminary research on available systemd crates:
-
systemd(0.4.0) - binds to native library, doesn't support named fds -
libsystemd(0.2.1) - pure Rust lib, supports named fds, may contain unneeded stuff -
sd_notify(0.1.1) - looks minimalistic, which is nice but doesn't support named fds
I personally believe that named file descriptors are much more robust way of interacting with systemd and thus I'd recommend libsystemd even at the cost of having a larger library. Alternatively, we could try to add named fds to sd_notify or implement our own.
Considerations on interaction with configuration
There are various ways to handle the situation where configuration is supplied in the files and fds are available from systemd:
- Fail on confusing situation
- Warn, continue with systemd
- Warn, continue with configuration
- Fail unless a special switch explicitly defines the behavior (but what's that good for?)
I intuitively like warn, continue with systemd, can't explain why.
Another thing to consider is how the code should be structured. Currently we use Option and turn it into SocketAddr. We could turn it into a socket directly, but then we would have to bind inside configuration, which feels a bit weird. Alternatively we could create a new enum that can contain the file descriptor or address and then handle it in RPC acceptor. This solution feels quite clean, but maybe needlessly complex?
I was also thinking about combining this with configure_me somehow. Sure, it feels quite kitchen-sink-ey but it could have the ability to generate systemd socket files from specification. That'd be pretty cool. Maybe do it as a special library that can interact with configure_me in a reasonable way? Or finally implement interface file specification and do it there - that sounds quite nice, if done properly.
I'd also like to see Unix socket support at some point in the future, which would almost work out-of-the-box (Unix socket pretending to be TCP socket), except for accept() having incompatible address. I'd probably address this in the future by implementing abstract sockets that don't care about it.
What do you think?
The suggestion sounds good - although I am not using systemd for electrs, I guess it should help many other users. I'll be happy to help with review and tests :)
Do you have any preference on how it should interact with the configuration?
I agree with "Warn, continue with systemd" - but we should make sure the warning is verbose & clear enough to understand what went is wrong, and how it should be fixed. Regarding opening sockets - I would suggest to start with the simplest solution, since we can always make it more generic later :)
I was thinking about this again and found an alternative that's quite elegant in my opinion. Instead of adding another configuration switch, we could allow systemd://SOCKET_NAME inside the current configuration options.
This would be explicit and possibly less confusing.
Since this approach might be more widely useful, I'm seriously considering writing a crate which helps with that a bit. It'd define a type:
enum Socket {
Address(SocketAddr),
Systemd(String),
}
// Let's not expose the representation for now
// Any better name for this struct?
pub struct MaybeSystemdSocket(Socket);
#[derive(Deserialize)]
#[serde(try_from = String)]
impl MaybeSystemdSocket {
// some constructors...
pub fn bind(self) -> Result<TcpListener, Error> {
// ...
}
}
impl ParseArg for MaybeSystemdSocket {
// ...
}
This would allow us to use this type with configure_me directly and then just call .bind() on the socket.
Unsurprisingly, I would use this crate in at least two other projects which also use configure_me. :)
Of course, one question still remains: what to do if the user didn't specify the socket? I'm currently rethinking "Warn, continue with systemd". Maybe the explicit configuration should be respected instead? Would love to know some arguments if there are any.
I've got pissed about some services not using systemd so I wrote the mentioned crate for socket activation. Please take a look at the API and tell me if you see any issue.
~~Disclaimer: I did NOT try whether it actually works with systemd yet. I will test it tomorrow or during weekend.~~
Tested now, even added automated tests to the repo.
Sounds good, let's do it for next branch.
I'd prefer to have it for both. I actually started doing it recently but hit feature lacking in tinyhttp. I suggest waiting with monitoring for now and see if I get a reply to my question. If I don't get a reply in a reasonable time, then it might be a sign of an unmaintained library and we will decide later.
LOL, couldn't wait for reply, found out it's trivial and submitted a PR. So lets' wait for the PR to be merged instead. ;)