Add systemd socket activation support #2095

Open
seercat wants to merge 3 commits from seercat/continuwuity:cat/socket-activation into main
First-time contributor

This pull request adds support for systemd socket activation. If systemd passes sockets, they will be used to listen in addition to the addresses/ports and unix socket set in the configuration.

This allows greater flexibility in managing the addresses on which continuwuity will listen, as systemd can use it's root privileges to bind to ports and paths which continuwuity itself cannot. Zero-downtime restarts may also be possible, as the listening socket will be kept open by systemd while continuwuity restarts.

I have also refactored the router/serve code to allow for this addition. I have kept that in a separate commit (i.e. please review by commits), but I can split this into two MRs if preferable.

Still to do:

  • Make sure it still works if direct_tls and/or systemd features are disabled
  • Add documentation
  • Changelog entry

Closes: #2087

Pull request checklist:

  • This pull request targets the main branch, and the branch is named something other than
    main.
  • I have written an appropriate pull request title and my description is clear.
  • I understand I am responsible for the contents of this pull request.
  • I have followed the contributing guidelines:
<!-- In order to help reviewers know what your pull request does at a glance, you should ensure that 1. Your PR title is a short, single sentence describing what you changed 2. You have described in more detail what you have changed, why you have changed it, what the intended effect is, and why you think this will be beneficial to the project. If you have made any potentially strange/questionable design choices, but didn't feel they'd benefit from code comments, please don't mention them here - after opening your pull request, go to "files changed", and click on the "+" symbol in the line number gutter, and attach comments to the lines that you think would benefit from some clarification. --> This pull request adds support for systemd socket activation. If systemd passes sockets, they will be used to listen in addition to the addresses/ports and unix socket set in the configuration. This allows greater flexibility in managing the addresses on which continuwuity will listen, as systemd can use it's root privileges to bind to ports and paths which continuwuity itself cannot. Zero-downtime restarts may also be possible, as the listening socket will be kept open by systemd while continuwuity restarts. I have also refactored the `router/serve` code to allow for this addition. I have kept that in a separate commit (i.e. please review by commits), but I can split this into two MRs if preferable. Still to do: - [x] Make sure it still works if `direct_tls` and/or `systemd` features are disabled - [x] Add documentation - [x] Changelog entry <!-- Example: This pull request allows us to warp through time and space ten times faster than before by double-inverting the warp drive with hyperheated jump fluid, both making the drive faster and more efficient. This resolves the common issue where we have to wait more than 10 milliseconds to engage, use, and disengage the warp drive when travelling between galaxies. --> Closes: #2087 <!-- Fixes: #... --> <!-- Uncomment the above line(s) if your pull request fixes an issue or closes another pull request by superseding it. Replace `#...` with the issue/pr number, such as `#123`. --> **Pull request checklist:** <!-- You need to complete these before your PR can be considered. If you aren't sure about some, feel free to ask for clarification in #dev:continuwuity.org. --> - [x] This pull request targets the `main` branch, and the branch is named something other than `main`. - [x] I have written an appropriate pull request title and my description is clear. - [x] I understand I am responsible for the contents of this pull request. - I have followed the [contributing guidelines][c1]: - [x] My contribution follows the [code style][c2], if applicable. - [x] I ran [pre-commit checks][c1pc] before opening/drafting this pull request. - [x] I have [tested my contribution][c1t] (or proof-read it for documentation-only changes) myself, if applicable. This includes ensuring code compiles. - [x] My commit messages follow the [commit message format][c1cm] and are descriptive. <!-- Notes on these requirements: - While not required, we encourage you to sign your commits with GPG or SSH to attest the authenticity of your changes. - While we allow LLM-assisted contributions, we do not appreciate contributions that are low quality, which is typical of machine-generated contributions that have not had a lot of love and care from a human. Please do not open a PR if all you have done is asked ChatGPT to tidy up the codebase with a +-100,000 diff. - In the case of code style violations, reviewers may leave review comments/change requests indicating what the ideal change would look like. For example, a reviewer may suggest you lower a log level, or use `match` instead of `if/else` etc. - In the case of code style violations, pre-commit check failures, minor things like typos/spelling errors, and in some cases commit format violations, reviewers may modify your branch directly, typically by making changes and adding a commit. Particularly in the latter case, a reviewer may rebase your commits to squash "spammy" ones (like "fix", "fix", "actually fix"), and reword commit messages that don't satisfy the format. - Pull requests MUST pass the `Checks` CI workflows to be capable of being merged. This can only be bypassed in exceptional circumstances. If your CI flakes, let us know in matrix:r/dev:continuwuity.org. - Pull requests have to be based on the latest `main` commit before being merged. If the main branch changes while you're making your changes, you should make sure you rebase on main before opening a PR. Your branch will be rebased on main before it is merged if it has fallen behind. - We typically only do fast-forward merges, so your entire commit log will be included. Once in main, it's difficult to get out cleanly, so put on your best dress, smile for the cameras! --> [c1]: https://forgejo.ellis.link/continuwuation/continuwuity/src/branch/main/CONTRIBUTING.md [c2]: https://forgejo.ellis.link/continuwuation/continuwuity/src/branch/main/docs/development/code_style.mdx [c1pc]: https://forgejo.ellis.link/continuwuation/continuwuity/src/branch/main/CONTRIBUTING.md#pre-commit-checks [c1t]: https://forgejo.ellis.link/continuwuation/continuwuity/src/branch/main/CONTRIBUTING.md#running-tests-locally [c1cm]: https://forgejo.ellis.link/continuwuation/continuwuity/src/branch/main/CONTRIBUTING.md#commit-messages
seercat force-pushed cat/socket-activation from 619269ee06
Some checks failed
Auto Labeler / Apply labels based on changed files (pull_request_target) Successful in 3s
Checks / Changelog / Check changelog is added (pull_request_target) Failing after 6s
Documentation / Build and Deploy Documentation (pull_request) Has been cancelled
Checks / Prek / Pre-commit & Formatting (pull_request) Has been cancelled
Checks / Prek / Check changed files (pull_request) Has been cancelled
Checks / Prek / Clippy and Cargo Tests (pull_request) Has been cancelled
Update flake hashes / update-flake-hashes (pull_request) Has been cancelled
to 743ff8a5d1
Some checks failed
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 7s
Documentation / Build and Deploy Documentation (pull_request) Has been cancelled
Checks / Prek / Pre-commit & Formatting (pull_request) Has been cancelled
Checks / Prek / Check changed files (pull_request) Has been cancelled
Checks / Prek / Clippy and Cargo Tests (pull_request) Has been cancelled
Update flake hashes / update-flake-hashes (pull_request) Has been cancelled
2026-08-04 10:17:49 +00:00
Compare
seercat force-pushed cat/socket-activation from 743ff8a5d1
Some checks failed
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 7s
Documentation / Build and Deploy Documentation (pull_request) Has been cancelled
Checks / Prek / Pre-commit & Formatting (pull_request) Has been cancelled
Checks / Prek / Check changed files (pull_request) Has been cancelled
Checks / Prek / Clippy and Cargo Tests (pull_request) Has been cancelled
Update flake hashes / update-flake-hashes (pull_request) Has been cancelled
to 1d5b448a7c
Some checks failed
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 6s
Documentation / Build and Deploy Documentation (pull_request) Has been cancelled
Checks / Prek / Pre-commit & Formatting (pull_request) Has been cancelled
Checks / Prek / Check changed files (pull_request) Has been cancelled
Checks / Prek / Clippy and Cargo Tests (pull_request) Has been cancelled
Update flake hashes / update-flake-hashes (pull_request) Has been cancelled
2026-08-04 11:16:47 +00:00
Compare
seercat force-pushed cat/socket-activation from 1d5b448a7c
Some checks failed
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 6s
Documentation / Build and Deploy Documentation (pull_request) Has been cancelled
Checks / Prek / Pre-commit & Formatting (pull_request) Has been cancelled
Checks / Prek / Check changed files (pull_request) Has been cancelled
Checks / Prek / Clippy and Cargo Tests (pull_request) Has been cancelled
Update flake hashes / update-flake-hashes (pull_request) Has been cancelled
to 4ba493fffe
Some checks failed
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 6s
Documentation / Build and Deploy Documentation (pull_request) Has been cancelled
Checks / Prek / Pre-commit & Formatting (pull_request) Has been cancelled
Checks / Prek / Check changed files (pull_request) Has been cancelled
Checks / Prek / Clippy and Cargo Tests (pull_request) Has been cancelled
Update flake hashes / update-flake-hashes (pull_request) Has been cancelled
2026-08-04 11:18:39 +00:00
Compare
seercat changed title from WIP: Add systemd socket activation support to Add systemd socket activation support 2026-08-04 11:20:02 +00:00
seercat changed title from Add systemd socket activation support to WIP: Add systemd socket activation support 2026-08-04 11:56:15 +00:00
seercat force-pushed cat/socket-activation from 4ba493fffe
Some checks failed
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 6s
Documentation / Build and Deploy Documentation (pull_request) Has been cancelled
Checks / Prek / Pre-commit & Formatting (pull_request) Has been cancelled
Checks / Prek / Check changed files (pull_request) Has been cancelled
Checks / Prek / Clippy and Cargo Tests (pull_request) Has been cancelled
Update flake hashes / update-flake-hashes (pull_request) Has been cancelled
to 5fc2e72c13
Some checks failed
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 6s
Documentation / Build and Deploy Documentation (pull_request) Has been skipped
Checks / Prek / Check changed files (pull_request) Successful in 6s
Update flake hashes / update-flake-hashes (pull_request) Successful in 1m21s
Checks / Prek / Pre-commit & Formatting (pull_request) Failing after 2m9s
Checks / Prek / Clippy and Cargo Tests (pull_request) Successful in 10m37s
2026-08-04 12:02:09 +00:00
Compare
Cargo.toml Outdated
@ -481,0 +481,4 @@
[workspace.dependencies.sd-listen-fds]
version = "0.2.0"
default-features = false
First-time contributor

I just noticed that the existing sd-notify dependency also provides a helper for supporting socket activation: https://docs.rs/sd-notify/0.5.0/sd_notify/fn.listen_fds.html (weird, because I wouldn't expect that from the name).

It can be used instead of adding sd-listen-fds

I just noticed that the existing `sd-notify` dependency also provides a helper for supporting socket activation: https://docs.rs/sd-notify/0.5.0/sd_notify/fn.listen_fds.html (weird, because I wouldn't expect that from the name). It can be used instead of adding `sd-listen-fds`
Author
First-time contributor

Thanks for pointing this out, I'll swap to using that.

Thanks for pointing this out, I'll swap to using that.
seercat marked this conversation as resolved
@ -0,0 +1,8 @@
[Socket]
ListenStream=127.0.0.1:8008
ListenStream=[::1]:8008
First-time contributor

It might make sense to explicitly disable ipv4 compatibility on the ipv6 socket if using two separate sockets: BindIPv6Only=ipv6-only (though it's kind of a misleading name when also listening on an ipv4 address 😅).

Alternatively, this could just listen on the ipv6 address, and set BindIPv6Only=both (but then I think any ipv4 addresses displayed in the logs would show up as ipv6 addresses).

It might make sense to explicitly disable ipv4 compatibility on the ipv6 socket if using two separate sockets: `BindIPv6Only=ipv6-only` (though it's kind of a misleading name when also listening on an ipv4 address 😅). Alternatively, this could just listen on the ipv6 address, and set `BindIPv6Only=both` (but then I think any ipv4 addresses displayed in the logs would show up as ipv6 addresses).
Author
First-time contributor

When running this on my own server, where I have nginx pointed at http://127.0.0.1:8008, I found that using just BindIPv6Only=both and ListenStream=[::1]:8008 prevented nginx from being able to connect to c10y, while ListenStream=127.0.0.1:8008 works fine.

I'm leaning towards changing the recommended/example to the following, but I would appreciate input from anyone with more thoughts on this matter.

[Socket]
ListenStream=127.0.0.1:8008
ListenStream=[::1]:8008
BindIPv6Only=ipv6-only
When running this on my own server, where I have nginx pointed at `http://127.0.0.1:8008`, I found that using just `BindIPv6Only=both` and `ListenStream=[::1]:8008` prevented nginx from being able to connect to c10y, while `ListenStream=127.0.0.1:8008` works fine. I'm leaning towards changing the recommended/example to the following, but I would appreciate input from anyone with more thoughts on this matter. ```ini [Socket] ListenStream=127.0.0.1:8008 ListenStream=[::1]:8008 BindIPv6Only=ipv6-only ```
seercat marked this conversation as resolved
@ -0,0 +2,4 @@
ListenStream=127.0.0.1:8008
ListenStream=[::1]:8008
Accept=no
First-time contributor

Accept=no is the default, this can be removed to simplify the file.

`Accept=no` is the default, this can be removed to simplify the file.
Author
First-time contributor

Thanks.

Thanks.
seercat marked this conversation as resolved
seercat force-pushed cat/socket-activation from 5fc2e72c13
Some checks failed
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 6s
Documentation / Build and Deploy Documentation (pull_request) Has been skipped
Checks / Prek / Check changed files (pull_request) Successful in 6s
Update flake hashes / update-flake-hashes (pull_request) Successful in 1m21s
Checks / Prek / Pre-commit & Formatting (pull_request) Failing after 2m9s
Checks / Prek / Clippy and Cargo Tests (pull_request) Successful in 10m37s
to 4c2c56d77e
Some checks failed
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 6s
Documentation / Build and Deploy Documentation (pull_request) Has been cancelled
Checks / Prek / Pre-commit & Formatting (pull_request) Has been cancelled
Checks / Prek / Check changed files (pull_request) Has been cancelled
Checks / Prek / Clippy and Cargo Tests (pull_request) Has been cancelled
Update flake hashes / update-flake-hashes (pull_request) Has been cancelled
2026-08-04 22:32:34 +00:00
Compare
Author
First-time contributor

I've updated this to address those review comments (thanks!), although I would still appreciate input on the ListenStream= matter (unresolved above). I'm running this on my server at the moment, and I think the code is ready for review.

I've updated this to address those review comments (thanks!), although I would still appreciate input on the `ListenStream=` matter (unresolved above). I'm running this on my server at the moment, and I think the code is ready for review.
seercat changed title from WIP: Add systemd socket activation support to Add systemd socket activation support 2026-08-04 23:03:21 +00:00
seercat force-pushed cat/socket-activation from 4c2c56d77e
Some checks failed
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 6s
Documentation / Build and Deploy Documentation (pull_request) Has been cancelled
Checks / Prek / Pre-commit & Formatting (pull_request) Has been cancelled
Checks / Prek / Check changed files (pull_request) Has been cancelled
Checks / Prek / Clippy and Cargo Tests (pull_request) Has been cancelled
Update flake hashes / update-flake-hashes (pull_request) Has been cancelled
to 2e84b83c74
Some checks failed
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 6s
Documentation / Build and Deploy Documentation (pull_request) Has been cancelled
Checks / Prek / Pre-commit & Formatting (pull_request) Has been cancelled
Checks / Prek / Check changed files (pull_request) Has been cancelled
Checks / Prek / Clippy and Cargo Tests (pull_request) Has been cancelled
Update flake hashes / update-flake-hashes (pull_request) Has been cancelled
2026-08-04 23:25:04 +00:00
Compare
seercat force-pushed cat/socket-activation from 2e84b83c74
Some checks failed
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 6s
Documentation / Build and Deploy Documentation (pull_request) Has been cancelled
Checks / Prek / Pre-commit & Formatting (pull_request) Has been cancelled
Checks / Prek / Check changed files (pull_request) Has been cancelled
Checks / Prek / Clippy and Cargo Tests (pull_request) Has been cancelled
Update flake hashes / update-flake-hashes (pull_request) Has been cancelled
to 73104e6333
Some checks failed
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 7s
Documentation / Build and Deploy Documentation (pull_request) Has been cancelled
Checks / Prek / Pre-commit & Formatting (pull_request) Has been cancelled
Checks / Prek / Check changed files (pull_request) Has been cancelled
Checks / Prek / Clippy and Cargo Tests (pull_request) Has been cancelled
Update flake hashes / update-flake-hashes (pull_request) Has been cancelled
2026-08-05 01:11:13 +00:00
Compare
seercat force-pushed cat/socket-activation from 73104e6333
Some checks failed
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 7s
Documentation / Build and Deploy Documentation (pull_request) Has been cancelled
Checks / Prek / Pre-commit & Formatting (pull_request) Has been cancelled
Checks / Prek / Check changed files (pull_request) Has been cancelled
Checks / Prek / Clippy and Cargo Tests (pull_request) Has been cancelled
Update flake hashes / update-flake-hashes (pull_request) Has been cancelled
to 0797faf5f1
Some checks failed
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 7s
Documentation / Build and Deploy Documentation (pull_request) Has been skipped
Checks / Prek / Check changed files (pull_request) Successful in 7s
Update flake hashes / update-flake-hashes (pull_request) Successful in 1m22s
Checks / Prek / Pre-commit & Formatting (pull_request) Failing after 2m4s
Checks / Prek / Clippy and Cargo Tests (pull_request) Successful in 10m7s
2026-08-05 01:49:04 +00:00
Compare
seercat force-pushed cat/socket-activation from 0797faf5f1
Some checks failed
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 7s
Documentation / Build and Deploy Documentation (pull_request) Has been skipped
Checks / Prek / Check changed files (pull_request) Successful in 7s
Update flake hashes / update-flake-hashes (pull_request) Successful in 1m22s
Checks / Prek / Pre-commit & Formatting (pull_request) Failing after 2m4s
Checks / Prek / Clippy and Cargo Tests (pull_request) Successful in 10m7s
to 38d892935f
All checks were successful
Documentation / Build and Deploy Documentation (pull_request) Has been skipped
Checks / Prek / Check changed files (pull_request) Successful in 6s
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 7s
Update flake hashes / update-flake-hashes (pull_request) Successful in 1m15s
Checks / Prek / Pre-commit & Formatting (pull_request) Successful in 1m46s
Checks / Prek / Clippy and Cargo Tests (pull_request) Successful in 9m2s
2026-08-05 02:08:50 +00:00
Compare
First-time contributor

I've updated this to address those review comments (thanks!), although I would still appreciate input on the ListenStream= matter (unresolved above). I'm running this on my server at the moment, and I think the code is ready for review.

Right sorry! It looks like the BindIPv6Only option only makes a difference when binding on the wildcard ipv6 address [::] (I thought it would also apply to [::1]), so I was wrong to suggest it. There's no harm in keeping BindIPv6Only, but it would probably make sense to remove to reduce the noise in the file.

> I've updated this to address those review comments (thanks!), although I would still appreciate input on the ListenStream= matter (unresolved above). I'm running this on my server at the moment, and I think the code is ready for review. Right sorry! It looks like the `BindIPv6Only` option only makes a difference when binding on the wildcard ipv6 address `[::]` (I thought it would also apply to `[::1]`), so I was wrong to suggest it. There's no harm in keeping `BindIPv6Only`, but it would probably make sense to remove to reduce the noise in the file.
@ -18,3 +34,1 @@
handle: ServerHandle<std::net::SocketAddr>,
mut shutdown: broadcast::Receiver<()>,
) -> Result {
mut shutdown_rx: broadcast::Receiver<Duration>,
First-time contributor

I'm not a maintainer for the project, but it seems like this file is handling too many cases now. Previously it would just determine what kind of listener it would need to run from the config, and delegate to the specific implementation. Now it seems like it needs to be aware of implementation details for every possible type of listener, which makes it harder to understand.

I don't have enough context of the project to know the best approach to decouple everything, but I think unix socket specific code & tcp specific code should be split off into in separate files (and ideally only have a single point of entry from this file).

I also find it strange that the direct tls layer only applies to tcp listeners. I think it would make things easier to decouple if it was added as an extra layer on top of all listeners (but it also makes sense why it's done this way for backwards compatibility with the previous implementation).

I'm not a maintainer for the project, but it seems like this file is handling too many cases now. Previously it would just determine what kind of listener it would need to run from the config, and delegate to the specific implementation. Now it seems like it needs to be aware of implementation details for every possible type of listener, which makes it harder to understand. I don't have enough context of the project to know the best approach to decouple everything, but I think unix socket specific code & tcp specific code should be split off into in separate files (and ideally only have a single point of entry from this file). I also find it strange that the direct tls layer only applies to tcp listeners. I think it would make things easier to decouple if it was added as an extra layer on top of all listeners (but it also makes sense why it's done this way for backwards compatibility with the previous implementation).
First-time contributor

Ooh, it looks like axum-server-dual-protocol doesn't support unix listeners, even though axum-server does.

Ooh, it looks like `axum-server-dual-protocol` doesn't support unix listeners, even though `axum-server` does.
Owner

Upstream axum-server-dual-protocol appears abandoned, could probably fork it given we're already relying on a patch

Upstream `axum-server-dual-protocol` appears abandoned, could probably fork it given we're already relying on a patch
Author
First-time contributor

I agree that I could probably split things up a bit more, but the dual-protocol TLS is certainly part of the problem.
Out of curiosity, how important is that feature? (I know, hard question to answer)

I am not interested in maintaining such a fork, fwiw. I also think that having no-tls vs. just-tls vs. dual is always going to be more complicated than a binary of TLS or non-TLS.

I agree that I could probably split things up a bit more, but the dual-protocol TLS is certainly part of the problem. Out of curiosity, how important is that feature? (I know, hard question to answer) _I_ am not interested in maintaining such a fork, fwiw. I also think that having no-tls vs. just-tls vs. dual is always going to be more complicated than a binary of TLS or non-TLS.
Owner

Having the ability is needed to run some test suites.

Having the ability is needed to run some test suites.
@ -43,0 +53,4 @@
let mut bound_ip_addrs = Vec::new();
#[cfg(all(target_os = "linux", feature = "systemd"))]
let mut bound_unix_socket_paths = Vec::new();
First-time contributor

It also seems like the implementation can be generalized to avoid needing bound_ip_addrs & bound_unix_socket_paths.

From what I understand, they're used to allow the server to skip binding sockets that would conflict with sockets allocated by systemd (essentially reducing errors into warnings for a narrow use-case).

To generalize it, I think it would make sense to allow the server to continue running after any bind error (as long as one listener remains active). Then maybe it would also make sense to add a "strict" listening option that would enforce all listeners be active.

It also seems like the implementation can be generalized to avoid needing `bound_ip_addrs` & `bound_unix_socket_paths`. From what I understand, they're used to allow the server to skip binding sockets that would conflict with sockets allocated by systemd (essentially reducing errors into warnings for a narrow use-case). To generalize it, I think it would make sense to allow the server to continue running after any bind error (as long as one listener remains active). Then maybe it would also make sense to add a "strict" listening option that would enforce all listeners be active.
Author
First-time contributor

My intention in doing this was to make it easier to use socket activation, as it wouldn't be strictly necessary to explicitly unset your listeners if not using use_exclusively_socket_activation, so that c10y would be able to bind sockets itself if socket activation didn't provide them for some reason.

Other than that case, I feel that it makes sense to fail completely if anything fails to bind, since other downstream stuff might otherwise break in confusing ways: "continuwuity is running, so why can't nginx get at it?"
I'd appreciate a maintainer's opinion on this since it's more of a design decision.

My intention in doing this was to make it easier to use socket activation, as it wouldn't be strictly necessary to explicitly unset your listeners if not using `use_exclusively_socket_activation`, so that c10y would be able to bind sockets itself if socket activation didn't provide them for some reason. Other than that case, I feel that it makes sense to fail completely if anything fails to bind, since other downstream stuff might otherwise break in confusing ways: "continuwuity is running, so why can't nginx get at it?" I'd appreciate a maintainer's opinion on this since it's more of a design decision.
First-time contributor

Aside from my comments - I've applied the changes from 9c714035a9 .. 38d892935f on one of my servers, and it's been working great for socket activation! Thanks for working on this! 😊

Aside from my comments - I've applied the changes from 9c714035a943c5941f2fa5f3014fbdc57811173e .. 38d892935f34601f753b0f83fe9815b6ac281528 on one of my servers, and it's been working great for socket activation! Thanks for working on this! 😊
@ -122,4 +13,1 @@
.expect("failed to extract configured unix socket path");
if path.exists() {
warn!("Removing existing UNIX socket {:#?} (unclean shutdown?)...", path.display());
First-time contributor

It looks like this warning was accidentally removed?

It looks like this warning was accidentally removed?
Author
First-time contributor

Intentionally, although this is something I need to look further into.

I also removed the code that deletes/unlinks the filesystem path when shutting down the unix socket listener, since I couldn't see a good way to fit it in and I'm not sure that it's strictly necessary. As such, I removed the check that complains if there was already a socket there, since that would now be the expected case.

Intentionally, although this is something I need to look further into. I also removed the code that deletes/unlinks the filesystem path when shutting down the unix socket listener, since I couldn't see a good way to fit it in and I'm not sure that it's strictly necessary. As such, I removed the check that complains if there was already a socket there, since that would now be the expected case.
@ -2677,4 +2700,2 @@
.map_err(|e| err!("There was a problem with your configuration file: {e}"))?;
// don't start if we're listening on both UNIX sockets and TCP at same time
check::is_dual_listening(raw_config)?;
Owner

Justification for removing this check?

Justification for removing this check?
First-time contributor

With the refactoring done to support multiple fds passed by systemd socket activation, the server can now listen on both unix & tcp sockets at the same time.

The check could be left in, but it would be unnecessarily restrictive now that it's supported.

With the refactoring done to support multiple fds passed by systemd socket activation, the server can now listen on both unix & tcp sockets at the same time. The check could be left in, but it would be unnecessarily restrictive now that it's supported.
Author
First-time contributor

That is correct.

That is correct.
@ -0,0 +13,4 @@
/// Given a file descriptor, classify it and convert to a `TcpListener`,
/// `UnixListener`, or neither (in which case the fd is returned).
fn classify_fd(fd: OwnedFd) -> std::io::Result<Result<Listener, OwnedFd>> {
Owner

Wrapping errors is bad, either use thiserror/anyhow to make a specific error or use the global generic error type

Wrapping errors is bad, either use thiserror/anyhow to make a specific error or use the global generic error type
Owner

You're also not adding any context to the errors removing the original errors, which may make issues harder to debug

You're also ~~not adding any context to the errors~~ removing the original errors, which may make issues harder to debug
@ -45,0 +216,4 @@
server
.runtime()
.spawn(graceful_shutdown_handler(shutdown_rx, server_handle.clone()));
First-time contributor

Couldn't everything that's done by shutdown_rx be done with a ServerHandle passed down from src/router/run.rs? It seems wasteful to create a new ServerHandle & spawn a task for every socket just to handle shutdowns.

Couldn't everything that's done by `shutdown_rx` be done with a `ServerHandle` passed down from `src/router/run.rs`? It seems wasteful to create a new `ServerHandle` & spawn a task for every socket just to handle shutdowns.
Author
First-time contributor

Unfortunately not, though I did try initially.

  1. ServerHandle is generic over the type of address used by the listener bound, for some reason, and I can't find a nice way to work around that, especially considering that...
  2. I suspect that the old way (using one ServerHandle for all of them if multiple tcp listeners existed) could have caused some subtle issues with the shutdown process, since the handles seems to encapsulate some mutable inner state for communication.

I agree that it's not the nicest, but the generics mean that a "better" solution may still require keeping unix socket- and tcp socket-adjacent things separate like with bound_ip_addrs/bound_unix_socket_paths.

Unfortunately not, though I did try initially. 1. `ServerHandle` is generic over the type of address used by the listener bound, for some reason, and I can't find a nice way to work around that, especially considering that... 2. I suspect that the old way (using one `ServerHandle` for all of them if multiple tcp listeners existed) could have caused some subtle issues with the shutdown process, since the handles seems to encapsulate some mutable inner state for communication. I agree that it's not the nicest, but the generics mean that a "better" solution may still require keeping unix socket- and tcp socket-adjacent things separate like with `bound_ip_addrs`/`bound_unix_socket_paths`.
seercat force-pushed cat/socket-activation from 38d892935f
All checks were successful
Documentation / Build and Deploy Documentation (pull_request) Has been skipped
Checks / Prek / Check changed files (pull_request) Successful in 6s
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 7s
Update flake hashes / update-flake-hashes (pull_request) Successful in 1m15s
Checks / Prek / Pre-commit & Formatting (pull_request) Successful in 1m46s
Checks / Prek / Clippy and Cargo Tests (pull_request) Successful in 9m2s
to 186ec15170
All checks were successful
Documentation / Build and Deploy Documentation (pull_request) Has been skipped
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 7s
Checks / Prek / Check changed files (pull_request) Successful in 7s
Update flake hashes / update-flake-hashes (pull_request) Successful in 1m24s
Checks / Prek / Pre-commit & Formatting (pull_request) Successful in 2m24s
Checks / Prek / Clippy and Cargo Tests (pull_request) Successful in 9m36s
2026-08-15 01:55:15 +00:00
Compare
seercat force-pushed cat/socket-activation from 186ec15170
All checks were successful
Documentation / Build and Deploy Documentation (pull_request) Has been skipped
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 7s
Checks / Prek / Check changed files (pull_request) Successful in 7s
Update flake hashes / update-flake-hashes (pull_request) Successful in 1m24s
Checks / Prek / Pre-commit & Formatting (pull_request) Successful in 2m24s
Checks / Prek / Clippy and Cargo Tests (pull_request) Successful in 9m36s
to 9e1e488679
All checks were successful
Documentation / Build and Deploy Documentation (pull_request) Has been skipped
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 7s
Checks / Prek / Check changed files (pull_request) Successful in 7s
Checks / Prek / Pre-commit & Formatting (pull_request) Successful in 54s
Update flake hashes / update-flake-hashes (pull_request) Successful in 1m24s
Checks / Prek / Clippy and Cargo Tests (pull_request) Successful in 8m17s
2026-08-15 23:45:27 +00:00
Compare
bart left a comment

Not doing a functional review (yet), I think no one noticed this yet so that's why I am submitting this.

Do I understand correctly that this will be a non-default feature? I am thinking of making socket activation the default (if we end up merging this) on NixOS 26.11 (the upcoming release at the end of the year), as socket activation is the more regular approach on NixOS anyway.

Not doing a functional review (yet), I think no one noticed this yet so that's why I am submitting this. Do I understand correctly that this will be a non-default feature? I am thinking of making socket activation the default (if we end up merging this) on NixOS 26.11 (the upcoming release at the end of the year), as socket activation is the more regular approach on NixOS anyway.
@ -69,0 +69,4 @@
# If a provided socket is bound to an address also configured here,
# Continuwuity will not bind that address again.
#
# Does nothing if Continuwuity was not build with the `systemd` feature.
Contributor

s/build/built

s/build/built
@ -123,0 +123,4 @@
/// If a provided socket is bound to an address also configured here,
/// Continuwuity will not bind that address again.
///
/// Does nothing if Continuwuity was not build with the `systemd` feature.
Contributor

s/build/built

s/build/built
Author
First-time contributor

@bart wrote in #2095 (comment):

Not doing a functional review (yet), I think no one noticed this yet so that's why I am submitting this.

Thanks, and thanks for the typo fix! I'll include that when I make and push other changes.

Do I understand correctly that this will be a non-default feature? I am thinking of making socket activation the default (if we end up merging this) on NixOS 26.11 (the upcoming release at the end of the year), as socket activation is the more regular approach on NixOS anyway.

I wouldn't agree that this will be a non-default feature, though I may misunderstand what specifically you are referring to.

By default, c10y is built with the systemd feature, and use_socket_activation defaults to true. That means that if an appropriately-configured .socket unit is present, causing systemd to pass sockets to c10y when executing it, c10y will automatically use those sockets in addition to binding its own (with the exception that it won't bind anything itself if it doesn't need to).
Effectively, the bulk of the socket activation functionality is enabled by default, if a socket unit is set up.

However, use_exclusively_socket_activation defaults to false. If set to true, c10y will never try to bind its own listening sockets.

Of course the design is up to you, but my input w/r/t using this for the NixOS module would be:

  • If some bikeshed_socket_activation module option is enabled, set use_exclusively_socket_activation = true and throw an error if any of c10y's listening-related config is set (perhaps non-exhaustively, address, port, unix_socket_path).
  • You would have to provide module options to configure the socket unit's listening addresses, since c10y's config would no longer affect that.

By the way: I'll get to all the other review stuff from other people hopefully sometime soon, I got busier shortly after opening this PR. I just need to avoid making this code all overly-complicated since my goal here was to make things simpler.

@bart wrote in https://forgejo.ellis.link/continuwuation/continuwuity/pulls/2095#issuecomment-34280: > Not doing a functional review (yet), I think no one noticed this yet so that's why I am submitting this. Thanks, and thanks for the typo fix! I'll include that when I make and push other changes. > Do I understand correctly that this will be a non-default feature? I am thinking of making socket activation the default (if we end up merging this) on NixOS 26.11 (the upcoming release at the end of the year), as socket activation is the more regular approach on NixOS anyway. I wouldn't agree that this will be a non-default feature, though I may misunderstand what specifically you are referring to. By default, c10y is built with the `systemd` feature, and `use_socket_activation` defaults to `true`. That means that if an appropriately-configured `.socket` unit is present, causing systemd to pass sockets to c10y when executing it, c10y will automatically use those sockets in addition to binding its own (with the exception that it won't bind anything itself if it doesn't need to). Effectively, the bulk of the socket activation functionality is enabled by default, if a socket unit is set up. However, `use_exclusively_socket_activation` defaults to `false`. If set to `true`, c10y will never try to bind its own listening sockets. Of course the design is up to you, but my input w/r/t using this for the NixOS module would be: - If some `bikeshed_socket_activation` module option is enabled, set `use_exclusively_socket_activation = true` and throw an error if any of c10y's listening-related config is set (perhaps non-exhaustively, `address`, `port`, `unix_socket_path`). - You would have to provide module options to configure the socket unit's listening addresses, since c10y's config would no longer affect that. By the way: I'll get to all the other review stuff from other people hopefully sometime soon, I got busier shortly after opening this PR. I just need to avoid making this code all overly-complicated since my goal here was to make things simpler.
First-time contributor

@seercat I did end up playing around with your PR about a week ago to try to see if I could organize things the way I described in some of my comments.

I basically had it ready to publish (in my own fork), but then suddenly lost all motivation (hopefully I can get back into the hyper-fixation again 😅). I also didn't really want to distract too much from what you already started - but let me know if you're curious to see what I came up with.

@seercat I did end up playing around with your PR about a week ago to try to see if I could organize things the way I described in some of my comments. I basically had it ready to publish (in my own fork), but then suddenly lost all motivation (hopefully I can get back into the hyper-fixation again 😅). I also didn't really want to distract too much from what you already started - but let me know if you're curious to see what I came up with.
Author
First-time contributor

@kira-bruneau wrote in #2095 (comment):

@seercat I did end up playing around with your PR about a week ago to try to see if I could organize things the way I described in some of my comments.

Cool!

I basically had it ready to publish (in my own fork), but then suddenly lost all motivation (hopefully I can get back into the hyper-fixation again 😅). I also didn't really want to distract too much from what you already started - but let me know if you're curious to see what I came up with.

I'd be very interested to see what you have, I'm not super happy with what I've tried so far. Would you be OK with me integrating it into my branch (i.e. this PR) if I agree with it? If you push it to a branch on your fork here I'll take a look.

@kira-bruneau wrote in https://forgejo.ellis.link/continuwuation/continuwuity/pulls/2095#issuecomment-34311: > @seercat I did end up playing around with your PR about a week ago to try to see if I could organize things the way I described in some of my comments. Cool! > I basically had it ready to publish (in my own fork), but then suddenly lost all motivation (hopefully I can get back into the hyper-fixation again :sweat_smile:). I also didn't really want to distract too much from what you already started - but let me know if you're curious to see what I came up with. I'd be very interested to see what you have, I'm not super happy with what I've tried so far. Would you be OK with me integrating it into my branch (i.e. this PR) if I agree with it? If you push it to a branch on your fork here I'll take a look.
Contributor

@seercat Thanks for the response! I was/am away, so I'll do a functional review once you got to resolving all (other) reviews. Thanks for explaining your intention. I just thought the systemd feature would not be on by default, no need to explain the rest :p

If some bikeshed_socket_activation module option is enabled, set use_exclusively_socket_activation = true and throw an error if any of c10y's listening-related config is set (perhaps non-exhaustively, address, port, unix_socket_path).

I think we can just reuse the services.matrix-continuwuity.settings freeform submodule and there's no need for a separate option that essentially just sets another option, and nothing else. This is better for introspection as well. The evaluation error is a good idea though, but I think an evaluation warning is nicer to the consumers of the module. Nothing breaks if both services.matrix-continuwuity.settings.global.address (or any of the other ones) and services.matrix-continuwuity.settings.global.use_exclusively_socket_activation = true; are set.

Thanks again, I'll shut up about NixOS now, it is better to defer that conversation to the nixpkgs PR for it.

@seercat Thanks for the response! I was/am away, so I'll do a functional review once you got to resolving all (other) reviews. Thanks for explaining your intention. I just thought the systemd feature would not be on by default, no need to explain the rest :p > If some bikeshed_socket_activation module option is enabled, set use_exclusively_socket_activation = true and throw an error if any of c10y's listening-related config is set (perhaps non-exhaustively, address, port, unix_socket_path). I think we can just reuse the `services.matrix-continuwuity.settings` freeform submodule and there's no need for a separate option that essentially just sets another option, and nothing else. This is better for introspection as well. The evaluation error is a good idea though, but I think an evaluation warning is nicer to the consumers of the module. Nothing breaks if both `services.matrix-continuwuity.settings.global.address` (or any of the other ones) and `services.matrix-continuwuity.settings.global.use_exclusively_socket_activation = true;` are set. Thanks again, I'll shut up about NixOS now, it is better to defer that conversation to the nixpkgs PR for it.
All checks were successful
Documentation / Build and Deploy Documentation (pull_request) Has been skipped
Checks / Changelog / Check changelog is added (pull_request_target) Successful in 7s
Required
Details
Checks / Prek / Check changed files (pull_request) Successful in 7s
Required
Details
Checks / Prek / Pre-commit & Formatting (pull_request) Successful in 54s
Required
Details
Update flake hashes / update-flake-hashes (pull_request) Successful in 1m24s
Checks / Prek / Clippy and Cargo Tests (pull_request) Successful in 8m17s
Required
Details
This pull request has changes requested by an official reviewer.
This branch is out-of-date with the base branch
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u cat/socket-activation:seercat-cat/socket-activation
git switch seercat-cat/socket-activation
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
4 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
continuwuation/continuwuity!2095
No description provided.